Hatred towards Nested Polymorphic Paths

Those look like legitimate bugs in implementation, rather than caused by confusion. I don't really see a point in extracting that functionality in a plugin, with hope of making a come back to core. If it's gets converted into a plugin, it should stay there, always.

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

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.

Josh Writes:

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

Josh, I grok what Daniel is trying to say, but I can't tell if you do or not. Perhaps this will help.

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

Agree. some_model/1/other_model/2 is a lame way to refer to join_model/1.

Evan

Agree. some_model/1/other_model/2 is a lame way to refer to join_model/1.

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?

> Agree. some_model/1/other_model/2 is a lame way to refer to join_model/1.

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?

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

It’s getting quite large. I’ve included relevant models, controllers and views, limited to an example of the purpose at hand. The index view of the clips controller.

The only time I access clips via a nested route is by the ClipsController - index, and show methods. Other than that I don’t require the nested resources at the moment.

I’m actually very apprehensive about putting this out for public crit, but I’m more interested in how to model this properly for this project and future ones, so please be gentle :wink: There are some very messy parts in the user model associations to access clips. The funny thing is, for all the ugliness they provide, I’m not actually using the methods anywhere yet.

If I’ve left out any important aspects of the code let me know.

http://pastie.caboo.se/77228

-Daniel

1) the uri

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

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

Decided to take some action with a Rails ticket.

http://dev.rubyonrails.org/ticket/8932

You are assuming that you know what the route will be. When developing a controller to handle a polymorphic resource its quite often that you don’t know the type of the objects, thus polymorphic_path([@parent, @child]) becomes much cleaner then a nested if or case statement.

I am actively using this feature on two large scale applications which are deployed into production and it has helped me to maintain skinny polymorphic controllers.

Forgive me for reopening an old thread, but

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! :slight_smile:

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.

Some people use the concept of resource nesting or "belongs to" to manage security, but it is pretty one-dimensional. More robust security systems (role-based access control, for example) have a much richer concept of authorization than could possibly be expressed in a request string. I love nested resources and use them a lot, but I wouldn't have expected Rails to hold up one (simple) interpretation of security as a justification.

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

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?

Are there examples of projects that use this nesting stuff in the wild that we could actually see the code to? I’d love to figure out what I’m missing here, as I really feel like I’m not “getting” it. In the past, for security purposes, I’d just have the user own everything by user_id and use that to see if someone can actually use it.

Thanks for the explanation though, this is a really good topic.

Are there examples of projects that use this nesting stuff in the wild that we could actually see the code to? I'd love to figure out what I'm missing here, as I really feel like I'm not "getting" it. In the past, for security purposes, I'd just have the user *own* everything by user_id and use that to see if someone can actually use it.

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.

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

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.

Thanks everyone! I really appreciate it.

I think nested routes are quite cool and useful, specially if instead of using numerical IDS you use codes that have a meaning for the user

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