SitePoint Sponsor

User Tag List

Results 1 to 8 of 8

Hybrid View

  1. #1
    SitePoint Member
    Join Date
    May 2007
    Posts
    22
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Being RESTful and avoiding redundancy question

    I've been reading about building RESTful applications and it sounds like a good approach that I want to use (great overview: http://softiesonrails.com/2007/3/28/...ding-resources )

    Question: how do I avoid redundancy if I have several controllers that do similar but slightly distinct things?

    For example, let's say I'm building a forum site with 2 categories: music and cars. People can click on the "music" tab to see discussions and add comments about music; they can click on the "cars" tab for discussions about cars.

    I create a model called Discussion and two controllers -- carDiscussion_controller and musicDiscussion_controller. Each controller has the standard 7 RESTful methods (index, show, new, edit, update, create, destroy). Thus, the index method of musicDiscussion could be customized to list only discussions related to music.

    This seems good and maybe it is, but I'm afraid there will some redundancy. For example, the destroy method will always do the same thing -- destroy a discussion. Is this a problem? Is there a better way to do this?

  2. #2
    SitePoint Guru
    Join Date
    Aug 2005
    Posts
    986
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Just create one DiscussionConrtroller.

    Create special actions like:

    your-site.com/discussions/music
    your-site.com/discussions/cars

    That list discussions about music and cars.

  3. #3
    SitePoint Member
    Join Date
    May 2007
    Posts
    22
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Are you saying that in addition to methods like index and show, I'd have a method named music in DiscussionController? The music method would, say, list all the discussions in the music topic.
    Wouldn't this be non-RESTful? Does that matter?

  4. #4
    SitePoint Guru
    Join Date
    Aug 2005
    Posts
    986
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes, you could do that. I think this is RESTful. You can do this without adding extra actions too:

    your-site.com/discussions?topic=music
    your-site.com/discussions?topic=cars

    You can extract the topic like this:

    Code Ruby:
    class DiscussionController < ApplicationController
      def index
         topic = params[:topic]
         if topic
           @discussions = Discussion.find_by_topic(topic)
         else
           @discussions = Discussion.find(:all)
         end
       end
     
       ...
    end

    You could also use the more Rails-like way: add an extra model Topic.

    Code Ruby:
    class Topic < ActiveRecord::Base
      has_many :discussions
    end

    Now create a TopicController, and add a nested route:

    Code Ruby:
    map.resources :topics do |topics|
      topics.resources :discussions
    end

    This gives you these urls:

    your-site.com/topics/[the-topic]/discussions

    This presentation is interesting: http://media.rubyonrails.org/present...fresources.pdf
    Video: http://www.scribemedia.org/2006/07/09/dhh/

  5. #5
    SitePoint Member
    Join Date
    May 2007
    Posts
    22
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Cool -- these links look great.

    I also found a document from March 2007 called RESTful RAILS Development by Wirdemann and Baustert:
    http://www.b-simple.de/documents
    It agrees with your recommendation above to use a nested route. This makes a lot of sense -- a Topic has many discussions. When a user views or manipulates a discussion, it should be in the context of a topic. One thing I hadn't realized, however, is that my generated controller won't know that it is responsible for a nested resource. Thus, the index action of my DiscussionsController would have to be changed to look like this:

    Code:
    def index
    	topic = Topic.find(params[:topic_id])
    	@discussions = topic.discussions.find(:all)
    	
    end
    (ReggieB in this forum recommended I follow this strategy a few days ago, but I didn't understand it the first time; now I think I do).

    One syntax question: why not make topic an instance variable? (as in topic = Topic.find(params[:topic_id])
    Is it simply considered a local variable?

  6. #6
    SitePoint Guru
    Join Date
    Aug 2005
    Posts
    986
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If you don't need it in the view then it's better to use a local variable: you don't clutter your namespace this way.

    Your code is not as efficient as it could be: you're executing two SQL selects, but it can be done with one:

    Code Ruby:
    def index
      @discussions = Discussion.find_by_topic(params[:topic_id])
    end

  7. #7
    SitePoint Member
    Join Date
    May 2007
    Posts
    22
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If I wanted to list comments associated with a given discussion, would my show action would be something like the following?
    def show
    @comments = Comment.find_by_discussion(params[:discussion_id])
    end

    It doesn't like this: says "undefined method `find_by_discussion' for Comment:Class"

    I say "has_many :comments" in Discussion class and "belongs_to :discussion" in Comment class.

  8. #8
    SitePoint Member
    Join Date
    May 2007
    Posts
    22
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Actually, I think it should say find_by_discussion(params[:id]) -- but that doesn't work. Nor does what you suggested above for index -- although the inefficient code does work. What am I missing? Do I need to add a find_by_topic method to my model? I tried Discussion.find_by_topic_id as well, since my discussions table has a column called "topic_id"


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •