Acts_as_urlnameable Instructions

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


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


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)
    # We got a string, a urlname id
    found_foo = Foo.find_by_urlname(id)   

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
    # 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)
  def self.down
    drop_table 'urlnames'

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.


Open Source as a Roadside Picnic

One of the many changes that Jessamyn suggested for MyKidsLibrary is that the URLs should be comprised of meaningful text rather than just numbers.  In addition to being more human-friendly, it is, apparently, an important search engine optimization technique.

Ruby on Rails likes to construct URLs that end with a numeric identifier that is used to look up a specific record in the database.  It is an efficient, effective solution, and the software engineer side of me never considered why you'd have it be otherwise.  I have come to think of URLs as being things that are as effectively meaningless and worthless to my brain as printouts of UNIX coredumps.  I click on links, I bookmark pages, I never pay the slightest attention to URLs.  I use tools -- browsers, bookmarking services -- to work with URLs just as I use tools to write software.

Once I decided to go about making the change, I set out to find who else had already done this work.  The Rails ecosystem is vast and densely populated; I knew that there was but a very tiny chance that I'd actually have to start from scratch.  Sure enough, a little work on Google revealed that there were many candidate solutions.  I picked one that looked solid and set about integrating it into my project.

I'm not new to this; I have been a working, salary-earning software engineer for nearly two decades, so I should have been prepared for the documentation to suck.  The documentation always sucks.  The last time that I read really good, comprehensive documentation was when I was writing code for a VMS system, and I sat right next to the big orange wall.  At least, I remember it being good; it's all so long ago that I might be remembering it in a somewhat nostalgic light.

I had to figure out a lot of things that weren't mentioned in the documentation, and while that's not the worst thing, it is still frustrating to see a useful, well-put-together package that stops just short of being perfect.  And, really, they all do.

Open Source is invaluable, but in many respects, it reminds me of a Roadside Picnic.

And just to prove that I'm not a hypocritical dick, my next post will include extensive, failproof instructions for configuring and using the wonderful Rails plugin acts_as_urlnameable.


The joy of productivity

What time I had this weekend that was not spent ferrying children to-and-fro, maintaining my yard, grilling steaks for Mother's Day dinner and otherwise acting hyper-kinetic was spent adding discussion forums to My Kid's Library.  I had managed to put together the data models during the week, but other than that, I was starting from virtually zero code when I began to work on the new feature on Friday afternoon.

I worked whenever I could sit down at my computer for five minutes.  I would implement a method in a controller here, I would slap together HTML there...I'm not sure that I could accurately gauge how much total time I spent working on it, but it couldn't have amounted to more than four hours.  However, as of Monday morning, I have fully-functional, reasonably-attractive, usable discussion forums.  Don't go looking for them quite yet on the public site, as the code changes haven't been published, but they will be quite soon.

That I was able to create a significant amount of functionality in very limited time and with a pleasingly-small amount of code is a testament to Ruby on Rails.  It does what it does well. Very well.  It continues to impress and surprise me even now, a year-or-more since I started using it to develop a live, working web site.  I still have a little bit of the happy buzz that comes along with completing a major feature in a piece of software, and that feeling comes in the wake of a couple of days that didn't see me sitting at the computer for any extended length of time at all.  Crazy.  Awesome.

Thanks for the buzz, Rails.


Going back to MyKidsLibrary.com

I've been playing hard with AppEngine, Python and Django for a couple of weeks now, and I've managed to come out of it with a useful application: this blog.  While I'd hardly say that it is finished, as there are still some features that I'd like to add, I have climbed the steep -- and thus challenging, interesting, addictive -- part of the learning curve.  So, I can no longer justify continuing to blow off MyKidsLibrary.com.

MKL is my primary off-hours project, and it has been for almost a year now.  It's been in public beta for a couple months now, and I am just beginning to implement the larger features that were suggested by the very helpful and considerate testers.  Apparently, as it stands, it lacks the stickiness that is mandatory for a successful social/crowd-sourcing web site.  Last night, I designed the data models for the new capabilities, and I started getting that buzzy feeling of excitement that I had lost during the last few months of coding that preceded the start of beta.

Getting back in to the Ruby on Rails groove was easy and enjoyable.  As cool as AppEngine is right now, Rails beats it hands-down for a productive development experience.  It was also nice to be back in NetBeans.  Komodo and the other IDEs that I played around with for Python were decent enough, but they felt immature, especially as I never was able to do any kind of step-through-the-code debugging with AppEngine and the tools that I had.  That seriously rubs me the wrong way; I've been coding far-too-long to be stuck with printf-style debugging.  It's bad enough that working with dynamically-typed languages seriously limits the code completion that is available.


Still learning, still liking

I'm continuing to work on this blog software, and I am learning a lot about AppEngine, Python and Django.  I used Django's include function to refactor the rendering of posts.  That removed some code duplication.  Got to love that.  I feel like I've already done a full day's work, and it's only 10:30!

My inclination is next to look at extending Python's WSGIHandler further to make it more MVC-like.  I'd never paid much attention to that design pattern before I started using Ruby on Rails, but I'm quite addicted to it now, and doing the extra work -- and admittedly, it's not that much -- to do it the AppEngine way irks me.  I've already taken a step in that direction, subclassing RequestHandler to do all the work of creating a path to the appropriate template file and passing it to the render method, but I know that there's a lot more to do.  I also feel like I could refactor the handling of the dictionary that passes values to the template to reduce the repititious setting of values that happens in each handler.


Live at Last!

Learning to create Google App Engine applications via building this simple blog has been hugely fun and not just a little bit frustrating.  Many, many thanks to Google's Marzia for so diligently helping me to figure out the missing piece that kept me from being able to upload this code.

I've come away from the experience feeling very enthusiastic about App Engine.  It's not going to replace Ruby on Rails as my web development platform of choice, but I have to admit that GAE makes it very, very easy to quickly build and deploy an application.  I managed to become a pretty competant Python programmer in just a few days, and I know enough about Django to build a small web application that observes the DRY principle.

I became very frustrated that Django is a whole seperate language unto itself, so I had to learn Python and Django.  I suppose that I have become spolied by Rails, which allows me to write the logic, data and presentation layers in one language.  It's so simple, beautiful and elegant.

That's not neccesarily an argument against GAE, which it seems is going to support multiple language and frameworks as it matures.  The combination of Ruby on Rails's power with Google's essentially infinite scalability would be a mighty and compelling thing.