Saturday, December 23, 2006

Contextual Decoration of a Ruby object

One of my current work problems is that I have an Account class that needs optional decoration if it has been initialized in the context of an Import. I was kicking around an Observer or AspectR but the latter library wreaks of Java, and the former just isn't as seamless as I'd like. Also, I need to be mindful of multiple threads. Below is the solution I came up with, without the problem specific code.

The situation is actually more complicated than what I am modeling below.


class Import
attr_reader :start_at, :stop_at
class << self
def run!(&block)
Thread.new do
Thread.current['import_log'] = import_log = self.new
import_log.start!
yield block if block
import_log.stop!
end
end
end
[:start, :stop].eacha do |m|
define_method("#{m}!") do
instance_variable_set("@#{m}_at", Time.now)
end
end

def log_change(message)
... magical stuff happens here
end
end

class Account < ActiveRecord::Base
attr_reader :import_log
def initialize
@import_log = Thread.current['import_log']
super
end

def after_save
import_log.log_change("Saved Account with ID=#{self.id}") if import_log
end
end


What is actually happening is that I have numerous objects that use the "is_a_import_model" interface. If a class "is_a_import_model" then its initialize method needs to be overwritten to account for the import_log. There is also a StreamParser that is reading an XML document to process the lots of imports. If the StreamParser is created in the context of an Import.run!, it needs to log and rescue all exceptions, otherwise, it re-raises those exceptions.

No comments: