diff --git a/API b/API index a83fa38..b9acad1 100644 --- a/API +++ b/API @@ -7,3 +7,13 @@ Overwrite method to read the current state # retrieve the current state manually end end + +Overwrite method to write the current state + + class MyClass + include AASM + + def aasm_write_state + # store the current state manually + end + end diff --git a/lib/aasm/aasm.rb b/lib/aasm/aasm.rb index 1f6c013..b8d8d62 100644 --- a/lib/aasm/aasm.rb +++ b/lib/aasm/aasm.rb @@ -86,6 +86,11 @@ module AASM aasm.enter_initial_state end + # may be overwritten by persistence mixins + def aasm_write_state(new_state) + true + end + # deprecated def aasm_current_state # warn "#aasm_current_state is deprecated and will be removed in version 3.2.0; please use #aasm.state instead!" diff --git a/lib/aasm/instance_base.rb b/lib/aasm/instance_base.rb index 41f7339..e79352a 100644 --- a/lib/aasm/instance_base.rb +++ b/lib/aasm/instance_base.rb @@ -69,10 +69,7 @@ module AASM end def set_current_state_with_persistence(state) - save_success = true - if @instance.respond_to?(:aasm_write_state) || @instance.private_methods.include?('aasm_write_state') - save_success = @instance.aasm_write_state(state) - end + save_success = @instance.aasm_write_state(state) self.current_state = state if save_success save_success end diff --git a/lib/aasm/persistence/active_record_persistence.rb b/lib/aasm/persistence/active_record_persistence.rb index 544e17b..077f449 100644 --- a/lib/aasm/persistence/active_record_persistence.rb +++ b/lib/aasm/persistence/active_record_persistence.rb @@ -7,7 +7,6 @@ module AASM # * includes InstanceMethods # # Unless the corresponding methods are already defined, it includes - # * WriteState # * WriteStateWithoutPersistence # # Adds @@ -34,7 +33,6 @@ module AASM base.send(:include, AASM::Persistence::Base) base.extend AASM::Persistence::ActiveRecordPersistence::ClassMethods base.send(:include, AASM::Persistence::ActiveRecordPersistence::InstanceMethods) - base.send(:include, AASM::Persistence::ActiveRecordPersistence::WriteState) unless base.method_defined?(:aasm_write_state) base.send(:include, AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence) unless base.method_defined?(:aasm_write_state_without_persistence) if ActiveRecord::VERSION::MAJOR >= 3 @@ -74,54 +72,6 @@ module AASM module InstanceMethods - private - - # Ensures that if the aasm_state column is nil and the record is new - # that the initial state gets populated before validation on create - # - # foo = Foo.new - # foo.aasm_state # => nil - # foo.valid? - # foo.aasm_state # => "open" (where :open is the initial state) - # - # - # foo = Foo.find(:first) - # foo.aasm_state # => 1 - # foo.aasm_state = nil - # foo.valid? - # foo.aasm_state # => nil - # - def aasm_ensure_initial_state - aasm.enter_initial_state if send(self.class.aasm_column).blank? - end - - def aasm_fire_event(name, options, *args) - transaction do - super - end - end - - end - - module WriteStateWithoutPersistence - # Writes state to the state column, but does not persist it to the database - # - # foo = Foo.find(1) - # foo.aasm_current_state # => :opened - # foo.close - # foo.aasm_current_state # => :closed - # Foo.find(1).aasm_current_state # => :opened - # foo.save - # foo.aasm_current_state # => :closed - # Foo.find(1).aasm_current_state # => :closed - # - # NOTE: intended to be called from an event - def aasm_write_state_without_persistence(state) - write_attribute(self.class.aasm_column, state.to_s) - end - end - - module WriteState # Writes state to the state column and persists it to the database # # foo = Foo.find(1) @@ -147,6 +97,51 @@ module AASM true end + + private + + # Ensures that if the aasm_state column is nil and the record is new + # that the initial state gets populated before validation on create + # + # foo = Foo.new + # foo.aasm_state # => nil + # foo.valid? + # foo.aasm_state # => "open" (where :open is the initial state) + # + # + # foo = Foo.find(:first) + # foo.aasm_state # => 1 + # foo.aasm_state = nil + # foo.valid? + # foo.aasm_state # => nil + # + def aasm_ensure_initial_state + aasm.enter_initial_state if send(self.class.aasm_column).blank? + end + + def aasm_fire_event(name, options, *args) + transaction do + super + end + end + end # InstanceMethods + + module WriteStateWithoutPersistence + # Writes state to the state column, but does not persist it to the database + # + # foo = Foo.find(1) + # foo.aasm_current_state # => :opened + # foo.close + # foo.aasm_current_state # => :closed + # Foo.find(1).aasm_current_state # => :opened + # foo.save + # foo.aasm_current_state # => :closed + # Foo.find(1).aasm_current_state # => :closed + # + # NOTE: intended to be called from an event + def aasm_write_state_without_persistence(state) + write_attribute(self.class.aasm_column, state.to_s) + end end end diff --git a/lib/aasm/persistence/mongoid_persistence.rb b/lib/aasm/persistence/mongoid_persistence.rb index f4f10b0..a6ae33e 100644 --- a/lib/aasm/persistence/mongoid_persistence.rb +++ b/lib/aasm/persistence/mongoid_persistence.rb @@ -7,7 +7,6 @@ module AASM # * includes InstanceMethods # # Unless the corresponding methods are already defined, it includes - # * WriteState # * WriteStateWithoutPersistence # # Adds @@ -36,7 +35,6 @@ module AASM base.send(:include, AASM::Persistence::Base) base.extend AASM::Persistence::MongoidPersistence::ClassMethods base.send(:include, AASM::Persistence::MongoidPersistence::InstanceMethods) - base.send(:include, AASM::Persistence::MongoidPersistence::WriteState) unless base.method_defined?(:aasm_write_state) base.send(:include, AASM::Persistence::MongoidPersistence::WriteStateWithoutPersistence) unless base.method_defined?(:aasm_write_state_without_persistence) # Mongoid's Validatable gem dependency goes not have a before_validation_on_xxx hook yet. @@ -68,48 +66,6 @@ module AASM module InstanceMethods - private - - # Ensures that if the aasm_state column is nil and the record is new - # that the initial state gets populated before validation on create - # - # foo = Foo.new - # foo.aasm_state # => nil - # foo.valid? - # foo.aasm_state # => "open" (where :open is the initial state) - # - # - # foo = Foo.find(:first) - # foo.aasm_state # => 1 - # foo.aasm_state = nil - # foo.valid? - # foo.aasm_state # => nil - # - def aasm_ensure_initial_state - send("#{self.class.aasm_column}=", aasm.enter_initial_state.to_s) if send(self.class.aasm_column).blank? - end - - end - - module WriteStateWithoutPersistence - # Writes state to the state column, but does not persist it to the database - # - # foo = Foo.find(1) - # foo.aasm_current_state # => :opened - # foo.close - # foo.aasm_current_state # => :closed - # Foo.find(1).aasm_current_state # => :opened - # foo.save - # foo.aasm_current_state # => :closed - # Foo.find(1).aasm_current_state # => :closed - # - # NOTE: intended to be called from an event - def aasm_write_state_without_persistence(state) - write_attribute(self.class.aasm_column, state.to_s) - end - end - - module WriteState # Writes state to the state column and persists it to the database # using update_attribute (which bypasses validation) # @@ -131,6 +87,45 @@ module AASM true end + + private + + # Ensures that if the aasm_state column is nil and the record is new + # that the initial state gets populated before validation on create + # + # foo = Foo.new + # foo.aasm_state # => nil + # foo.valid? + # foo.aasm_state # => "open" (where :open is the initial state) + # + # + # foo = Foo.find(:first) + # foo.aasm_state # => 1 + # foo.aasm_state = nil + # foo.valid? + # foo.aasm_state # => nil + # + def aasm_ensure_initial_state + send("#{self.class.aasm_column}=", aasm.enter_initial_state.to_s) if send(self.class.aasm_column).blank? + end + end # InstanceMethods + + module WriteStateWithoutPersistence + # Writes state to the state column, but does not persist it to the database + # + # foo = Foo.find(1) + # foo.aasm_current_state # => :opened + # foo.close + # foo.aasm_current_state # => :closed + # Foo.find(1).aasm_current_state # => :opened + # foo.save + # foo.aasm_current_state # => :closed + # Foo.find(1).aasm_current_state # => :closed + # + # NOTE: intended to be called from an event + def aasm_write_state_without_persistence(state) + write_attribute(self.class.aasm_column, state.to_s) + end end module NamedScopeMethods diff --git a/spec/models/active_record/api.rb b/spec/models/active_record/api.rb index 7b4f39f..082e9ea 100644 --- a/spec/models/active_record/api.rb +++ b/spec/models/active_record/api.rb @@ -1,43 +1,67 @@ class DefaultState + attr_accessor :transient_store include AASM aasm do state :alpha, :initial => true state :beta state :gamma + event :release do + transitions :from => [:alpha, :beta, :gamma], :to => :beta + end end end class ProvidedState + attr_accessor :transient_store include AASM aasm do state :alpha, :initial => true state :beta state :gamma + event :release do + transitions :from => [:alpha, :beta, :gamma], :to => :beta + end end def aasm_read_state :beta end + + def aasm_write_state(new_state) + @transient_store = new_state + end end class PersistedState < ActiveRecord::Base + attr_accessor :transient_store include AASM aasm do state :alpha, :initial => true state :beta state :gamma + event :release do + transitions :from => [:alpha, :beta, :gamma], :to => :beta + end end end class ProvidedAndPersistedState < ActiveRecord::Base + attr_accessor :transient_store include AASM aasm do state :alpha, :initial => true state :beta state :gamma + event :release do + transitions :from => [:alpha, :beta, :gamma], :to => :beta + end end def aasm_read_state :gamma end + + def aasm_write_state(new_state) + @transient_store = new_state + end end diff --git a/spec/unit/api_spec.rb b/spec/unit/api_spec.rb index 7768203..55a08d9 100644 --- a/spec/unit/api_spec.rb +++ b/spec/unit/api_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'models/active_record/api.rb' -describe "retrieving the current state" do +describe "reading the current state" do it "uses the AASM default" do DefaultState.new.aasm.current_state.should eql :alpha end @@ -18,3 +18,29 @@ describe "retrieving the current state" do ProvidedAndPersistedState.new.aasm.current_state.should eql :gamma end end + +describe "writing the current state" do + it "uses the AASM default" do + o = DefaultState.new + o.release! + o.transient_store.should be_nil + end + + it "uses the provided method" do + o = ProvidedState.new + o.release! + o.transient_store.should eql :beta + end + + it "uses the persistence storage" do + o = PersistedState.new + o.release! + o.transient_store.should be_nil + end + + it "uses the provided method even if persisted" do + o = ProvidedAndPersistedState.new + o.release! + o.transient_store.should eql :beta + end +end diff --git a/spec/unit/persistence/active_record_persistence_spec.rb b/spec/unit/persistence/active_record_persistence_spec.rb index 2e2c7c6..9d73811 100644 --- a/spec/unit/persistence/active_record_persistence_spec.rb +++ b/spec/unit/persistence/active_record_persistence_spec.rb @@ -18,7 +18,6 @@ describe "class methods for classes without own read or write state" do let(:klass) {Gate} it_should_behave_like "aasm model" it "should include all persistence mixins" do - klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState) klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence) end end @@ -27,7 +26,6 @@ describe "class methods for classes with own write state" do let(:klass) {Writer} it_should_behave_like "aasm model" it "should include include all persistence mixins but write state" do - klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteState) klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence) end end @@ -36,7 +34,6 @@ describe "class methods for classes without persistence" do let(:klass) {Transient} it_should_behave_like "aasm model" it "should include all mixins but persistence" do - klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState) klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence) end end