The Problem
## routes.rb
map.resources :photos
map.resources :users, :has_many => :photos
map.resources :tags, :has_many => :photos
## photos_controller.rb
class PhotosController
## in the create method
wants.html { redirect_to url_for(url_helper_params) } # I know DHH doesn't want us to use this, but how else can I get this effect?
## in the destroy method
wants.html { redirect_to photos_url } # unfortunately, this will drop any parent paths we may have
# a user who called DELETE /users/james/photos/1 ends up at /photos instead of /users/james/photos)
private
# Note: I actually use a modification to make_resourceful to do this far more cleanly, but wanted to be as complete as possible in this example.
def parent_object
case
when params[:user_id] then User.find_by_id(params[:article_id])
when params[:tag_id] then Tag.find_by_id(params[:news_id])
end
end
def url_helper_params
[parent_object, @photo].reject { |obj| obj.nil? } # we have to get rid of any the parent_object if it's nil, or else this will fail :(
end
end
# index.rhtml
<%= link_to "New Photo", new_photo_path %> # same problem as our destroy redirect
We have routes like /users/james/photos, and /tags/montrealonrails/photos. We want to seamlessly maintain the top level of the path without resorting to conditionals or repetition. That is, if you browse to /users/james/photos, and click on the show link, you should go to /users/james/photos/1, not /photos/1. There should also be a polymorphic plural url helper. We have url_for for members (/photos/1), but for collections (/photos), we're stuck resorting to calling the url helpers themselves. So, to create a collection url, we'd have to check what the parent object was, and then call the appropriate helper based on that. And, all of this is further complicated when there are namespaces (/cms/products). Wouldn't it be great if there was a better way?
Stupid Simple Urls
Try saying that three times fast. Done? Good, because it's not called that - I was just messing with you.URLigence
Intelligent url building.## routes.rb
map.resources :photos
map.resources :users, :has_many => :photos
map.resources :tags, :has_many => :photos
map.namespace :admin do |admin|
admin.resources :products, :has_many => :options
admin.resources :options
end
##
assert_equal "/photos", smart_url(:photos)
assert_equal "/photos", smart_url(nil, nil, nil, :photos) # in case your parent objects aren't there
assert_equal "/users/james/photos", smart_url(User.find_by_name(:james), :photos)
assert_equal "/users/james/photos/1", smart_url(User.find_by_name(:james), Photo.find_by_id(1))
assert_equal "/photos/1", smart_url(nil, Photo.find_by_id(1)) # in the case that your parent object isn't there
assert_equal "/photos/1/edit", smart_url(:edit, Photo.find_by_id(1))
assert_equal "/admin/products", smart_url(:admin, :products)
assert_equal "/admin/products/1", smart_url(:admin, Product.find_by_id(1))
assert_equal "/admin/products/1/options", smart_url(:admin, Product.find_by_id(1), :options)
assert_equal "/admin/products/1/edit", smart_url(:edit, :admin, Product.find_by_id(1))
assert_equal "/admin/options", smart_url(:admin, nil, :options) # in the case that your parent object isn't there
It's pretty straightforward. Just call smart_url with any parameters that might be there, including ones that might be nil, and symbols at the beginning for namespaces (simple_url/cms/products), member actions (/products/1/edit), or both (/cms/products/1/edit), objects in the middle, and a symbol at the end for a resource's index method (/products). That's it.
Get It
Get version 0.1 at http://svn.jamesgolick.com/urligence/tags/stable. Oh, and don't take the name of the tag too seriously - I just wrote it tonight.svn export http://svn.jamesgolick.com/urligence/tags/stable vendor/plugins/urligence
Let me know what you think of it.


My friend Mina put together something similar for our purposes, only he also has some controller magic so that you can easily refer to a polymorphic resource's parent from the polymorphic resource's controller.
I've pointed him at your post so hopefully he'll comment with details.
Hi, sorry for the late reply.
While what I've done isn't as pluginized, I see that we've both re-invented the same wheel albeit sith slightly different conventions.
Basically I stuck with the notion of "parent" with these helpers in ApplicationController:
<table class="CodeRay"><tr> <td title="click to toggle" class="line_numbers" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"></td> <td class="code"></td> </tr></table>The bulk of the code above is to provide support for the parent(x) function which returns an ActiveRecord instance of parent X - x is optional and defaults to -1 which means for a url like this: http://domain.com/categories/20/posts/19 parent will return the AR instance of Category id 20
That in itself is awesomely helpful since in PostsController for example you can do:
<table class="CodeRay"><tr> <td title="click to toggle" class="line_numbers" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"></td> <td class="code"></td> </tr></table>Now on to the URL helpers I ended up creating a family of poly* URL generators (polypath/polyurl/polyppath/polypurl).. The ones with _p in their name automatically prepend parents defined above so you can generate a URL that automatically includes "your parents":
<table class="CodeRay"><tr> <td title="click to toggle" class="line_numbers" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"></td> <td class="code"></td> </tr></table>When I have some time I'll check out your plugin and would happily post back delta features I find in common.
finally, I hope the blog keeps all the formatting above.. there's no preview comment.. here it goes.. :)
Have you looked at the ResourceFu plugin? I'm using it for polymorphic attendees to both webinars and training courses. I haven't found any documentation outside the readme but it works really well once you understand it.