Given the following resource definition:
map.resources :posts, :except => [:show]
map.post '/:year/:month/:slug, :controller => :posts, :action => :show
I can make url_for
work for me, using this syntax:
<%= link_to @post.title, post_url(:year => '2010', :month => '02', :slug => 'test') %>
But is there a way to make this work?
<%= link_to @post.title, @post %>
Currently it throws this error:
No route matches {:year=>#<Post id: 1, title: "test", (...)>, :controller=>"posts", :action=>"show"}
Apparently it passes the @post object to the first route parameter (seems like a Rails bug...). But can I make this work for me? I'll add that messing with default_url_options
is a dead end.
Solution working only in Rails 3.x is ok, but I would prefer not to use any plugins.
Override to_param
. The default value for to_param
is the record ID, but you can change that.
class Post
def to_param
"/#{year}/#{month}/#{slug}"
end
end
(This assumes you have methods in the Post
class for year
, month
and slug
.)
Now when URLs are generated, your overridden to_param
will be called.
<%= link_to @post.title, @post %>
=> <a href="/2010/02/foobar">Foobar</a>
Obviously you must ensure your PostsController show action can find the record based on the parameters it is passed.
You may need to change your route definition and eliminate this route:
map.post '/:year/:month/:slug, :controller => :posts, :action => :show
However, with to_param
overridden, the default RESTful post_path
will work just fine for the URLs you want to generate.
Unfortunately, when you pass an ActiveRecord to link_to
, it tries to use Polymorphic URL helpers to generate the URL for the link.
The Polymorphic URL helpers are only designed to generate RESTful URLs by using the record identifier of your ActiveRecord.
Since your route uses multiple attributes of the Post object, the Polymorphic URL helpers are not equipped to generate the correct URL... not so much a bug as a limitation :)
Delving into link_to
, when you pass it a Hash, it doesn't use Polymorphic Routing, so you avoid the whole problem.
I suppose a hacky approach would be to define a method on Post called routing_hash
which returns
(:year => post.year, :month => post.month, :slug => post.slug)
I appreciate that it's not a DRY approach, but it's the best I can come up with at the moment
How about fixing url_for
for the PostsController
? May not be very elegant but it is DRY and things should just work.
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
protected
def url_for(options = {} )
if options[:year].class.to_s == "Post"
obj = options[:year]
options[:year] = obj.year
options[:month] = obj.month
options[:slug] = obj.slug
end
super(options)
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With