From f87565f2e4357481244e32863e5e73744b5a0056 Mon Sep 17 00:00:00 2001 From: Nathaniel Bibler Date: Thu, 26 Feb 2009 12:06:43 -0500 Subject: [PATCH] Added initial state Proc support. Added more complex example to README --- README.rdoc | 32 ++++++++++++++++++++++++++ lib/aasm.rb | 13 +++++++++- lib/persistence/active_record_persistence.rb | 2 +- spec/unit/aasm_spec.rb | 16 +++++++++++++ spec/unit/active_record_persistence_spec.rb | 23 ++++++++++++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/README.rdoc b/README.rdoc index 1bfaaa7..588b016 100644 --- a/README.rdoc +++ b/README.rdoc @@ -57,6 +57,38 @@ Here's a quick example highlighting some of the features. end end +== A Slightly More Complex Example + +This example uses a few of the more complex features available. + + class Relationship + include AASM + + aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating } + + aasm_state :dating, :enter => :make_happy, :exit => :make_depressed + aasm_state :intimate, :enter => :make_very_happy, :exit => :never_speak_again + aasm_state :married, :enter => :give_up_intimacy, :exit => :buy_exotic_car_and_wear_a_combover + + aasm_event :get_intimate do + transitions :to => :intimate, :from => [:dating], :guard => :drunk? + end + + aasm_event :get_married do + transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood? + end + + def strictly_for_fun?; end + def drunk?; end + def willing_to_give_up_manhood?; end + def make_happy; end + def make_depressed; end + def make_very_happy; end + def never_speak_again; end + def give_up_intimacy; end + def buy_exotic_car_and_wear_a_combover; end + end + = Other Stuff Author:: Scott Barron diff --git a/lib/aasm.rb b/lib/aasm.rb index fdee2ff..e4cebd4 100644 --- a/lib/aasm.rb +++ b/lib/aasm.rb @@ -89,7 +89,7 @@ module AASM @aasm_current_state = aasm_read_state end return @aasm_current_state if @aasm_current_state - self.class.aasm_initial_state + aasm_determine_state_name(self.class.aasm_initial_state) end def aasm_events_for_current_state @@ -118,6 +118,17 @@ module AASM end @aasm_current_state = state end + + def aasm_determine_state_name(state) + case state + when Symbol, String + state + when Proc + state.call(self) + else + raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc." + end + end def aasm_state_object_for_state(name) obj = self.class.aasm_states.find {|s| s == name} diff --git a/lib/persistence/active_record_persistence.rb b/lib/persistence/active_record_persistence.rb index 9ff1284..a628eb5 100644 --- a/lib/persistence/active_record_persistence.rb +++ b/lib/persistence/active_record_persistence.rb @@ -228,7 +228,7 @@ module AASM # This allows for nil aasm states - be sure to add validation to your model def aasm_read_state if new_record? - send(self.class.aasm_column).blank? ? self.class.aasm_initial_state : send(self.class.aasm_column).to_sym + send(self.class.aasm_column).blank? ? aasm_determine_state_name(self.class.aasm_initial_state) : send(self.class.aasm_column).to_sym else send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym end diff --git a/spec/unit/aasm_spec.rb b/spec/unit/aasm_spec.rb index 30cfe87..19fd05a 100644 --- a/spec/unit/aasm_spec.rb +++ b/spec/unit/aasm_spec.rb @@ -41,6 +41,17 @@ end class Baz < Bar end +class Banker + include AASM + aasm_initial_state Proc.new { |banker| banker.rich? ? :retired : :selling_bad_mortgages } + aasm_state :retired + aasm_state :selling_bad_mortgages + RICH = 1_000_000 + attr_accessor :balance + def initialize(balance = 0); self.balance = balance; end + def rich?; self.balance >= RICH; end +end + describe AASM, '- class level definitions' do it 'should define a class level aasm_initial_state() method on its including class' do @@ -130,6 +141,11 @@ describe AASM, '- initial states' do it 'should use the first state defined if no initial state is given' do @bar.aasm_current_state.should == :read end + + it 'should determine initial state from the Proc results' do + Banker.new(Banker::RICH - 1).aasm_current_state.should == :selling_bad_mortgages + Banker.new(Banker::RICH + 1).aasm_current_state.should == :retired + end end describe AASM, '- event firing with persistence' do diff --git a/spec/unit/active_record_persistence_spec.rb b/spec/unit/active_record_persistence_spec.rb index 0c1b0f4..fed2d48 100644 --- a/spec/unit/active_record_persistence_spec.rb +++ b/spec/unit/active_record_persistence_spec.rb @@ -50,6 +50,14 @@ begin class Beaver < June end + + class Thief < ActiveRecord::Base + include AASM + aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed } + aasm_state :rich + aasm_state :jailed + attr_accessor :skilled, :aasm_state + end describe "aasm model", :shared => true do it "should include AASM::Persistence::ActiveRecordPersistence" do @@ -219,6 +227,21 @@ begin end end end + + describe 'Thieves' do + before(:each) do + connection = mock(Connection, :columns => []) + Thief.stub!(:connection).and_return(connection) + end + + it 'should be rich if they\'re skilled' do + Thief.new(:skilled => true).aasm_current_state.should == :rich + end + + it 'should be jailed if they\'re unskilled' do + Thief.new(:skilled => false).aasm_current_state.should == :jailed + end + end # TODO: figure out how to test ActiveRecord reload! without a database -- 1.6.0.4