December 07, 2008
A few posts ago, I promised to share my fail-proof instructions for installing and integrating the Ruby on Rails plugin called acts_as_urlnameable. Here is what I learned. Since I put this together, I have discovered some more issues that I need to find workarounds for, and I'll post about those later. For now, here's how you get it working for you in the vast majortiy of cases.
1. Install plugin:
- script/plugin install http://code.helicoid.net/svn/rails/plugins/acts_as_urlnameable/ will make a static copy of the source for you, or
- script/plugin install -x http://code.helicoid.net/svn/rails/plugins/acts_as_urlnameable/ will fetch a copy via SVN
2. Add acts_as_urlnameable to //environment.rb// if needed. If you define config.plugins, add urlnameable there.
3. For each model that will be urlnameable, add acts_as_urlnameable:
class Foo < ActiveRecord::Base acts_as_urlnameable :nameable_field end
4. Add to each Model an override of to_param. This implementation differs from the suggested ones by continuing to provide the default numeric id for records that haven't been urlnameified yet. This should help you to avoid breaking existing functionality when adding this to an existing website:
def to_param if urlname and urlname.length > 0 urlname else id end end
5. Add a new method, smart_find. Again, this approach allows you to have mixed numeric and urlnamed ids. This will save a good deal of time when converting an existing Rails application to use acts_as_urlnameable. There are plenty of places in code that you won't care about having pretty, legible ids, like in HIDDEN form fields. This reduces the total exposure to your code:
def self.smart_find(id) found_foo = nil if id.to_i > 0 # We got a regular, old int id, so look it up as usual found_foo = Foo.find(id) else # We got a string, a urlname id found_foo = Foo.find_by_urlname(id) end end
6. Add a migration to add the table and apply to existing rows:
class AddUrlnamesTable < ActiveRecord::Migration # :nodoc: def self.up create_table 'urlnames' do |t| t.column 'nameable_type', :string t.column 'nameable_id', :integer t.column 'name', :string end # For each Model to which acts_as_urlnameable will apply # and which has existing rows, add a loop like the following; # simply resaving each record will add the urlname data. for each_foo in Foo.find(:all) each_foo.save end end def self.down drop_table 'urlnames' end end
7. In each controller that references one of the now-acts_as_urlnameable Models, change references to Foo.find to Foo.smart_find
8. In all of your views, check for links that use the pattern '':id => @foo.id'' and replace them with '':id => @foo''. This will allow the to_param override to intelligently choose which id to provide.
That's the basic idea, and that should be enough to get anyone going with acts_as_urlnameable. During the course of integrating it into My Kids Library, I have discovered a number of circumstances that require some pretty sophisticated customization, and I will detail those in future posts. Until then, I am happy to answer any questions posted in the Comments section.