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)
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)
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
end
end
end
more details here