<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: observer code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Sun, 18 May 2008 04:33:29 GMT</pubDate>
    <description>DZone Snippets: observer code</description>
    <item>
      <title>State change observer for ActiveRecord in Rails</title>
      <link>http://snippets.dzone.com/posts/show/4789</link>
      <description>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:&lt;br /&gt;&lt;br /&gt;Create a model like this.  Here, the attribute mode keeps track of the state.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class CreateCarTransmission &lt; ActiveRecord::Migration&lt;br /&gt;  def self.up&lt;br /&gt;    create_table :car_transmission do |t|&lt;br /&gt;      t.column :engine_id, :integer, :null =&gt; false&lt;br /&gt;      t.column :mode, :string, :null =&gt; false, :default =&gt; "park"&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def self.down&lt;br /&gt;    drop_table :car_transmission&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;In the model, you include the state transition observable class, and call state_observable.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class CarTransmission &lt; ActiveRecord::Base&lt;br /&gt;  include StateTransition::Observable&lt;br /&gt;  state_observable CarTransmissionNotifier, :state_name =&gt; :mode&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;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}"&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class CarTransmissionNotifier &lt; StateTransition::Observer&lt;br /&gt;  def mode_from_drive_to_reverse(transmission)&lt;br /&gt;    # send out mail and flash lights about how this is bad.&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;So whenever you change the state from "drive" to "reverse" of a car transmission model in the controller, the notifier will fire up.&lt;br /&gt;&lt;br /&gt;To use it, put the following code in a file called 'state_transition.rb', put it in your lib/ directory in rails.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;require 'observer'&lt;br /&gt;&lt;br /&gt;module StateTransition&lt;br /&gt;  module Observable&lt;br /&gt;    class StateNameNotFoundError &lt; RuntimeError&lt;br /&gt;      def message&lt;br /&gt;        "option :state_name needs to be set to the name of an attribute"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def self.included(mod)&lt;br /&gt;      mod.extend(ClassMethods)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    module ClassMethods&lt;br /&gt;&lt;br /&gt;      def state_observable(observer_class, options)&lt;br /&gt;        raise StateNameNotFoundError.new if options[:state_name].nil?&lt;br /&gt;        state_name = options[:state_name].to_s&lt;br /&gt;        &lt;br /&gt;        include Object::Observable&lt;br /&gt;&lt;br /&gt;        define_method(:after_initialize) do &lt;br /&gt;          add_observer(observer_class.new)&lt;br /&gt;        end&lt;br /&gt;&lt;br /&gt;        define_method("#{state_name}=") do |new_state|&lt;br /&gt;          old_state = read_attribute(state_name)&lt;br /&gt;          if old_state != new_state&lt;br /&gt;            write_attribute(state_name, new_state) # TODO yield the update method&lt;br /&gt;            changed&lt;br /&gt;            notify_observers(self, state_name, old_state, new_state)&lt;br /&gt;          end&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  class Observer&lt;br /&gt;    def update(observable, state_name, old_state, new_state)&lt;br /&gt;      send("#{state_name}_from_#{old_state}_to_#{new_state}", observable)&lt;br /&gt;    rescue NoMethodError =&gt; e&lt;br /&gt;      # ignore any methods not found here&lt;br /&gt;    end  &lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://webjazz.blogspot.com/2007/11/state-change-observer-for-activerecord.html"&gt;more details here&lt;/a&gt;</description>
      <pubDate>Fri, 16 Nov 2007 20:07:04 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4789</guid>
      <author>iamwil (wilhelm)</author>
    </item>
  </channel>
</rss>
