Passively Multiplay

Friday, 4 January, 2008

acts_as_list :scope

With a helpful nudge from Artur, we’ve been using UUID primary keys on almost all of our Rails models to ward off scalability problems further down the line (see this for a decent guide to how and why).

In general, Rails copes with it all extremely well. As you’d expect, Rails handles standard MySQL primary keys without any intervention, but if you want to use UUID keys it offers you enough callbacks to do so. There is one such instance where we had problems, though, where one of our models is using acts_as_list along with acts_as_tree.

With a standard ‘acts_as_list :scope => :parent_id’ declaration, Rails has some trouble parsing a UUID key when it expects a standard integer one. Like this:

Mysql::Error: Unknown column ‘9dd631cc’ in ‘where clause’: SELECT * FROM branches WHERE (parent_id = 9dd631cc-5ac3-11dc-b5f5-0017f232d58f) ORDER BY position DESC LIMIT 1

Which, as you can see, is caused by improper quoting of the ‘parent_id‘ field. UUIDs are strings, and Rails is parsing it as an integer. Digging into the guts of ‘acts_as_list’ shows us that the relevant conditions are created by a method called ’scope_condition’ which is setup with a class_eval. Before you go digging any further, though, the API docs helpfully point us back to the acts_as_list :scope‘ arrangement. Although ‘acts_as_list :scope‘ expects a symbol from which to create the ’scope_condition’ method, we can pass in a string, and have Rails take care of the quoting, like so:

acts_as_list :scope => “parent_id”

Now everything is quoted properly, and the tree structure happily accepts UUID primary keys! As you might expect, my reaction to this was, simply, ‘awsm’.

Nb, db/schema.rb also has some problems with UUID primary keys, ignoring them, in fact. So, use ‘rake db:test:clone_structure‘ to setup your test database properly.

Comments are closed.