"/companies/1/people/1" == BAD
"/people/1" == GOOD
I have no problem with nested collections.
"/companies/1/people"
You shouldn't need to be scoping person #1 if its the unique id.
I've seen alot of defect and enhancement tickets going by Trac showing
its immaturity and lack of real need. None of this stuff has been
merge with the stable, so its not to late to change things. It should
really be pulled from Edge and into a plugin,
"nested_polymorphic_paths".
Its not in the spirit of Rails to be providing tools to help you
practice bad habits.
Other thoughts?
Evan
> --
> Evan Weaver
> Cloudburst, LLC
>
--
Evan Weaver
Cloudburst, LLC
>
> I tend to agree with this. I don't really understand the need to
> "canonically" identify a resource based on its context.
>
> Other thoughts?
>
> Evan
The simple reason would be that you want your URLs to look that way.
Joshua
Whats the join model in this case? Can you pastie the example? (http://
pastie.caboo.se/)
Because each clip (and every other model you mentioned) has its own
unique id, you can always use that to find it.
I think this is going too far. I too prefer to limit the scope to one
level, but there are legitimate reasons to have deeper nesting. /
albums/45/tracks/1 (where the track number is scoped on the album). Or
closer to home, /forums/5/threads/1 and /forums/15/threads/1.
I think it's perfectly fine to encourage people to not nest when they
don't really care one way or another. But to outright "forbid" it is
pushing it.
What are your technical grudges against this? And what would be better
if we didn't support it?
I think this is also a reasonable argument. Again, I'm actually in the
camp of it "not being worth it" from an effort/gain perspective to do
fully nested URLs, but I don't want to push that dictate on someone
else. The URL is half-ways part of the UI design and I'm very
hesistant to push UI conventions on anyone.
Rails applications should be able to look completely incognito.
> You shouldn't need to be scoping person #1 if its the unique id.
I think this is going too far. I too prefer to limit the scope to one
level, but there are legitimate reasons to have deeper nesting. /
albums/45/tracks/1 (where the track number is scoped on the album).
I wasn't clear about banning what..
I wanted to remove the whole polymorphic nesting with an array:
polymorphic_path([@artist, @album, @track]). If you want to do
something like that, use the old school named route for it:
artist_album_track_path(@artist, @album, @track). I've seen a ton of
tickets of people trying to extend polymorphic_path([...]) to do
everything for them when a proper work around with named routes will
do.
link_to @track.name, [@album, @track]
...we should be required to do this:
link_to @track.name, album_track_path(@album, @track)
?
I'm saying you should be doing ...
link_to @track.name, @track
Like I said, [@album, @track] is giving you an excuse to use complex
routing patterns.
If your referring to a HABTM relationship, look into creating a real
join model between them using has many though. That why you have a
real resource you can point your url to.
Since you are so passionate about nested polymorphic resource, it
seems like a perfect fit for you to make and maintain a plugin for
this.
It sounds like the real issue is that there are too many patches and
too many bugs to manage. Well if it can't be implemented cleanly then
maybe it should be pulled. But the whole "encouraging bad practices"
seems spurious.
--
Gabe da Silveira
http://darwinweb.net
Another point I was trying to make. If it was pulling into a plugin
and matured a little, it could then be considered for reentry.
Thanks,
Pratik
On 7/7/07, Josh Peek <josh...@gmail.com> wrote:
>
Heres a few: [7152], #8725, #8782, #8719, #8720, #8705, #8830, #8654,
#8640
/album/1/tracks
/tracks #=> not valid
/tracks/1 #=> valid
/tracks/1/artists #=> Assume a track has many artists
/artists/1/tracks #=> reverse of above; all the tracks for an
artist.
Basically, we want `/foo/x/bar` to be valid, but not `/foo/x/bar/y`.
And we want `/bar/y` to be valid, but not `/bar`. We're finishing up a
plugin that defines those partial routes: index and create as nested
resources, and show, edit and delete as non-nested.
Also, converting an edge functionality into a plugin because of
defects in implementation, defeats the whole purpose of having edge at
first place. I guess it's time to make a choice once and for all, if
it's gonna be in core rails or not.
My vote is to keep it in edge for points explained in posts above.
Thanks,
Pratik
On 7/7/07, Josh Peek <josh...@gmail.com> wrote:
>
>
On Jul 7, 1:22 pm, "Daniel N" <has....@gmail.com> wrote:
> I can't pastie it unfortunately but it. But I believe it goes for any time
> you use a has_many :through relationship with additional data in the join
> table. Without both sides of the join, you can't identify join, and
> therefore don't have access to the extra data.
You identify the join with the id of the join model. Thats the purpose
of using has many though, exposing an actual model. If you could
pastie on example, I could show you how to model it better.
>"/companies/1/people/1" == BAD
>"/people/1" == GOOD
>
>I have no problem with nested collections.
>
>"/companies/1/people"
>
>You shouldn't need to be scoping person #1 if its the unique id.
But isn't that the rub? Who says the ids must be unique? True, Rails
support for composite keys makes it less-than-direct to convert the
REST resource with composite keys into an AR model with unique keys,
but from a UI point of view, some may just want to expose more
intuitive composite keys:
/month/3/week/2/day/1
/albums/6552/track/1
/cuisines/46598/popular_restaurants/1
The path components really are part of the UI -and sometimes a very
visible part. I want mine to be intuitive and that sometimes means
nested with non-unique IDs.
/mother/273753/child/234
/father/342986/child/234
/child/234
In all of those URLs it is very clear which single resource we are
referencing: Child 234. But the URLs give very different context
information. For the first URL, I want to show a pink background, in
the second I want to show a blue background and in the last one, I
want to show a white background, to give a simple example.
Context counts.
Now if we could only get Rails to help us identify that context more
accurately, I'd be happy [warning -going slightly off topic...].
Are these two URLs presenting the same context?
/users/17/groups/2/tags
/groups/2/users/17/tags
Short of routing these URLs to different (otherwise identical)
controllers or manually inspecting the URI, does anybody know how to
differentiate them?
Sure would be nice if Rails exposed the matched route to the
controller! Perhaps an RO attribute of
ActionController::AbstractRequest set in
ActionController::Routing::RouteSet.recognize?
Evan
On 7/7/07, Josh Peek <josh...@gmail.com> wrote:
>
Putting polymorphism aside, what about:
posts/1/comments/34
Which then lets me do
@post = user.posts.find(params[:post_id])
@comment = @post.comments.find(params[:id])
Does that suck?
--
Cheers
Koz
No, it doesn't suck at all, and that snippet represents a Rails "best
practice", doesn't it?
Frankly, I haven't seen any compelling arguments against multi-level
nested routes yet. Not to mention that If you have public-facing URLs,
then the realities of SEO mean that the more relevant information you
can cram into your URLs the better.
Cheers,
Obie
>
>
> --
> Cheers
>
> Koz
>
> >
>
/albums/79/tracks/219
doesn't reveal whether the underlying association between Album and
Track is has-many, has-and-belongs-to-many, or has-and-belongs-to-many-
through. I see this as an asset -- you can refactor your model
associations as business requirements change without having to rework
your uri structure.
2) even if you do have a join table -- say Book
has_many :authors, :through => :authorships -- you still might want
the uri to reference a Book and an Author, which are concrete objects.
rather than an Authorship, which is an abstract concept. In the real-
world DSL of the book trade, a customer might request more information
about a book's author, or about a particular book written by an
author, but not for more information about an "authorship."
On Jul 8, 9:01 pm, "Evan Weaver" <ewea...@gmail.com> wrote:
> Agree. some_model/1/other_model/2 is a lame way to refer to join_model/1.
>
> Evan
>
Not at all. The alternative implies: If you go around promoting every
2-way association (habtm) to an actual Entity, you probably want to
crack open the PoEAA and brush up a bit on Domain Model. Your object
model can be richer than your database schema. That's not a bad thing.
Trying to achieve 1:1 parity between the two seems awfully misguided.
More than that, a single authoritative URI for a particular resource
seems arbitrarily limiting. Objects don't work that way (see:
references). Your database schema doesn't work that way. What goal is
being achieved through it? Nothing at all pragmatic as far as I can
tell...
In fact, the idea that 2-way associations should be banished from your
Domain Model is entirely unique to Rails as far as my limited
experience suggests, and it certainly doesn't strike me as a good idea
at all, but more a misrepresentation of "you can" into "you should". I
might even go so far as to call the unjustified and/or indescriminate
promotion of habtm relationships to has_many => :through as a "code
smell", though I personally can't stand the phrase. :-)
I've seen a ton of tickets of people trying to extend polymorphic_path([...]) to do
everything for them when a proper work around with named routes will
do.
> posts/1/comments/34
>
> Which then lets me do
>
> @post = user.posts.find(params[:post_id])
> @comment = @post.comments.find(params[:id])
Forgive me, but I actually do think that's silly. If I really needed
to do that I'd do
@comment = Comment.find(params[:id], :include=>[:post]
@post = @comment.post
and save a hit to the database.
I'm having a really hard time wrapping my mind around the nesting,
except that the URL looks pretty. Do you really mean to say that we
should be accessing both objects that way? It seems really hackish.
When I did this without nested routes on my first project, I built
URLs this way but never used the post_id in the URL at all. Is that
wrong?
I'm just looking for some enlightenment here, not trying to be
difficult! :)
This is for security/data integrity reasons. When you fetch like the
example above, you can only access posts that were written by the
user. And only access comments written about that particular post. On
a blog that might not matter much, but on an application that keeps
data secret from different accounts, it's paramount.
I justify nested resources because they provide context information.
For example:
/users/23/groups/17/tags
/groups/17/users/23/tags
Those two URLs refer to very different things and it is the ORDERED
nesting that sets them apart. Unfortunately, Rails doesn't provide
any help with the ordering (ticket 8105) obliging me to inspect the
request in order to know which tags we are talking about.
-Chris
But if you have a team of people who can access a project, then how
can you use user_id?
I'd expect almost all of the open source rails applications which do
this kind of security will be following association proxies to
implement their security.
--
Cheers
Koz
> Well see that's exactly why I am confused.
> I understand the data integrity thing and the security thing, but
> how far do you take that?
>
> /users/1/projects/2/tasks/3/notes/4
>
> @user.find(params[:user_id]
> @project @user.projects.find(params[:project_id]
> @task = @project.tasks.find(params[:task_id]
> @note = @task.notes.find(params[:id]
>
> That just really seems to get messy. Is that really the approach
> you'd take? I assume you'd use some sort of includes there to
> reduce the db calls then, right?
>
AIUI, In this situation, I'd be in the notes_controller. I'd have
already set up find_user, find_project, find_task, and
find_note_by_id methods called by before_filter, e.g.,
# in application.rb
private
def find_user
@user = User.find(params[:user_id])
end
def find_project
@project = @user.projects.find(params[:project_id])
end
def find_task
@task = @project.tasks.find(params[:task_id])
end
# in notes_controller.rb
private
before_filter :find_user
before_filter :find_project
before_filter :find_task
before_filter :find_note, :except => [:index, :new, :create]
def find_note_by_id
@note = @task.notes.find(params[:id])
end
Very succinct, I believe.
Michael Glaesemann
grzm seespotcode net
On Aug 3, 2007, at 17:01 , Brian Hogan wrote:
> Well see that's exactly why I am confused.
> I understand the data integrity thing and the security thing, but
> how far do you take that?
>
> /users/1/projects/2/tasks/3/notes/4
>
> @user.find(params[:user_id]
> @project @ user.projects.find(params[:project_id]
> @task = @project.tasks.find(params[:task_id]
> @note = @task.notes.find(params[:id]
>
> That just really seems to get messy. Is that really the approach
> you'd take? I assume you'd use some sort of includes there to
> reduce the db calls then, right?
>
AIUI, In this situation, I'd be in the notes_controller. I'd have
already set up find_user, find_project, find_task, and
find_note_by_id methods called by before_filter, e.g.,
# in application.rb
private
def find_user
@user = User.find(params[:user_id])
end
def find_project
@project = @user.projects.find (params[:project_id])
so
projects/2/tasks_category/3/task/5
becomes
projects/CoolWebApp/task_categories/design/tasks/user_profile
Such a url is hackable and meaningful, it's currently supported by
rails and I'd be disappointed if it won't anymore.
that's my 0.02
Paolo
> Sure. Very much so... and that's exactly how I've been doing it.
> So what I understand is that it's perfectly fine to query the
> database that many times, because it's good for security and data
> integrity. I can live with that. Just wanted to make sure that was
> the best way to explain it to my students. Some people will surely
> look at the sql logs and see four separate (albeit small) queries
> and wonder why that's necessary, so I want to have a good answer
> for them.
If performance were an issue, I'd benchmark it as is, then test it
using a find_by_sql. So far, performance hasn't been an issue for me.
I'd also have to figure out how to go about initializing models with
the values pulled from that find_by_sql. Premature optimization, and
all that.
MIchael Koziarski brings up an interesting point, however, and I'm
interested in hearing discussion on that as well.
> Indeed. I'd love to see examples of this proxy for authentication
> for teams of which he speaks. :)
/projects/3/tasks/8/notes/2
class User < ActiveRecord::Base
has_many :roles
has_many :projects, :through => :roles
end
class Project < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
has_many :notes
end
class NotesController < ApplicationController
before_filter :find_project
before_filter :find_task
before_filter :find_note
def show
end
protected
def find_project
@project = current_user.projects.find(params[:project_id])
end
def find_task
@task = @project.tasks.find(params[:task_id])
end
def find_note
@note = @task.notes.find(params[:id])
end
end
Notice each find is scoped using it's AR associations proxy, starting
from current_user.projects
-- tim
Using polymorphic paths to be more logical and nice to look is great...
but if security is one added advantage, is it really the appropriate
place to put such (only!) security measure?
--
Hendy Irawan
www.hendyirawan.com
> To add, shouldn't security/authorization be implemented mostly in the
> model?
Depends on the level you have to authorize on. I've never written an
application where is was necessary to drop down to model level to
ensure people didn't access information they shouldn't access.