🕷 zenspider.com

by ryan davis



sitemap
Looking for the Ruby Quickref?

Overriding url_for for Fun and Profit

Published 2005-12-02 @ 13:38

Tagged rails

(guest authored by eric hodel)

A while back I was thinking about MVC and how URLs are built and came to the conclusion that every model should know what its URL is. I don’t think it is the responsibility of the Controller to know this, and certainly not the view.

Since Rails uses a Hash to represent a URL, I thought it would be easy to just add a method to every Model that would return a Hash representation of the URL and override url_for to perform the expansion from a model into its URL Hash.

It turns out that it was easy. After some enhancements by Ryan that adds defaults we came up with the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ApplicationController < ActionController::Base
  def url_for(options = {}, *params)
    if options.include? :model then
      model = options.delete(:model)
      defaults = {
        :action => 'show',
        :id => model.id,
        :controller => model.class.name.downcase,
      }
      defaults = defaults.merge model.url_params if model.respond_to? :url_params 
      options.delete :only_path if defaults.include? :only_path
      options = defaults.merge options
    end
        
    return super(options, *params)
  end
end

You can use the modified url_for like this:

1
2
3
4
5
@model = Model.find some_id
url_for :model => @model
url_for :model => @model, :action => 'edit'
redirect_to :model => @model
link_to @model.name, :model => @model

Sometimes the defaults aren’t good enough though. To override them you add a url_params method to your model. In Trackmap (now dead) I store enough information about a flickr photo to generate its flickr URL in the database and my url_for modification (with a bit of cheating) makes this very clean:

1
2
3
4
5
6
7
8
9
10
11
class Photo < ActiveRecord::Base

  #...

  def url_params
    return { :host => 'flickr.com', :controller => 'photos',
             :action => flickr_nsid, :id => flickr_photo_id,
             :only_path => false }
  end

end