Interesting Things

Be careful when extending classes from structs. Their superclasses are essentially anonymous classes, so reopening them can be difficult. If you attempt to reopen them by extending them from “the same” struct, it will actually be a different anonymous class.

Ever had an STI model but wanted the views and controllers to pretend like it all extended the base class? You can have rails change the params[] namespace that it uses like so:

form_for :user, @admin_user, :url => user_path(@admin_user)

Or you can be super-cool and use polymorphic routes:

form_for @admin_user.becomes(User)

The becomes method is part of ActiveRecord, and it actually creates a 2nd copy of the object with the same attributes and a different class (shallow copy). Due to this implementation, it has limitations so use it carefully.

Upgrading to rails 2.3.3 breaks HopToad. This is related to filter_parameter_logging, and it’s technically rails’ fault. It has been fixed in 2.3.3 stable (which I assume will be released as 2.3.4). You can also fix it yourself with a one-line-patch. Personally, I’d wait to upgrade till 2.3.4 comes out.

Interesting Things

One of our projects is using mechanical turk to match and normalize their ugly data. After building some automatic matching with little success, the group is simply using the template web interface. The task can be trivially decomposed into tiny tasks, and accuracy does not need to be 100% perfect, so it’s a great fit. They only spent a few hours and have results already!

When creating associations to classes that use Single Table Inheritance (STI), rails is smarter about the hierarchy than I would have expected. A call to find on a base class yields a query that does not filter on type. But what about a call to find on a subclass? Instead of making a single filter on type, rails finds all the subclasses of that subclass and creates an “OR” expression in the “WHERE” clause.

Generated SQL respects class heirarchy

When it doesn’t work

Since it relies on the class hierarchy, the query is only accurate if every subclass has been loaded. If class preloading is off, for example, very weird things can happen. The query will depend non-deterministically on which subclasses have been loaded.

I just spent a wonderful weekend with 75 of the brightest folks I know in the Ruby community. My hat’s off to Obie and the Hashrocket crew for putting together a really great, intimate conference in a beautiful location. It’s refreshing to really have to struggle to choose which talk to attend from so many choices at each session. I know too many choices are a Bad Thing™, but the format made for great small sessions, and a wonderful thing happened: Everyone got to really meet and get to know everyone.

Among many others, I had the pleasure of meeting CJ Kihlbom, who nails a lot of why these conferences are so important in his post, The Business Value of Conferences.

It was really pleasant to present to a community of business leaders who understand the value of agile, and who are serious practitioners in their own practices.

These new Points Breakdown charts help you visualize the progress of your project as stories move through different stages of completion. Stories start out as “Unstarted”, then move on to “Started”, “Finished”, “Delivered”, and then “Accepted” (unless they get rejected). The different colored bars show the point totals of the stories that are in each state at the end of each day. As days pass, you would expect the number of unstarted to go down, and the number of accepted to go up. If any of the other groups are especially big, the chart may help you identify bottlenecks in your workflow.

This breakdown is available for both the current iteration and the previous one. You can also use it to visualize the development of your entire project for the last 15, 30, or 60 days.

To access the Points Breakdown charts, click the Reports link on top of the page, or navigate from your project via the Reports option in the View menu.

A few days ago I finally discovered why rake db:migrate:redo consistently angers me nearly as much as watching Paula Dean deep fry the vegetable kingdom. As any devoted connoisseur of the db rake tasks in Rails knows, db:migrate:redo always leaves your schema.rb file in the wrong state. The reason, as mentioned in our standup blog, is that rake will only invoke a given task once in a particular run.

To trivially test this try running a single task twice:

rake db:rollback db:rollback

You’ll find that your database only rolls back one migration. Now, you can set the STEP environment variable when calling db:rollback, but this is, as I said, a trivial example. It gets worse.

Take a look at the implementation of the db:migrate:redo task. The part we’re interested in looks like this:

namespace :migrate do
task :redo => :environment do
...
Rake::Task["db:rollback"].invoke
Rake::Task["db:migrate"].invoke
end
end

That looks fine; db:migrate:redo just verifies that your new migration will properly run down and up without blowing up. Sweet.

Both db:migrate and db:rollback dump the schema after they run, as they should. If you were to migrate or rollback your database and not dump the schema, then your schema would be in an invalid state. So, of course you can see where this is going, when you run db:migrate:redo the task performs the rollback, dumps the schema, performs the migrate, and then doesn’t dump the schema, because that task has already run. Boom, your schema is one migration behind, db:test:prepare loads the invalid schema into your test database, and all your tests fail (or, worse, pass inappropriately)

Now, I assumed this was a bug in Rake, and so I went on a little investigatory safari through the jungles of the Rake code to find it and kill it. I found the culprit, but invoking each task at most one time is, somewhat surprisingly, the expected behavior; it’s tested and everything. Now I can only wonder why. Why prevent invocation of a task more than once in a given rake run? The code contains unrelated guards against circular task dependencies, so that’s not it. Is this an example of overly-speculative defensive coding, or is there an actual use case for which this behavior is desirable? I’d like to hear from anyone who has written tasks that depend on this behavior, as well as anyone who (like me) considers this behavior unexpected and has run into problems because of it.

Assuming no one steps forward with a compelling reason that Rake should behave this way, I’d suggest that this be changed. I could see the value of it (perhaps as a performance optimization?) if rake tasks were guaranteed to not change the state of anything they operate on, or even were guaranteed to be idempotent; but neither is the case. This behavior severely limits the composability of tasks, since a task writer has to know which atomic tasks have run, and avoid any task that might try to run them again.

In the meantime, Rake provides a way to explicitly re-enable tasks that have run once, but it doesn’t seem to work. The db:schema:dump definition looks like this:

namespace :schema do
task :dump => :environment do
# Do dumpy stuff
Rake::Task["db:schema:dump"].reenable
end
end

That #reenable call is meant to tell the task “hey, task, you can run again.” I tried calling #reenable on the db:schema:dump task inside the db:migrate and db:rollback tasks as well, but without any luck.

At Pivotal, some of our client projects use plugins from our home-grown social networking platform and rely on Desert to tie them all together. To test this package of plugins we created a project that contains all of our Desert plugins and wrote some rake tasks that run all of their tests. Great, right?

Mostly. We want to ensure that our plugins have the absolute minimum dependencies to function. Let’s pretend we have an UserAuth plugin and a SocialPivots plugin, where UserAuth has no dependencies, but SocialPivots depends on UserAuth. We would like to test these the to plugins in isolation. But, with Desert doing it’s job so well, our UserAuth plugin could have a dependency on the SocialPivots plugins’ models or tables and we would never know it. Everything from SocialPivots is mixed-in and loaded into memory, and all of its migrations have executed, at the time we are running UserAuth’s tests.

What we need is a way to tell Desert to load only the plugin under test, plus its dependencies listed in init.rb. Hacking Desert and Rails to allow us to specify which plugins to load turned out to be pretty easy. Check it out (full gist here):

Once you can control which plugins are loaded you can expand this to dictate which routes and migrations should be run:

Routes:

# config/routes.rb
ActionController::Routing::Routes.draw do |map|
if ENV['PLUGIN']
Rails::Plugin.tracked_plugins.each do |plugin|
map.routes_from_plugin(plugin)
end
map.routes_from_plugin(ENV['PLUGIN'])
end
end

If you run that code, even though you’ve defined .nil? to be true, it will print “Access Approved”. Ruby if/unless/else only recognize nil and false to be “falsey”.

RubyMine rebase strangeness

When you rebase from the command line and get a merge error, then run the merge tool from RubyMine, the “Your changes” and “Their changes” will be the reverse of what you might think. If you are accustomed to clicking “accept theirs” to mean taking other people’s changes, you’ll be in for a surprise.

If you botch the git rebase, you can always go back with this awesome git command, brought to you by Peter Jaros:

Apparently, there are various situations that can cause the infamous “Mumble is not missing constant Post!” error. In this case it appears the associations do not understand the module scoping, despite the statement:

By default, associations will look for objects within the current module scope.

at http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

The solution was to explicitly declare the class name in the association:

“Rake Set Theory” when running the same thing in rake multiple times, Rake strips out the extra commands. For example:
$rake db:rollback db:rollback
which you might expect to preform two rollbacks, only does one rollback. This can be frustrating if you’re trying to couple rollbacks with a db:test:prepare or some other logical chain of events. At the command line you can of course work around most situations by && together multiple rake commands.