Merge commit 'nbibler/dynamic_initial_state'

This commit is contained in:
Travis Tilley 2009-05-27 20:05:43 -04:00
commit 15915ad6e7
5 changed files with 84 additions and 2 deletions

View File

@ -57,6 +57,38 @@ Here's a quick example highlighting some of the features.
end end
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 = Other Stuff
Author:: Scott Barron <scott at elitists dot net> Author:: Scott Barron <scott at elitists dot net>

View File

@ -89,7 +89,7 @@ module AASM
@aasm_current_state = aasm_read_state @aasm_current_state = aasm_read_state
end end
return @aasm_current_state if @aasm_current_state return @aasm_current_state if @aasm_current_state
self.class.aasm_initial_state aasm_determine_state_name(self.class.aasm_initial_state)
end end
def aasm_events_for_current_state def aasm_events_for_current_state
@ -118,6 +118,17 @@ module AASM
end end
@aasm_current_state = state @aasm_current_state = state
end 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) def aasm_state_object_for_state(name)
obj = self.class.aasm_states.find {|s| s == name} obj = self.class.aasm_states.find {|s| s == name}

View File

@ -230,7 +230,7 @@ module AASM
# This allows for nil aasm states - be sure to add validation to your model # This allows for nil aasm states - be sure to add validation to your model
def aasm_read_state def aasm_read_state
if new_record? 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 else
send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym
end end

View File

@ -41,6 +41,17 @@ end
class Baz < Bar class Baz < Bar
end 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 describe AASM, '- class level definitions' do
it 'should define a class level aasm_initial_state() method on its including class' 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 it 'should use the first state defined if no initial state is given' do
@bar.aasm_current_state.should == :read @bar.aasm_current_state.should == :read
end 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 end
describe AASM, '- event firing with persistence' do describe AASM, '- event firing with persistence' do

View File

@ -50,6 +50,14 @@ begin
class Beaver < June class Beaver < June
end 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 describe "aasm model", :shared => true do
it "should include AASM::Persistence::ActiveRecordPersistence" do it "should include AASM::Persistence::ActiveRecordPersistence" do
@ -219,6 +227,21 @@ begin
end end
end 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 # TODO: figure out how to test ActiveRecord reload! without a database