Implementing REST properly

Hi,

Same question is in http://www.ruby-forum.com/topic/144125#new but since
it evolved from a different question I move it to its own topic.

Hi again,

This is what I've done in the Programs index view:

<%= link_to 'View comments', :controller => 'comments', :action =>
'index', :program_name => program.program_name %>

And this in the comments controller index function:

# GET /comments
# GET /comments.xml
def index

if params[:program_name]
  @comments = Comment.find(:all, :conditions => ["program_name = ?",
params[:program_name]])
else
  @comments = Comment.find(params[:id])
end

respond_to do |format|
  format.html # index.html.erb
  format.xml { render :xml => @comments }
end

end

So do you thing that's the right way?

Cheers

Hi,

My understanding of the RESTful way would lead me to using nested
resources in this situation

so your urls would be

/Programs/1/comments for index of all comments
for a program whose id is 1
/Programs/1/comments/new for new comments for a program
whose id is 1
/Programs/1/comments/10/show for showing comment 10 for a
program whose id is 1

and so on for all 7 crud actions. that way your comments controller
does not need logic like that.

so in your routes.rb you would do this

map.resources :programs do |program|
   article.resources :comments
end

which will build all those routes for you you can check the routes
it creates by using

rake routes

from command line. it will list all of the routes that your
routes.rb file sets up. very useful for finding the correct
dynamic_url

for instance a few from my routes rb are

               company GET /
companies/:id
{:action=>"show", :controller=>"companies"}
company_profile GET /companies/:company_id/
profile/:id {:action=>"show", :controller=>"profile"}
  new_company GET /companies/
new
{:action=>"new", :controller=>"companies"}

left hand column can be used by appending _url or _path accordingly

new_company_path in your index view for companies check your
scaffolded views and controllers and you'll see them everywhere. the
table in rake routes shows the mappings

hope this helps :slight_smile:
Nathan

hope this helps :slight_smile:

Sure it does. Thanks!

I've been now playing with this again.
Sorry to be back with the same one.

Yeah the rake routes is great to see all options clearly.
However I'm facing a problem:

This is what I have now in the routes:

  map.resources :comments

  map.resources :programs do |program|
   program.resources :comments
  end

Everthing is more or less working. I have created some programs and then
using "/programs/:program_id/comments/new" I have created some comments
for programs.

But, according to routes using "program_comments GET
/programs/:program_id/comments" should list all the comments for that
program id.

However that always shows all the comments for all the programs. That is
getting into the controller via the 'index' method which is:

  # GET /comments
  # GET /comments.xml
  def index
    @comments = Comment.find(:all)

    respond_to do |format|
      format.html # index.html.erb
      format.xml { render :xml => @comments }
    end
  end

And that finds all the comments as the code is. So how can it find
comments only for a give program with no changes in that controller?

that way your comments controller does not need logic like that.

map.resources :programs do |program|
   article.resources :comments
end

I have changed article with programs in the code above. Is my routing
still ok?

I must be doing something wrong again...

Thanks.

<snip>

However that always shows all the comments for all the programs. That is
getting into the controller via the 'index' method which is:

  # GET /comments
  # GET /comments.xml
  def index

    @comments = Comment.find(:all)

<snip>

And that finds all the comments as the code is. So how can it find
comments only for a give program with no changes in that controller?

You need to change your controller so you scope the comments by the
program, such as:

before_filter :get_program

private
def get_program
@program = Program.find_by_id(params[:product_id]) || Program.new
end

public
def index
  @comments = @program.comments
end

Note that the above will only work when called in a nested fashion,
such as /program/123/comments

If you try calling /comments, you'll get an empty list

Mike

just noticed that you have both a nested route and a non nested route
for comments.

so if you want both '/comments' and '/programs/123/comments' to work,
you'll need something like the following:

comments_controller.rb

def index
  @program = Program.find_by_id(params[:program_id]) if params[:program_id]
  @comments = @program ? @program.comments : Comment.find(:all)
end

Mike

sorry i probably mislead you by showing that route in my last mail as an example, i would get rid of the index action and show action instead use the program view and controller to display the comments in the context of the program, i can’t really envisage a situation where you would want to show all your comments in an index view. but i suppose you might want to have the index show a list of comments for a given program.

basically you would find the program based on params[:program_id] and use it to populate @comments like so.

program = Program.find(params[:program_id])

@comments = program.comments.find(:all)

given that other actions in the view will use program variable you could use a before_filter to create @programs variable that can be referenced by all the actions. so for instance your new action can be done like this:-

def new

@program.comments.build

end

check out this tutorial, I confess i haven’t read it thoroughly but just skimming it i think it will explain all you will want to know.

http://www.akitaonrails.com/2007/12/12/rolling-with-rails-2-0-the-first-full-tutorial

cheers

nathan

sorry again just realised mike had replied already … :slight_smile: I don’t think you would actually want the comments controller to respond to just /comments so I would remove

map.resources :comments

from your routes.rb file.

cheers

Hi, reply to myself (and anyone interested of course...)

    <% form_tag :action=>"destroy", :controller=>"session" do %>
        <%= submit_tag 'Log out' %>
    <% end %>

So my problem what that the controller was not found in admin. So the
fix is to use :controller=>"/session"

That will look in root controller and that's where it is found.

Cheers!