REST: nested resources and attributes as resources

Hi you all,

After reading the manual from b-simple[1], I have two doubts:

1: I have a Project, which may has cero or more Iterations, so I
have

map.resources :projects do |projects|
projects.resources :iterations
end

But… what if I also want to list all the iterations
(http://localhost:3000/iterations)? I can’t, because a project id is
required.

2: Let’s suppose Project has an attribute like “started_date”. I want to
see projects that started in a certain date so… should I have a
(virtual) resource called Date? And a DateController? And paths such as
http://localhost:3000/date/24-12-2006/projects ?

[1]
http://blog.b-simple.de/articles/2007/02/19/restful-rails-tutorial-in-english

Lots of thanks

map.resources :projects do |projects|
projects.resources :iterations
end

But… what if I also want to list all the iterations
(http://localhost:3000/iterations)? I can’t, because a project id is
required.

you can put this in your routes twice:

map.resources :projects do |projects|
projects.resources :iterations
end
projects.resources :iterations

then use
project_iterations_path(@project_id)
or
iterations_path()

as needed

2: Let’s suppose Project has an attribute like “started_date”. I want to
see projects that started in a certain date so… should I have a
(virtual) resource called Date? And a DateController? And paths such as
http://localhost:3000/date/24-12-2006/projects ?

just hand over the wanted date like

project_iterations_path(@project_id, :date => @wanted_date)

and check in your index action:
if(params[:date)then

that should work and keep you RESTful enough

Hi

you can put this in your routes twice:

map.resources :projects do |projects|
projects.resources :iterations
end
projects.resources :iterations

Um… do you mean “map.resources :iterations” with your last line? :S

http://localhost:3000/date/24-12-2006/projects ?

just hand over the wanted date like

project_iterations_path(@project_id, :date => @wanted_date)

and check in your index action:
if(params[:date)then

Umm… but how do I select the date? It is intended to be also a list
(well, intended for me :D)

Thanks a lot.

Um… do you mean “map.resources :iterations” with your last line? :S

oops :), yes, of course, you’re right there

project_iterations_path(@project_id, :date => @wanted_date)

and check in your index action:
if(params[:date)then

Umm… but how do I select the date? It is intended to be also a list
(well, intended for me :D)

you mean in a select list/box or something? then it’ll be a form and you
hand back the form data anyway, including the date (which may include
some entry for not selecting by date at all)
the thing is just to send back a date together with everything else and
evaluate this param[:date] and let the controller decide, which kind of
find to use for this or that case

Umm… but how do I select the date? It is intended to be also a list
(well, intended for me :D)

you mean in a select list/box or something? then it’ll be a form and you
hand back the form data anyway, including the date (which may include
some entry for not selecting by date at all)
the thing is just to send back a date together with everything else and
evaluate this param[:date] and let the controller decide, which kind of
find to use for this or that case

I wanted to show a list of dates (such as a list of projects), I mean, a
list of links with the dates (not boxes). For example, I show a project
and then the dates where any of its iterations started on. And then
select one of these dates to see the iterations which started on that
date… The thing is that “date” is not a class, but an attribute of
either projects or iterations.

Btw, I had another question, not so important. How could I have a “home
page” which includes the ways to reach the basic lists (list of projects
and list iterations)? Which is the REST way to do it?

Lots of thanks again.

Hi –

On Thu, 15 Nov 2007, Thorsten M. wrote:

iterations_path()

as needed

In Rails versions < 2.0, you need to specify a name prefix in the
nesting:

map.resources :projects do |p|
p.resources :iterations, :name_prefix => “projects_”
end
map.resources :iterations

The reason is that .resources :iterations creates named routes
– i.e., Ruby methods – for you: iterations_path, iterations_url,
etc. If you don’t use a name prefix, then the second time you create
those methods, the first ones you created get clobbered. It’s just
like doing:

def iterations_path
end

def iterations_path
end

The name prefix gives you sessions_iterations_path, which can
peacefully coexist with iterations_path.

In Rails 2.0 the name prefix based on the outer resource is added
automatically.

David


Upcoming training by David A. Black/Ruby Power and Light, LLC:

  • Advancing With Rails, Berlin, Germany, November 19-22
  • Intro to Rails, London, UK, December 3-6 (by Skills Matter)
    See http://www.rubypal.com for details!

David,
When Rails 2.0 automatically adds the ‘parent’ prefix is it singular
or plural? Based on your answer, I’m going to go into my HUGE project
and rewrite all of my routing to be ready for 2.0
Thank you,
Kathleen

I wanted to show a list of dates (such as a list of projects), I mean, a
list of links with the dates (not boxes). For example, I show a project
and then the dates where any of its iterations started on. And then
select one of these dates to see the iterations which started on that
date… The thing is that “date” is not a class, but an attribute of
either projects or iterations.

eg (sketch):

    <% @project.iterations.each do |it|
  • <%= link_to("start: #{it.start_date}", project_iterations_path(@project.id, :date => it.start_date)) %>
  • <% end %>

Btw, I had another question, not so important. How could I have a “home
page” which includes the ways to reach the basic lists (list of projects
and list iterations)? Which is the REST way to do it?

map.home ‘/’, :controller => ‘projects’

Hi –

On Fri, 16 Nov 2007, [email protected] wrote:

map.resources :projects do |projects|
projects.resources :iterations
In Rails versions < 2.0, you need to specify a name prefix in the
those methods, the first ones you created get clobbered. It’s just

In Rails 2.0 the name prefix based on the outer resource is added
automatically.

David,
When Rails 2.0 automatically adds the ‘parent’ prefix is it singular
or plural? Based on your answer, I’m going to go into my HUGE project
and rewrite all of my routing to be ready for 2.0
Thank you,
Kathleen

I disclaim all legal responsibility for the results :slight_smile: It appears to
be singular. Here’s a console session with 2.0 release candidate 1:

irb ActionController::Routing::Routes
draw do |map| map.resources :outers do |o| o.resources :inners;
?> end; end
=> [ActionController::Base, ActionView::Base]

puts named_routes.names.map {|r| r.to_s }.sort
edit_outer
edit_outer_inner
formatted_edit_outer
formatted_edit_outer_inner
formatted_new_outer
formatted_new_outer_inner
formatted_outer
formatted_outer_inner
formatted_outer_inners
formatted_outers
new_outer
new_outer_inner
outer
outer_inner
outer_inners
outers

This comports with the idea that every inner, or collection of inners,
is scoped to a particular outer.

David


Upcoming training by David A. Black/Ruby Power and Light, LLC:

  • Advancing With Rails, Berlin, Germany, November 19-22
  • Intro to Rails, London, UK, December 3-6 (by Skills Matter)
    See http://www.rubypal.com for details!

Thorsten,
When I read this post I was thrilled to see a new world of
possibilities opening up with nested RESTful resources. I jumped into
my own code and tried passing in a params value as you show in your
date example.
I crashed and burned with a “You have a nil object when you didn’t
expect it! The error occurred while evaluating nil.to_sym”
So we’re on the same page, this is the example of yours that I’m
looking at;

project_iterations_path(@project_id, :date => @wanted_date)

I assume that “@wanted_date” is an instance variable available on the
current view and “:date” is simply an arbitrary symbol you chose to
use?
In my own example, I’m confident that my instance variable is valid
and in scope…but Ruby doesn’t like it. In testing your idea I
modified on of my resource calls to look like this;
<%= link_to ‘Add to Favorites’, efav_efavs_path(@user, :eitem =>
@eitem.id)%>
I feel like I just got a Christmas present and opened the box to find
it doesn’t work…any ideas are appreciated.
Kathy

On Nov 15, 11:01 am, Thorsten M. <rails-mailing-l…@andreas-

Hi –

On Fri, 16 Nov 2007, [email protected] wrote:

I feel like I just got a Christmas present and opened the box to find
it doesn’t work…any ideas are appreciated.

You can’t mix the plain arguments and the hash arguments. Try this:

efav_efavs_path(@user, @eitem)

Which reminds me: I really have to go back and see how my Inferred
Routes plugin fares with 2.0-ish Railses… (Inferred Routes lets you
leave off the left-hand arguments if they can be inferred, based on
segment names, from the right-most argument.)

David


Upcoming training by David A. Black/Ruby Power and Light, LLC:

  • Advancing With Rails, Berlin, Germany, November 19-22
  • Intro to Rails, London, UK, December 3-6 (by Skills Matter)
    See http://www.rubypal.com for details!

ok, i hope, we can fix that one.
but to change to RESTfullness takes a few steps and you can get errors
in different places.
main source of trouble is the routing
no. two are the path methods

so a good approach would be to start with a small dummy project, just to
get used to the way, rails handles those things, before you start
working over a large realworld project

first of all, the RESTfull routes expect that you use those actions:

action path_function method

index plural_path get
show singular_path(id) get
new new_singular_path get
create plural_path post
edit edit_singular_path(id) get
update singular_path(id) put
destroy singular_path(id) delete

the _path functions work on those (but you can still add your own)

simple example: grouped articles:
map.resources :groups do |group|
group.resources :articles
end

this adds the path_prefix group (singular, since it will work on
articles in the selected group)
and the path function will expect now an additional group id
so to show all articles in a group:
group_articles_path(@my_group_id) (this will call the index action of
articles and hand over params[:group_id])

or to show a single article:
group_article_path(@my_group_id, @my_article_id) (this will call the
show action of articles and hand over params[:group_id] and params[:id])

somuch to a very rough overview, you can read more details here:
http://www.b-simple.de/documents
http://topfunky.com/clients/peepcode/REST-cheatsheet.pdf

and to your specific error:
The error occurred while evaluating nil.to_sym
this you will get most likely, if your routing doesn’t match with your
path

<%= link_to ‘Add to Favorites’, efav_efavs_path(@user, :eitem =>
@eitem.id)%>
assuming, that @user and @eitem do exist, i can only guess from the path
name,
that you want to show all the efavs of a given efav (whatever an efav is
:wink:
the routing would look like this: (???)

map.resources :efavs do |efav|
efav.resources :efavs
end
seems a bit strange, but may work for a kind of tree structure

another guess:
user_efavs_path(@user, …)

or is @eitem.id an efav? then David’s gues would be right:
efav_efavs_path(@user, @eitem)

you need the hash syntax only for additional attributes (like the date
in the other example)

sorry, i’m at a loss here, whithout knowing more details about your
project
or what exactly you want to do

and your question:
project_iterations_path(@project_id, :date => @wanted_date)

I assume that “@wanted_date” is an instance variable available on the
current view and “:date” is simply an arbitrary symbol you chose to
use?
yes, thats right. it would show the iterations of a given project
(index) and hand over an additional
parameter date which the action will get as params[:date]

thorsten

I have moved the conversation of filtering resources by attributes to a
new thread:
http://www.ruby-forum.com/topic/132154

Hi again

eg (sketch):

    <% @project.iterations.each do |it|
  • <%= link_to("start: #{it.start_date}", project_iterations_path(@project.id, :date => it.start_date)) %>
  • <% end %>

Ok, I wanna come back to this REST way in handling attributes with an
approximate idea of my project. My app will have two basic resources:
Clients and the Tasks they do (lets focus on the Tasks).

Listing the Tasks through http://myapp/tasks
Will give back the list of Tasks, each of one with attributes such as
the date is started, the subject of the Task, etc.

Users will interact with my app to see the whole Tasks and the Tasks
with restrictions: Tasks that started in a certain date, or the Tasks
that deal with certain subjects. Here is my problem.
I have planning to have a kind of menu with links such as “By subject”,
“By date” in the TaskController index page. So, when user clicks “By
date” (to obtain the possible dates, selecting one, and obtaining the
tasks which started on that date), which URL should appear? Not the one
of http://myapp/tasks, cause I want the list of dates, or subjects (or
any Task attribute) to select, prior to obtain the final tasks.

Btw, I had another question, not so important. How could I have a “home
page” which includes the ways to reach the basic lists (list of projects
and list iterations)? Which is the REST way to do it?

map.home ‘/’, :controller => ‘projects’

Um, I don’t understand. My idea was having an index with links such as
“See projects” and “See whatever”. (that is, a list of links to the
collection of resources). I mean, I can have this links in the
ProjectController index, but I just wanted to know how could I do that
if needed.

Thanks a lot again.