Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-1 of 1 total  RSS 

State change observer for ActiveRecord in Rails

This is something to observe state changes as indicated by an attribute in an ActiveRecord Object in Rails. Normally, if you just need to observe an object and do things based on what state it's in, use the Observer provided in Rails. You can then use callbacks to do whatever you need to in the observer. However, if you want to do something upon a particular state transition, then you can try the stuff below:

Create a model like this. Here, the attribute mode keeps track of the state.
class CreateCarTransmission < ActiveRecord::Migration
  def self.up
    create_table :car_transmission do |t|
      t.column :engine_id, :integer, :null => false
      t.column :mode, :string, :null => false, :default => "park"
    end
  end

  def self.down
    drop_table :car_transmission
  end
end

In the model, you include the state transition observable class, and call state_observable.
class CarTransmission < ActiveRecord::Base
  include StateTransition::Observable
  state_observable CarTransmissionNotifier, :state_name => :mode
end

Then you define a notifier class that has different callbacks based on the transitions. It follows the pattern, "#{state_name}_from_#{state you're coming from}_to_#{state you're going to}"
class CarTransmissionNotifier < StateTransition::Observer
  def mode_from_drive_to_reverse(transmission)
    # send out mail and flash lights about how this is bad.
  end
end


So whenever you change the state from "drive" to "reverse" of a car transmission model in the controller, the notifier will fire up.

To use it, put the following code in a file called 'state_transition.rb', put it in your lib/ directory in rails.
require 'observer'

module StateTransition
  module Observable
    class StateNameNotFoundError < RuntimeError
      def message
        "option :state_name needs to be set to the name of an attribute"
      end
    end

    def self.included(mod)
      mod.extend(ClassMethods)
    end

    module ClassMethods

      def state_observable(observer_class, options)
        raise StateNameNotFoundError.new if options[:state_name].nil?
        state_name = options[:state_name].to_s
        
        include Object::Observable

        define_method(:after_initialize) do 
          add_observer(observer_class.new)
        end

        define_method("#{state_name}=") do |new_state|
          old_state = read_attribute(state_name)
          if old_state != new_state
            write_attribute(state_name, new_state) # TODO yield the update method
            changed
            notify_observers(self, state_name, old_state, new_state)
          end
        end
      end

    end
    
  end

  class Observer
    def update(observable, state_name, old_state, new_state)
      send("#{state_name}_from_#{old_state}_to_#{new_state}", observable)
    rescue NoMethodError => e
      # ignore any methods not found here
    end  
  end

end


more details here
« Newer Snippets
Older Snippets »
Showing 1-1 of 1 total  RSS