Special characters and nested routes »
Created at: 14.11.2007 22:58, source: poocs.net - Home, tagged: rails
I might be the only crazy guy on the planet using strings as the primary record identifier in the URI and also allowing a bunch of special characters in it. And just in case I am, please move along. I’ll just keep this article around as a reference for myself.
Say you’ve got a Rails web app from the early days. Way back when REST and Rails haven’t been spending day and night together. Back when we just had named routes.
But actually, back when it was already popular to occasionally use something different from the numeric primary key to identify a resource in a URL. Example:
/profile/show/scoop
Well, as far as that would actually resemble a URI of my own profile, some people are way fancier with their nicknames and the punctuation therein. The most prominent punctuation character is probably the period (.).
Back in the days, that was all dandy. Well, that was then and this is now.
Meet the Resource
With restful routes, we don’t need no stinkin’ named routes (for the most part). A simple declaration like
# config/routes.rb
map.resources :users
and we get a boatload of useful helpers such as user_path and new_user_path generating the appropriate URIs for us. But I digress.
Rewiring our application to use restful routes for the User model (after having seen the light), all’s well until you try to access a URI like
/users/mr.piggy
Eek! Rails interprets the period as a designation of format, like in /users.xml to offer an XML representation of the requested resource.
Now, who wants a piggy representation of a user?
To allow a period in the params[:id] part of the URL (which, after all, is what the username ends up in after routing is done chopping apart the requested URI), Rails 2 will have a way to re-use the :requirements parameter as an argument to map.resources, like so:
# config/routes.rb
map.resources :users,
:requirements => { :id => %r([^/;,?]+) }
(Note: This has been committed to the Rails trunk back in February of 2007 as changeset 6232. I backported it as a patch to Rails 1.2 residing locally in my application (so I didn’t have to directly modify the Rails code. If there’s an interest in that I’ll post it as a separate article in a few days.)
Boom, now /users/mr.piggy nicely ends up having a params[:id] value of mr.piggy (instead of a value of mr and a failing attempt to render a piggy format).
What about nesting?
But our journey doesn’t end here. Coincidentally, if a site has users, those users tend to interact with it (in a true Web 2.0 fashion, you know).
Adding further to routes.rb, we now have a routing configuration like this
# config/routes.rb
map.resources :users,
:requirements => { :id => %r([^/;,?]+) } do |users|
users.resources :interests
users.resources :buddies
end
Easy, right? Trying to access a URL like /users/mr.piggy/buddies throws an exception though.
No route matches "/users/mr.piggy/buddies" with {:method=>:get}
The fix would be to add the :requirements constraint to all the nested routes as well.
# config/routes.rb
map.resources :users,
:requirements => { :id => %r([^/;,?]+) } do |users|
users.resources :interests,
:requirements => { :id => %r([^/;,?]+) }
users.resources :buddies,
:requirements => { :id => %r([^/;,?]+) }
end
Such a nasty repetition. Especially considering we might add a bunch of additional resources in there. Object#with_options to the rescue. Here’s the refactored version:
# config/routes.rb
map.resources :users,
:requirements => { :id => %r([^/;,?]+) } do |users|
users.with_options :requirements => {
:id => %r([^/;,?]+)
} do |users_requirements|
users_requirements.resources :interests
users_requirements.resources :buddies
end
end
Much better. Accessing /users/mr.piggy/buddies works as expected now, routing to BuddiesController#index with params[:user_id] having a value of mr.piggy, which you can use in a before filter to setup the parent resource.
Final words
It’s debatable whether or not it’s a good idea to allow punctuation characters in the URI. In my case, reworking parts of an existing application with an existing user base of several hundred thousand registered users I didn’t have an option but to comply.
more »
Slow queries: Rails habtm gotcha »
Created at: 09.11.2007 00:23, source: poocs.net - Home, tagged: rails
Back in the old days when some brave souls started with Rails, the framework would automatically supply helper methods to check the availability of associated records for every has_many or has_and_belongs_to_many association in the form of has_foos?.
For example, given a declaration like this:
class User
has_and_belongs_to_many :interests
end
would get you User#has_interests? back in the days.
Fast forward to the present, these methods have long been removed from Rails core. There’s no 1:1 replacement, really. When you want to check a collection in a conditional for whether or not it’s empty, there are several things you can do.
Association Proxies, a quick review
Basically, every collection is a derivative of the Array class, as such most of Arrays methods have been (re-)implemented for the respective collection proxies.
First of all, there’s collection#length, which is the most brutal of them all because it simply loads the whole collection into memory and runs #size on it.
Next up is collection#count, which uses an SQL COUNT() statement to just fetch the number of associated records and is usually the quickest way at that.
Finally, there’s collection#size, which is basically a combination of both length and count since it behaves differently depending on whether or not the collection has already been loaded at the time it is being called. Given the collection has already been loaded (by a prior statement in your code, what have you) it’s identical to collection#length. If it hasn’t been loaded, its behavior is identical to collection#count.
Best practice
Judging from the rundown above, it’s usually best practice to simply use collection#size and trust it to do the right thing. In case you’re planning to use a collection as soon as you found out there are records associated with it, use collection#length to load the connection right away and then loop over it. Beware, however, that this is advisable only for those cases that don’t use pagination. (You’re using will_paginate for that, aren’t you?)
In a conditional, it makes most sense to use collection#empty?, which uses size#zero? internally.
unless user.interests.empty?
# ...
end
The problem
99% of what I said above applies to both has_many and has_and_belongs_to_many associations. Admittedly, poor little habtm has had its momentum and seems to be on the way out of the cool kids’ block. Still, there are a few cases where it’s still the best fit (such as the User#interests example from first section of this article) and some people *do* have Rails code that’s been in existence for a few years. (Well, maybe only I do.)
So, the 1% that behaves differently is that size on a habtm collection is not as intelligent as its little brother from the has_many camp. In fact, it’s pretty dumb because it’s in every case behaving identical to length meaning it’s loading the whole collection. Ouch.
>> user.interests.size
Join Table Columns (0.001284) SHOW FIELDS FROM interests_users
Interest Load (0.001263) SELECT * FROM interests
INNER JOIN interests_users
ON interests.id = interests_users.interest_id
WHERE (interests_users.user_id = 142841 )
The solution
You have to fill in the gaps of habtm#size’s lack of intelligence yourself. If you trap over habtm queries that take way too long for your taste, it might be about time to swap out that size call for an always speedy count call.
>> u.interests.count
Interest Columns (0.001006) SHOW FIELDS FROM interests
SQL (0.009749) SELECT count(*) AS count_all FROM interests
INNER JOIN interests_users
ON interests.id = interests_users.interest_id
WHERE (interests_users.user_id = 142841 )
You would then also avoid using collection#empty? in your conditionals, since that would then resort back to using size internally. Instead, use the count#zero? form or define your own instance methods on the association.
if user.interests.count.zero?
# ..
end
Postscript
To actually find out what Rails is transforming your method calls into (in terms of generated SQL) I prefer to use this technique by the always-wise Jamis Buck. Redirecting the ActiveRecord logger to the shell’s standard output will intermix the SQL statements with the output of your method calls, making everything relevant visible at first glance.
more »
Revisiting date localization for Ruby 1.8.6 »
Created at: 23.10.2007 17:45, source: poocs.net - Home, tagged: rails
Welcome to my yearly post about a revised version of a simple way to localize Ruby’s Time#strftime method (after my original post in ‘05 and the update in ‘06).
What’s changed?
In my original methodology, the arrays in the Date class containing the
actual day and month names in clear text had to be replaced by strings wrapped
in the infamous _() call of GetText in order to spit out their localized
versions when requested from the modified Time#strftime call.
When Ruby 1.8.6 came along, it had its Date array constants frozen. While I
agree it’s generally not a good idea to modify the contents of a constant
(it’s a constant after all, mind you), the implementation I came up with 2
years ago worked fine up to and including today.
Since, also, most of the sites I’m working on require multiple languages (as
opposed to, say, a single language set at application boot time), the contents
of those Date arrays truly need to be modified at runtime and having those
arrays frozen meant jumping through all sorts of hoops to address a supposedly
simple issue.
What now?
Well, I had to come up with a way to actually execute the gettext
functionality in the context of an object that has the current GetText locale
bound to it. The most obvious object with that kind of property is
ApplicationController.
But this still leaves us with the issue that we cannot modify the frozen
Date constants and that everything we put into these constants is evaluated
right away instead of at a point where we have the desired locale set.
What I came up with was a proxy object derived from the Array class that
would basically just wrap the cleartext contents that are already in each of
the Date arrays but not before they’re actually accessed/output.
# lib/date_localization.rb
class GetTextDateProxy < Array
cattr_accessor :gettext_proxy
def [](key)
_(at(key))
end
def _(string)
return string unless gettext_proxy
gettext_proxy.instance_eval { gettext(string) }
end
end
As you can see, the proxy object is pretty simple after all. It just
overwrites the default of Array#[] to look up the requested key and then
wrap the output in _().
Since I couldn’t actually be bothered to include the whole of Ruby-GetText
into my poor little proxy object, I then made room for it to hold a pointer to
an instance of ApplicationController in a class attribute called
gettext_proxy. The local underscore method of my proxy class then wraps
around the actual gettext implementation available to the gettext proxy (read:
ApplicationController), returning the string unchanged if there is no
gettext proxy and if there is, run the string through the proxy’s gettext
method.
Dealing with a state of not having a gettext proxy available is actually
necessary in cases where there are calls to these arrays before
ApplicationController gets to hook itself up to GetTextDateProxy.
But how does this actually hook into the Date class now? Let’s take a look:
# lib/date_localization.rb
class Date
silence_warnings do
%w(
MONTHNAMES DAYNAMES ABBR_MONTHNAMES ABBR_DAYNAMES
).each do |array|
class_eval %( #{array} = GetTextDateProxy.new(#{array}) )
end
end
end
While this still looks a little tedious, it’s far less tedious than repeating
the arrays’ contents simply wrapped in _() all over the place, like we had
to do in previous incarnations of this methodology.
Basically, now every array is hooked up with their very own instance of
GetTextDateProxy which then takes care of running the strings through
gettext in the appropriate moments.
Wiring it all up
For the last part, we need to get ApplicationController to hook itself into
GetTextDateProxy after having received the appropriate locale for the
request it’s dealing with.
The best fit for this is the after_init_gettext hook provided by
Ruby-GetText. Adding this code to ApplicationController will take care of
the last required step.
# app/controllers/application.rb
after_init_gettext { GetTextDateProxy.gettext_proxy = self }
Here you go, working (albeit hackish) localization for Ruby’s Time#strftime working up to and including the most recent version of Ruby, which is 1.8.6 as of this writing.
Enjoy.
more »
Ruby-Debug HOWTO and free book »
Created at: 02.10.2007 21:59, source: poocs.net - Home, tagged: rails
Somewhere between the release of my Rails book and now, several things happened. First of all, Ruby 1.8.6 was released in March and quickly became the preferred version to use with Rails application with the release of Rails 1.2.3.
This, however, meant the end to the era of the breakpointer library to debug your Rails application, which relied on a bug in Ruby that had been fixed in Ruby 1.8.5 onwards. The Rails Core group was quick to adopt ruby-debug, Kent Sibilev’s native implementation of a Ruby debugger which was also heavily inspired by more powerful debuggers from other programming languages such as C.
With the preview release of Rails 2.0, available since Sep 30, integration of ruby-debug has been fostered even more.
For this very reason, I recently sat down and wrote a replacement chapter for my Rails book that serves both as a gentle conceptual introduction to ruby-debug (which still somewhat lacks detailed hands-on articles, due to its still fairly young age) and a replacement for the example debugging sessions the printed book has (but relying on the slightly different operation of the breakpointer client).
The article is available through SitePoint.com. Please note, however, that you should have the source code for the sample application from the book available, to give yourself some context and the full scope of the code.
Rails book available for FREE
On a related note, SitePoint, the publisher of Build Your Own Ruby on Rails Web Applications, decided to offer the entire book as a free PDF download for a limited time period of 60 days starting today. Grab your copy of the 474 pages (including the updated chapter on debugging making use of ruby-debug) here.
more »
Loading all Rails test fixtures with fixtures :all »
Created at: 27.05.2007 19:39, source: Cody Fauser - Show all, tagged: rails rails testing
Are you as tired as we were of loading 20+ different fixtures in each of your Rails test classes? We were, and we even added a method all_fixtures() to test_helper.rb to do the loading of all our fixtures for us.
Thankfully though, we don't need our own helper method anymore, as the Rails fixtures() method will now accept a symbol :all, which will instruct the test helper to load all of your fixtures automatically.
1 2 3 4 5 6 7 8 |
require File.dirname(__FILE__) + '/../test_helper' class ShopTest < Test::Unit::TestCase fixtures :all # Your tests here end |
As of Rails 1.2.3 this feature has not yet been merged from the trunk. This means that you'll either need to run Edge Rails from Subversion, or install the beta Rails gems as follows:
sudo gem install -s http://gems.rubyonrails.org rails -y
Happy testing!
more »

