REST: nested resources and attributes as resources

4 views
Skip to first unread message

Damaris Fuentes

unread,
Nov 15, 2007, 11:54:00 AM11/15/07
to rubyonra...@googlegroups.com
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
--
Posted via http://www.ruby-forum.com/.

Thorsten Muller

unread,
Nov 15, 2007, 12:01:08 PM11/15/07
to rubyonra...@googlegroups.com
>
> 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

Damaris Fuentes

unread,
Nov 15, 2007, 12:10:49 PM11/15/07
to rubyonra...@googlegroups.com
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.

Thorsten Muller

unread,
Nov 15, 2007, 12:29:14 PM11/15/07
to rubyonra...@googlegroups.com
>
> 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

Damaris Fuentes

unread,
Nov 15, 2007, 12:37:18 PM11/15/07
to rubyonra...@googlegroups.com
>> 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.

David A. Black

unread,
Nov 15, 2007, 12:37:22 PM11/15/07
to rubyonra...@googlegroups.com
Hi --

On Thu, 15 Nov 2007, Thorsten Muller wrote:

>
>>
>> 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

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 <mapper>.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!

Thorsten Muller

unread,
Nov 15, 2007, 1:41:54 PM11/15/07
to rubyonra...@googlegroups.com
>
> 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):
<ul>
<% @project.iterations.each do |it|
<li><%= link_to("start: #{it.start_date}",
project_iterations_path(@project.id, :date => it.start_date)) %></li>
<% end %>
</ul>

> 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'

Kathy...@gmail.com

unread,
Nov 16, 2007, 8:54:10 AM11/16/07
to Ruby on Rails: Talk
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

On Nov 15, 11:37 am, "David A. Black" <dbl...@rubypal.com> wrote:
> Hi --
>
>
>
>
>
> On Thu, 15 Nov 2007, Thorsten Muller wrote:
>
> >> 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
> Seehttp://www.rubypal.comfor details!- Hide quoted text -
>
> - Show quoted text -

David A. Black

unread,
Nov 16, 2007, 9:39:46 AM11/16/07
to Ruby on Rails: Talk
Hi --

I disclaim all legal responsibility for the results :-) 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)

Kathy...@gmail.com

unread,
Nov 16, 2007, 9:50:31 AM11/16/07
to Ruby on Rails: Talk
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 Muller <rails-mailing-l...@andreas-
s.net> wrote:
> > 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

David A. Black

unread,
Nov 16, 2007, 11:15:12 AM11/16/07
to Ruby on Rails: Talk
Hi --

On Fri, 16 Nov 2007, Kathy...@gmail.com wrote:

>
> 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.

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.)

Thorsten Muller

unread,
Nov 16, 2007, 11:30:07 AM11/16/07
to rubyonra...@googlegroups.com
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
;)
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

Damaris Fuentes

unread,
Nov 17, 2007, 1:13:39 PM11/17/07
to rubyonra...@googlegroups.com
Hi again

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

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.

Damaris Fuentes

unread,
Nov 19, 2007, 9:28:44 AM11/19/07
to rubyonra...@googlegroups.com
I have moved the conversation of filtering resources by attributes to a
new thread:
http://www.ruby-forum.com/topic/132154
Reply all
Reply to author
Forward
0 new messages