read

Have you ever tried to get ActiveRecord Observers working in a Sinatra environment? I spent hours crawling trough the Rails sources but eventually gave up.

Plus: My goal anyway was to have some kind of global observer through all models in my application. So, easy decision - I build my one solution - as always: with "Blackjack and Hookers".

While browsing the ActiveRecord sources I found the perfect entrance to hook in to the needed callbacks (after_create, after_update and after_destroy).

Step 1: Hook in the callbacks

module ObjectWatcher                       
  def self.included(base)
    base.after_create do |obj|   
      puts "Yeah - we got a create"
    end
    base.after_update do |obj|  
      puts "Yeah - we got an update"
    end
    base.after_destroy do |obj|     
      puts "Yeah - we got a destroy"
    end
  end
end 
[...]
ActiveRecord::Base.send(:include, ObjectWatcher)

After that, the rest was pretty easy an straightforward: Create an Observer. Well: Someone has to receive above signals, and do something with them. If you only need one observer, you easily could do you stuff in the blocks. I'd like to be more flexible and for now, I have already 5 observers listening.

Step 2: Listener

this one gets called by the ObjectWatcher and dispatches to all its listeners (observers)

class ObjectListener
  class << self
    def listeners
      @listeners ||= []
    end
    def listeners=(l)
      @listeners = l
    end
    def run(obj, action)
      mn = "after_#{action}"
      self.listeners.each do |listener|
        lo = listener.to_s.classify.constantize
        lo.send mn, obj if lo.respond_to?(mn)
      end
    end 
  end 
end
Usage in ObjectWatcher:
[...]
  base.after_create do |obj|
ObjectListener.run obj, :create end [...]

Step 3: Create & Register Observers

class TestObserver
  def self.after_update(record)
    puts " >> updated"
  end   
  def self.after_create(record)
    puts " >> created" 
  end
  def self.after_destroy(record)
    puts " >> destroyed"
  end
end
[...]
ObjectListener.listeners << TestObserver    

that's it - happy coding.

( Update: 19.04.2012 - Fixed errors )

Back to Overview