A sketch of declarative ActiveRecord Migrations
Writing migrations can get pretty tedious when you’re being scrupulous about writing both the up and the down side of the migration. Okay, so the Textmate ninjas amongst you can use scarily clever snippets to populate the down migration while you write the up method, but I can’t be the only Mac user who still prefers Emacs. And not everyone gets to run on Macs either.
So, inspired by something Jamis Buck wrote about designing a DSL, I’ve been sketching out a DSL for describing the easy parts of a migration in declarative style. None of this is implemented yet, but I’m pretty sure that it’s relatively simple to implement for a decent Ruby metaprogrammer. I’m brain dumping it here so I can come back to it later, or, you never know, someone might have implemented it by the time I revisit…
The sketch
class AdjustContentTable < ActiveRecord::Migration
class Content < ActiveRecord::Base
include ActiveRecord::Migration::Delta
delta do
+(:extended, :text)
-(:excerpt, :text)
+index :text_filter_id
end
end
end
callbacks_for(:up) do
before_any_changes
Content.before_changes { ... }
Content.before_additions {...}
Content.after_additions {...}
Content.before_removals {...}
Content.after_removals {...}
Content.after_changes {...}
after_any_changes
end
callbacks_for(:down) do
...
end
endImplementation Pointers
By making delta do its work in a block, it’s possible to instance_eval the block using a Delta object that has appropriate implementations of + and -.
callbacks_for[:up] puts its block into, say @callbacks[:up] and the block is yielded to to set up the callbacks by the default implementation of up. The default up probably looks something like:
def self.up
@callbacks[:up].call
apply_all_deltas
endapply_all_deltas – the per class apply_delta is reasonably simple:
def self.apply_delta(direction)
callback(:before_changes)
apply_additions(direction)
apply_removals(direction)
callback(:after_changes)
end
def self.apply_additions(direction)
if has_additions?(direction)
callback(:before_additions)
eval additions_for(direction).to_ruby
callback(:after_additions)
end
end
...Thinking about it, apply_all_deltas is going to take a certain amount of grovelling about in the namespace to work out what classes need to have their deltas applied, but even with just per class delta declarations, this should be a useful thing to implement.
Braindump ends
Sorry this doesn’t come with a handy dandy Rails plugin with it all implemented; your contributions in this area would be very gratefully received.
