|
This post originated from an RSS feed registered with Ruby Buzz
by Matthias Georgi.
|
Original Post: DRY Up Your Url Helpers
Feed Title: Matthias Georgi
Feed URL: http://feeds.feedburner.com/matthias-georgi?format=xml
Feed Description: Webdev, Gamedev and Interaction Design.
|
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Matthias Georgi
Latest Posts From Matthias Georgi
|
|
This tutorial shows you how to simplify url generation in combination with RESTful resources by extending the url_for helper. This approach will also work with nested routes and other helpers like form_tag and link_to. Each resource will have its own unique url, which is generated by calling url_for(resource).
First we need a simple example. We have 2 models: User and Article. For our url generation to work we have to add following code to our models:
class User
has_many :articles
def to_params
{:id => permalink}
end
end
class Article
belongs_to :user
def to_params
{:user_id => user.permalink, :id => permalink}
end
end
This is necessary for nested routes to play nicely with our url generation code. We are now able to find the parameters for each record to generate an unique URL.
Our nested routes are defined in config/routes.rb:
map.resources :users do |user|
user.resources :articles
end
And now add the following module into your lib folder and include the module in both your application controller and application helper:
module ResourceHelper
def plural_class_name(record)
record.class.name.underscore.pluralize
end
def singular_class_name(record)
record.class.name.underscore
end
def params_for(record)
if record.respond_to?(:to_params)
record.to_params
else
{:id => record.to_param}
end
end
def collection_url(collection, record, options)
if record
params = params_for(record)
params["#{singular_class_name(record)}_id".to_sym] = params.delete(:id)
url_for options.merge(params).merge(:controller => collection)
else
url_for options.merge(:controller => collection)
end
end
def member_url(record, options)
url_for options.merge(params_for(record)).merge(:controller => plural_class_name(record))
end
def url_for(*args)
if [String, Hash].any? {|type| args.first.is_a? type }
super(*args)
else
if args[0].is_a?(Symbol)
collection_url(args[0], args[1], :action => 'index')
else
member_url(args.first, :action => 'show')
end
end
end
def new_url_for(collection, record=nil)
collection_url(collection, record, :action => 'new')
end
def edit_url_for(record)
member_url(record, :action => 'edit')
end
def path_for(*args)
if args[0].is_a?(Symbol)
collection_url(args[0], args[1], :action => 'index', :only_path => true)
else
member_url(args.first, :action => 'show', :only_path => true)
end
end
def new_path_for(collection, record=nil)
collection_url(collection, record, :action => 'new', :only_path => true)
end
def edit_path_for(record)
member_url(record, :action => 'edit', :only_path => true)
end
end
So how can you use this stuff actually?
It is pretty easy: just pass the record instead of the url hash and the unique url will be generated automatically. This works for helpers like url_for, form_tag or link_to.
Some examples:
new_path_for(:users) # => '/users/new'
path_for(user) # => '/users/harald'
edit_path_for(user) # => '/users/harald;edit'
path_for(:articles, user) # => '/users/harald/articles'
new_path_for(:articles, user) # => '/users/harald/articles/new'
path_for(article) # => '/users/harald/articles/article-1'
edit_path_for(article) # => '/users/harald/articles/article-1;edit'
link_to article.title, article
form_tag :articles
form_tag article, :method => 'put'
If anybody is interested I will release this stuff as plugin. I think other helpers could benefit as well as you can pass your records around and each helper may generate the appropriate url.
Read: DRY Up Your Url Helpers