Just A Summary

Piers Cawley Practices Punditry

A sketch of declarative ActiveRecord Migrations

Posted by Piers Cawley Fri, 24 Nov 2006 12:18:00 GMT

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
end

Implementation 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 -.

Then 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
  end
Of course the fun bit is the implementation of apply_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.

Comments

Leave a response

Comments



Just A Summary