1
0
Fork 0
mirror of https://github.com/aasm/aasm synced 2023-03-27 23:22:41 -04:00

Merge branch 'master' into aasm4

This commit is contained in:
Thorsten Böttger 2014-01-29 21:22:33 +01:00
commit 787650f4b3
24 changed files with 581 additions and 355 deletions

View file

@ -16,6 +16,10 @@
## 3.1.0 (not yet released)
* validating the current state (see [issue #95](https://github.com/aasm/aasm/issues/95), thanks to [@ivantsepp](https://github.com/ivantsepp))
* allow configuring behavior of nested transactions (see [issue #107](https://github.com/aasm/aasm/issues/107))
* support multiple guards per transition
* support event guards (see [issue #85](https://github.com/aasm/aasm/issues/85))
* support reading from- and to-state during on_transition callback (see [issue #100](https://github.com/aasm/aasm/issues/100))
## 3.0.26

View file

@ -1,6 +1,9 @@
# AASM - Ruby state machines
[![Gem Version](https://badge.fury.io/rb/aasm.png)](http://badge.fury.io/rb/aasm) [![Build Status](https://secure.travis-ci.org/aasm/aasm.png?branch=master)](http://travis-ci.org/aasm/aasm) [![Code Climate](https://codeclimate.com/github/aasm/aasm.png)](https://codeclimate.com/github/aasm/aasm) [![Coverage Status](https://coveralls.io/repos/aasm/aasm/badge.png?branch=master)](https://coveralls.io/r/aasm/aasm)
<a href="http://badge.fury.io/rb/aasm"><img src="https://badge.fury.io/rb/aasm@2x.png" alt="Gem Version" height="18"></a>
[![Build Status](https://secure.travis-ci.org/aasm/aasm.png?branch=master)](http://travis-ci.org/aasm/aasm)
[![Code Climate](https://codeclimate.com/github/aasm/aasm.png)](https://codeclimate.com/github/aasm/aasm)
[![Coverage Status](https://coveralls.io/repos/aasm/aasm/badge.png?branch=master)](https://coveralls.io/r/aasm/aasm)
This package contains AASM, a library for adding finite state machines to Ruby classes.
@ -149,6 +152,15 @@ In this case the `set_process` would be called with `:defagmentation` argument.
In case of an error during the event processing the error is rescued and passed to `:error`
callback, which can handle it or re-raise it for further propagation.
During the `:on_transition` callback (and reliably only then) you can access the
originating state (the from-state) and the target state (the to state), like this:
```ruby
def set_process(name)
logger.info "from #{aasm.from_state} to #{aasm.to_state}"
end
```
### Guards
Let's assume you want to allow particular transitions only if a defined condition is
@ -190,6 +202,24 @@ job.may_sleep? # => false
job.sleep # => raises AASM::InvalidTransition
```
You can even provide a number of guards, which all have to succeed to proceed
```ruby
def walked_the_dog?; ...; end
event :sleep do
transitions :from => :running, :to => :sleeping, :guards => [:cleaning_needed?, :walked_the_dog?]
end
```
If you want to provide guards for all transitions withing an event, you can use event guards
```ruby
event :sleep, :guards => [:walked_the_dog?] do
transitions :from => :running, :to => :sleeping, :guards => [:cleaning_needed?]
transitions :from => :cleaning, :to => :sleeping
end
```
### ActiveRecord
@ -339,6 +369,28 @@ class Job < ActiveRecord::Base
end
```
If you want to encapsulate state changes within an own transaction, the behavior
of this nested transaction might be confusing. Take a look at
[ActiveRecord Nested Transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html)
if you want to know more about this. Nevertheless, AASM by default requires a new transaction
`transaction(:requires_new => true)`. You can override this behavior by changing
the configuration
```ruby
class Job < ActiveRecord::Base
include AASM
aasm :requires_new_transaction => false do
...
end
...
end
```
which then leads to `transaction(:requires_new => false)`, the Rails default.
### Column name & migration
As a default AASM uses the column `aasm_state` to store the states. You can override

View file

@ -19,7 +19,7 @@ Gem::Specification.new do |s|
# s.add_development_dependency 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
s.add_development_dependency 'rake'
s.add_development_dependency 'sdoc'
s.add_development_dependency 'rspec', '~> 2.14'
s.add_development_dependency 'rspec', '>= 2.14'
s.add_development_dependency 'rr'
# s.add_development_dependency 'sqlite3'
s.add_development_dependency 'minitest'

View file

@ -5,24 +5,19 @@ module AASM
@clazz = clazz
@state_machine = AASM::StateMachine[@clazz]
@state_machine.config.column = options[:column].to_sym if options[:column]
@options = options
if options.key?(:whiny_transitions)
@state_machine.config.whiny_transitions = options[:whiny_transitions]
elsif @state_machine.config.whiny_transitions.nil?
@state_machine.config.whiny_transitions = true # this is the default, so let's cry
end
# let's cry if the transition is invalid
configure :whiny_transitions, true
if options.key?(:create_scopes)
@state_machine.config.create_scopes = options[:create_scopes]
elsif @state_machine.config.create_scopes.nil?
@state_machine.config.create_scopes = true # this is the default, so let's create scopes
end
# create named scopes for each state
configure :create_scopes, true
if options.key?(:skip_validation_on_save)
@state_machine.config.skip_validation_on_save = options[:skip_validation_on_save]
elsif @state_machine.config.skip_validation_on_save.nil?
@state_machine.config.skip_validation_on_save = false # this is the default, so don't store any new state if the model is invalid
end
# don't store any new state if the model is invalid
configure :skip_validation_on_save, false
# use requires_new for nested transactions
configure :requires_new_transaction, true
end
def initial_state(new_initial_state=nil)
@ -87,5 +82,16 @@ module AASM
end
end
private
def configure(key, default_value)
@state_machine.config.send(:new_ostruct_member, key)
if @options.key?(key)
@state_machine.config.send("#{key}=", @options[key])
elsif @state_machine.config.send(key).nil?
@state_machine.config.send("#{key}=", default_value)
end
end
end
end

View file

@ -6,6 +6,7 @@ module AASM
def initialize(name, options = {}, &block)
@name = name
@transitions = []
@guards = Array(options[:guard] || options[:guards])
update(options, &block)
end
@ -59,18 +60,28 @@ module AASM
## DSL interface
def transitions(definitions=nil)
if definitions # define new transitions
# Create a separate transition for each from state to the given state
# Create a separate transition for each from-state to the given state
Array(definitions[:from]).each do |s|
@transitions << AASM::Transition.new(definitions.merge({:from => s.to_sym}))
@transitions << AASM::Transition.new(attach_event_guards(definitions.merge(:from => s.to_sym)))
end
# Create a transition if :to is specified without :from (transitions from ANY state)
if @transitions.empty? && definitions[:to]
@transitions << AASM::Transition.new(attach_event_guards(definitions))
end
# Create a transition if to is specified without from (transitions from ANY state)
@transitions << AASM::Transition.new(definitions) if @transitions.empty? && definitions[:to]
end
@transitions
end
private
def attach_event_guards(definitions)
unless @guards.empty?
given_guards = Array(definitions.delete(:guard) || definitions.delete(:guards))
definitions[:guards] = given_guards + @guards
end
definitions
end
def update(options = {}, &block)
@options = options
if block then

View file

@ -1,6 +1,8 @@
module AASM
class InstanceBase
attr_accessor :from_state, :to_state
def initialize(instance)
@instance = instance
end

View file

@ -138,7 +138,7 @@ module AASM
end
def aasm_fire_event(name, options, *args, &block)
success = self.class.transaction(:requires_new => true) do
success = self.class.transaction(:requires_new => requires_new?) do
super
end
@ -150,6 +150,10 @@ module AASM
success
end
def requires_new?
AASM::StateMachine[self.class].config.requires_new_transaction
end
def aasm_validate_states
unless AASM::StateMachine[self.class].config.skip_validation_on_save
if aasm.current_state && !aasm.states.include?(aasm.current_state)

View file

@ -4,20 +4,24 @@ module AASM
alias_method :options, :opts
def initialize(opts)
@from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
@from = opts[:from]
@to = opts[:to]
@guards = Array(opts[:guard] || opts[:guards])
@on_transition = opts[:on_transition]
@opts = opts
end
# TODO: should be named allowed? or similar
def perform(obj, *args)
case @guard
@guards.each do |guard|
case guard
when Symbol, String
obj.send(@guard, *args)
return false unless obj.send(guard, *args)
when Proc
@guard.call(obj, *args)
else
true
return false unless guard.call(obj, *args)
end
end
true
end
def execute(obj, *args)
@ -37,6 +41,9 @@ module AASM
private
def _execute(obj, on_transition, *args)
obj.aasm.from_state = @from if obj.aasm.respond_to?(:from_state=)
obj.aasm.to_state = @to if obj.aasm.respond_to?(:to_state=)
case on_transition
when Proc
on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)

48
spec/models/guardian.rb Normal file
View file

@ -0,0 +1,48 @@
class Guardian
include AASM
aasm do
state :alpha, :initial => true
state :beta
event :use_one_guard_that_succeeds do
transitions :from => :alpha, :to => :beta, :guard => :succeed
end
event :use_one_guard_that_fails do
transitions :from => :alpha, :to => :beta, :guard => :fail
end
event :use_guards_that_succeed do
transitions :from => :alpha, :to => :beta, :guards => [:succeed, :another_succeed]
end
event :use_guards_where_the_first_fails do
transitions :from => :alpha, :to => :beta, :guards => [:succeed, :fail]
end
event :use_guards_where_the_second_fails do
transitions :from => :alpha, :to => :beta, :guards => [:fail, :succeed]
end
event :use_event_guards_that_succeed, :guards => [:succeed, :another_succeed] do
transitions :from => :alpha, :to => :beta
end
event :use_event_and_transition_guards_that_succeed, :guards => [:succeed, :another_succeed] do
transitions :from => :alpha, :to => :beta, :guards => [:more_succeed]
end
event :use_event_guards_where_the_first_fails, :guards => [:succeed, :fail] do
transitions :from => :alpha, :to => :beta
end
event :use_event_guards_where_the_second_fails, :guards => [:fail, :succeed] do
transitions :from => :alpha, :to => :beta, :guard => :another_succeed
end
event :use_event_and_transition_guards_where_third_fails, :guards => [:succeed, :another_succeed] do
transitions :from => :alpha, :to => :beta, :guards => [:fail]
end
end
def fail; false; end
def succeed; true; end
def another_succeed; true; end
def more_succeed; true; end
end

View file

@ -3,19 +3,19 @@ require 'models/active_record/api.rb'
describe "reading the current state" do
it "uses the AASM default" do
DefaultState.new.aasm.current_state.should eql :alpha
expect(DefaultState.new.aasm.current_state).to eql :alpha
end
it "uses the provided method" do
ProvidedState.new.aasm.current_state.should eql :beta
expect(ProvidedState.new.aasm.current_state).to eql :beta
end
it "uses the persistence storage" do
PersistedState.new.aasm.current_state.should eql :alpha
expect(PersistedState.new.aasm.current_state).to eql :alpha
end
it "uses the provided method even if persisted" do
ProvidedAndPersistedState.new.aasm.current_state.should eql :gamma
expect(ProvidedAndPersistedState.new.aasm.current_state).to eql :gamma
end
end
@ -23,25 +23,25 @@ describe "writing and persisting the current state" do
it "uses the AASM default" do
o = DefaultState.new
o.release!
o.persisted_store.should be_nil
expect(o.persisted_store).to be_nil
end
it "uses the provided method" do
o = ProvidedState.new
o.release!
o.persisted_store.should eql :beta
expect(o.persisted_store).to eql :beta
end
it "uses the persistence storage" do
o = PersistedState.new
o.release!
o.persisted_store.should be_nil
expect(o.persisted_store).to be_nil
end
it "uses the provided method even if persisted" do
o = ProvidedAndPersistedState.new
o.release!
o.persisted_store.should eql :beta
expect(o.persisted_store).to eql :beta
end
end
@ -49,24 +49,24 @@ describe "writing the current state without persisting it" do
it "uses the AASM default" do
o = DefaultState.new
o.release
o.transient_store.should be_nil
expect(o.transient_store).to be_nil
end
it "uses the provided method" do
o = ProvidedState.new
o.release
o.transient_store.should eql :beta
expect(o.transient_store).to eql :beta
end
it "uses the persistence storage" do
o = PersistedState.new
o.release
o.transient_store.should be_nil
expect(o.transient_store).to be_nil
end
it "uses the provided method even if persisted" do
o = ProvidedAndPersistedState.new
o.release
o.transient_store.should eql :beta
expect(o.transient_store).to eql :beta
end
end

View file

@ -4,15 +4,15 @@ describe 'callbacks for the new DSL' do
let(:callback) {CallbackNewDsl.new}
it "be called in order" do
callback.should_receive(:exit_open).once.ordered
callback.should_receive(:before).once.ordered
callback.should_receive(:before_exit_open).once.ordered # these should be before the state changes
callback.should_receive(:before_enter_closed).once.ordered
callback.should_receive(:enter_closed).once.ordered
callback.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
callback.should_receive(:after_exit_open).once.ordered # these should be after the state changes
callback.should_receive(:after_enter_closed).once.ordered
callback.should_receive(:after).once.ordered
expect(callback).to receive(:exit_open).once.ordered
expect(callback).to receive(:before).once.ordered
expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes
expect(callback).to receive(:before_enter_closed).once.ordered
expect(callback).to receive(:enter_closed).once.ordered
expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
expect(callback).to receive(:after_exit_open).once.ordered # these should be after the state changes
expect(callback).to receive(:after_enter_closed).once.ordered
expect(callback).to receive(:after).once.ordered
callback.close!
end
@ -67,20 +67,20 @@ describe 'event callbacks' do
it "should run error_callback if an exception is raised and error_callback defined" do
def @foo.error_callback(e); end
@foo.stub(:enter).and_raise(e=StandardError.new)
@foo.should_receive(:error_callback).with(e)
allow(@foo).to receive(:enter).and_raise(e=StandardError.new)
expect(@foo).to receive(:error_callback).with(e)
@foo.safe_close!
end
it "should raise NoMethodError if exceptionis raised and error_callback is declared but not defined" do
@foo.stub(:enter).and_raise(StandardError)
lambda{@foo.safe_close!}.should raise_error(NoMethodError)
allow(@foo).to receive(:enter).and_raise(StandardError)
expect{@foo.safe_close!}.to raise_error(NoMethodError)
end
it "should propagate an error if no error callback is declared" do
@foo.stub(:enter).and_raise("Cannot enter safe")
lambda{@foo.close!}.should raise_error(StandardError, "Cannot enter safe")
allow(@foo).to receive(:enter).and_raise("Cannot enter safe")
expect{@foo.close!}.to raise_error(StandardError, "Cannot enter safe")
end
end
@ -91,18 +91,18 @@ describe 'event callbacks' do
end
it 'should call it for successful bang fire' do
@foo.should_receive(:aasm_event_fired).with(:close, :open, :closed)
expect(@foo).to receive(:aasm_event_fired).with(:close, :open, :closed)
@foo.close!
end
it 'should call it for successful non-bang fire' do
@foo.should_receive(:aasm_event_fired)
expect(@foo).to receive(:aasm_event_fired)
@foo.close
end
it 'should not call it for failing bang fire' do
@foo.aasm.stub(:set_current_state_with_persistence).and_return(false)
@foo.should_not_receive(:aasm_event_fired)
allow(@foo.aasm).to receive(:set_current_state_with_persistence).and_return(false)
expect(@foo).not_to receive(:aasm_event_fired)
@foo.close!
end
end
@ -114,18 +114,18 @@ describe 'event callbacks' do
end
it 'should call it when transition failed for bang fire' do
@foo.should_receive(:aasm_event_failed).with(:null, :open)
lambda {@foo.null!}.should raise_error(AASM::InvalidTransition)
expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
expect {@foo.null!}.to raise_error(AASM::InvalidTransition)
end
it 'should call it when transition failed for non-bang fire' do
@foo.should_receive(:aasm_event_failed).with(:null, :open)
lambda {@foo.null}.should raise_error(AASM::InvalidTransition)
expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
expect {@foo.null}.to raise_error(AASM::InvalidTransition)
end
it 'should not call it if persist fails for bang fire' do
@foo.aasm.stub(:set_current_state_with_persistence).and_return(false)
@foo.should_receive(:aasm_event_failed)
allow(@foo.aasm).to receive(:set_current_state_with_persistence).and_return(false)
expect(@foo).to receive(:aasm_event_failed)
@foo.close!
end
end

View file

@ -4,12 +4,12 @@ describe 'on initialization' do
let(:auth) {AuthMachine.new}
it 'should be in the pending state' do
auth.aasm.current_state.should == :pending
expect(auth.aasm.current_state).to eq(:pending)
end
it 'should have an activation code' do
auth.has_activation_code?.should be_true
auth.activation_code.should_not be_nil
expect(auth.has_activation_code?).to be_true
expect(auth.activation_code).not_to be_nil
end
end
@ -19,26 +19,26 @@ describe 'when being unsuspended' do
it 'should be able to be unsuspended' do
auth.activate!
auth.suspend!
auth.may_unsuspend?.should be_true
expect(auth.may_unsuspend?).to be_true
end
it 'should not be able to be unsuspended into active' do
auth.suspend!
auth.may_unsuspend?(:active).should_not be_true
expect(auth.may_unsuspend?(:active)).not_to be_true
end
it 'should be able to be unsuspended into active if polite' do
auth.suspend!
auth.may_wait?(:waiting, :please).should be_true
expect(auth.may_wait?(:waiting, :please)).to be_true
auth.wait!(nil, :please)
end
it 'should not be able to be unsuspended into active if not polite' do
auth.suspend!
auth.may_wait?(:waiting).should_not be_true
auth.may_wait?(:waiting, :rude).should_not be_true
lambda {auth.wait!(nil, :rude)}.should raise_error(AASM::InvalidTransition)
lambda {auth.wait!}.should raise_error(AASM::InvalidTransition)
expect(auth.may_wait?(:waiting)).not_to be_true
expect(auth.may_wait?(:waiting, :rude)).not_to be_true
expect {auth.wait!(nil, :rude)}.to raise_error(AASM::InvalidTransition)
expect {auth.wait!}.to raise_error(AASM::InvalidTransition)
end
it 'should not be able to be unpassified' do
@ -46,8 +46,8 @@ describe 'when being unsuspended' do
auth.suspend!
auth.unsuspend!
auth.may_unpassify?.should_not be_true
lambda {auth.unpassify!}.should raise_error(AASM::InvalidTransition)
expect(auth.may_unpassify?).not_to be_true
expect {auth.unpassify!}.to raise_error(AASM::InvalidTransition)
end
it 'should be active if previously activated' do
@ -55,14 +55,14 @@ describe 'when being unsuspended' do
auth.suspend!
auth.unsuspend!
auth.aasm.current_state.should == :active
expect(auth.aasm.current_state).to eq(:active)
end
it 'should be pending if not previously activated, but an activation code is present' do
auth.suspend!
auth.unsuspend!
auth.aasm.current_state.should == :pending
expect(auth.aasm.current_state).to eq(:pending)
end
it 'should be passive if not previously activated and there is no activation code' do
@ -70,6 +70,6 @@ describe 'when being unsuspended' do
auth.suspend!
auth.unsuspend!
auth.aasm.current_state.should == :passive
expect(auth.aasm.current_state).to eq(:passive)
end
end

View file

@ -10,27 +10,27 @@ describe 'adding an event' do
end
it 'should set the name' do
event.name.should == :close_order
expect(event.name).to eq(:close_order)
end
it 'should set the success callback' do
event.options[:success].should == :success_callback
expect(event.options[:success]).to eq(:success_callback)
end
it 'should set the after callback' do
event.options[:after].should == [:after_callback]
expect(event.options[:after]).to eq([:after_callback])
end
it 'should set the before callback' do
event.options[:before].should == [:before_callback]
expect(event.options[:before]).to eq([:before_callback])
end
it 'should create transitions' do
transitions = event.transitions
transitions[0].from.should == :open
transitions[0].to.should == :closed
transitions[1].from.should == :received
transitions[1].to.should == :closed
expect(transitions[0].from).to eq(:open)
expect(transitions[0].to).to eq(:closed)
expect(transitions[1].from).to eq(:received)
expect(transitions[1].to).to eq(:closed)
end
end
@ -42,19 +42,19 @@ describe 'transition inspection' do
end
it 'should support inspecting transitions from other states' do
event.transitions_from_state(:sleeping).map(&:to).should == [:running]
event.transitions_from_state?(:sleeping).should be_true
expect(event.transitions_from_state(:sleeping).map(&:to)).to eq([:running])
expect(event.transitions_from_state?(:sleeping)).to be_true
event.transitions_from_state(:cleaning).map(&:to).should == []
event.transitions_from_state?(:cleaning).should be_false
expect(event.transitions_from_state(:cleaning).map(&:to)).to eq([])
expect(event.transitions_from_state?(:cleaning)).to be_false
end
it 'should support inspecting transitions to other states' do
event.transitions_to_state(:running).map(&:from).should == [:sleeping]
event.transitions_to_state?(:running).should be_true
expect(event.transitions_to_state(:running).map(&:from)).to eq([:sleeping])
expect(event.transitions_to_state?(:running)).to be_true
event.transitions_to_state(:cleaning).map(&:to).should == []
event.transitions_to_state?(:cleaning).should be_false
expect(event.transitions_to_state(:cleaning).map(&:to)).to eq([])
expect(event.transitions_to_state?(:cleaning)).to be_false
end
end
@ -63,7 +63,7 @@ describe 'firing an event' do
obj = double('object', :aasm => double('aasm', :current_state => 'open'))
event = AASM::Event.new(:event)
event.fire(obj).should be_nil
expect(event.fire(obj)).to be_nil
end
it 'should return the state of the first matching transition it finds' do
@ -73,7 +73,7 @@ describe 'firing an event' do
obj = double('object', :aasm => double('aasm', :current_state => :open))
event.fire(obj).should == :closed
expect(event.fire(obj)).to eq(:closed)
end
it 'should call the guard with the params passed in' do
@ -82,9 +82,9 @@ describe 'firing an event' do
end
obj = double('object', :aasm => double('aasm', :current_state => :open))
obj.should_receive(:guard_fn).with('arg1', 'arg2').and_return(true)
expect(obj).to receive(:guard_fn).with('arg1', 'arg2').and_return(true)
event.fire(obj, nil, 'arg1', 'arg2').should == :closed
expect(event.fire(obj, nil, 'arg1', 'arg2')).to eq(:closed)
end
end
@ -101,7 +101,7 @@ describe 'should fire callbacks' do
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:symbol_success_callback)
expect(model).to receive(:symbol_success_callback)
model.with_symbol!
end
@ -115,7 +115,7 @@ describe 'should fire callbacks' do
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:string_success_callback)
expect(model).to receive(:string_success_callback)
model.with_string!
end
@ -129,8 +129,8 @@ describe 'should fire callbacks' do
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:success_callback1)
model.should_receive(:success_callback2)
expect(model).to receive(:success_callback1)
expect(model).to receive(:success_callback2)
model.with_array!
end
@ -144,9 +144,9 @@ describe 'should fire callbacks' do
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:success_callback1)
model.should_receive(:success_callback2)
model.should_receive(:proc_success_callback)
expect(model).to receive(:success_callback1)
expect(model).to receive(:success_callback2)
expect(model).to receive(:proc_success_callback)
model.with_array_including_procs!
end
@ -160,7 +160,7 @@ describe 'should fire callbacks' do
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:proc_success_callback)
expect(model).to receive(:proc_success_callback)
model.with_proc!
end
end
@ -182,9 +182,9 @@ describe 'should fire callbacks' do
end
model = ThisNameBetterNotBeInUse.new
model.should_receive(:do_one_thing_after).once.ordered
model.should_receive(:do_another_thing_after_too).once.ordered
model.should_receive(:do_third_thing_at_last).once.ordered
expect(model).to receive(:do_one_thing_after).once.ordered
expect(model).to receive(:do_another_thing_after_too).once.ordered
expect(model).to receive(:do_third_thing_at_last).once.ordered
model.with_afters!
end
end
@ -203,7 +203,7 @@ describe 'should fire callbacks' do
end
model = ThisNameBetterNotBeInUse.new
model.should_receive(:do_something_before).once
expect(model).to receive(:do_something_before).once
model.before_as_proc!
end
end
@ -221,8 +221,8 @@ describe 'should fire callbacks' do
end
model = ThisNameBetterNotBeInUse.new
model.should_receive(:do_something_before).once.ordered
model.should_receive(:do_something_after).once.ordered
expect(model).to receive(:do_something_before).once.ordered
expect(model).to receive(:do_something_after).once.ordered
model.in_right_order!
end
end
@ -232,40 +232,40 @@ describe 'parametrised events' do
it 'should transition to specified next state (sleeping to showering)' do
pe.wakeup!(:showering)
pe.aasm.current_state.should == :showering
expect(pe.aasm.current_state).to eq(:showering)
end
it 'should transition to specified next state (sleeping to working)' do
pe.wakeup!(:working)
pe.aasm.current_state.should == :working
expect(pe.aasm.current_state).to eq(:working)
end
it 'should transition to default (first or showering) state' do
pe.wakeup!
pe.aasm.current_state.should == :showering
expect(pe.aasm.current_state).to eq(:showering)
end
it 'should transition to default state when on_transition invoked' do
pe.dress!(nil, 'purple', 'dressy')
pe.aasm.current_state.should == :working
expect(pe.aasm.current_state).to eq(:working)
end
it 'should call on_transition method with args' do
pe.wakeup!(:showering)
pe.should_receive(:wear_clothes).with('blue', 'jeans')
expect(pe).to receive(:wear_clothes).with('blue', 'jeans')
pe.dress!(:working, 'blue', 'jeans')
end
it 'should call on_transition proc' do
pe.wakeup!(:showering)
pe.should_receive(:wear_clothes).with('purple', 'slacks')
expect(pe).to receive(:wear_clothes).with('purple', 'slacks')
pe.dress!(:dating, 'purple', 'slacks')
end
it 'should call on_transition with an array of methods' do
pe.wakeup!(:showering)
pe.should_receive(:condition_hair)
pe.should_receive(:fix_hair)
expect(pe).to receive(:condition_hair)
expect(pe).to receive(:fix_hair)
pe.dress!(:prettying_up)
end
end
@ -274,9 +274,9 @@ describe 'event firing without persistence' do
it 'should attempt to persist if aasm_write_state is defined' do
foo = Foo.new
def foo.aasm_write_state; end
foo.should be_open
expect(foo).to be_open
foo.should_receive(:aasm_write_state_without_persistence)
expect(foo).to receive(:aasm_write_state_without_persistence)
foo.close
end
end

60
spec/unit/guard_spec.rb Normal file
View file

@ -0,0 +1,60 @@
require 'spec_helper'
describe "per-transition guards" do
let(:guardian) { Guardian.new }
it "allows the transition if the guard succeeds" do
expect { guardian.use_one_guard_that_succeeds! }.to_not raise_error
expect(guardian).to be_beta
end
it "stops the transition if the guard fails" do
expect { guardian.use_one_guard_that_fails! }.to raise_error(AASM::InvalidTransition)
expect(guardian).to be_alpha
end
it "allows the transition if all guards succeeds" do
expect { guardian.use_guards_that_succeed! }.to_not raise_error
expect(guardian).to be_beta
end
it "stops the transition if the first guard fails" do
expect { guardian.use_guards_where_the_first_fails! }.to raise_error(AASM::InvalidTransition)
expect(guardian).to be_alpha
end
it "stops the transition if the second guard fails" do
expect { guardian.use_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
expect(guardian).to be_alpha
end
end
describe "event guards" do
let(:guardian) { Guardian.new }
it "allows the transition if the event guards succeed" do
expect { guardian.use_event_guards_that_succeed! }.to_not raise_error
expect(guardian).to be_beta
end
it "allows the transition if the event and transition guards succeed" do
expect { guardian.use_event_and_transition_guards_that_succeed! }.to_not raise_error
expect(guardian).to be_beta
end
it "stops the transition if the first event guard fails" do
expect { guardian.use_event_guards_where_the_first_fails! }.to raise_error(AASM::InvalidTransition)
expect(guardian).to be_alpha
end
it "stops the transition if the second event guard fails" do
expect { guardian.use_event_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
expect(guardian).to be_alpha
end
it "stops the transition if the transition guard fails" do
expect { guardian.use_event_and_transition_guards_where_third_fails! }.to raise_error(AASM::InvalidTransition)
expect(guardian).to be_alpha
end
end

View file

@ -17,11 +17,11 @@ describe 'initial states' do
let(:bar) {Bar.new}
it 'should use the first state defined if no initial state is given' do
bar.aasm.current_state.should == :read
expect(bar.aasm.current_state).to eq(: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
expect(Banker.new(Banker::RICH - 1).aasm.current_state).to eq(:selling_bad_mortgages)
expect(Banker.new(Banker::RICH + 1).aasm.current_state).to eq(:retired)
end
end

View file

@ -2,16 +2,16 @@ require 'spec_helper'
describe 'inspection for common cases' do
it 'should support the new DSL' do
Foo.aasm.should respond_to(:states)
Foo.aasm.states.should include(:open)
Foo.aasm.states.should include(:closed)
expect(Foo.aasm).to respond_to(:states)
expect(Foo.aasm.states).to include(:open)
expect(Foo.aasm.states).to include(:closed)
Foo.aasm.should respond_to(:initial_state)
Foo.aasm.initial_state.should == :open
expect(Foo.aasm).to respond_to(:initial_state)
expect(Foo.aasm.initial_state).to eq(:open)
Foo.aasm.should respond_to(:events)
Foo.aasm.events.should include(:close)
Foo.aasm.events.should include(:null)
expect(Foo.aasm).to respond_to(:events)
expect(Foo.aasm.events).to include(:close)
expect(Foo.aasm.events).to include(:null)
end
context "instance level inspection" do
@ -20,77 +20,77 @@ describe 'inspection for common cases' do
it "delivers all states" do
states = foo.aasm.states
states.should include(:open)
states.should include(:closed)
expect(states).to include(:open)
expect(states).to include(:closed)
states = foo.aasm.states(:permissible => true)
states.should include(:closed)
states.should_not include(:open)
expect(states).to include(:closed)
expect(states).not_to include(:open)
foo.close
foo.aasm.states(:permissible => true).should be_empty
expect(foo.aasm.states(:permissible => true)).to be_empty
end
it "delivers all states for subclasses" do
states = two.aasm.states
states.should include(:open)
states.should include(:closed)
states.should include(:foo)
expect(states).to include(:open)
expect(states).to include(:closed)
expect(states).to include(:foo)
states = two.aasm.states(:permissible => true)
states.should include(:closed)
states.should_not include(:open)
expect(states).to include(:closed)
expect(states).not_to include(:open)
two.close
two.aasm.states(:permissible => true).should be_empty
expect(two.aasm.states(:permissible => true)).to be_empty
end
it "delivers all events" do
events = foo.aasm.events
events.should include(:close)
events.should include(:null)
expect(events).to include(:close)
expect(events).to include(:null)
foo.close
foo.aasm.events.should be_empty
expect(foo.aasm.events).to be_empty
end
end
it 'should list states in the order they have been defined' do
Conversation.aasm.states.should == [:needs_attention, :read, :closed, :awaiting_response, :junk]
expect(Conversation.aasm.states).to eq([:needs_attention, :read, :closed, :awaiting_response, :junk])
end
end
describe "special cases" do
it "should support valid a state name" do
Argument.aasm.states.should include(:invalid)
Argument.aasm.states.should include(:valid)
expect(Argument.aasm.states).to include(:invalid)
expect(Argument.aasm.states).to include(:valid)
argument = Argument.new
argument.invalid?.should be_true
argument.aasm.current_state.should == :invalid
expect(argument.invalid?).to be_true
expect(argument.aasm.current_state).to eq(:invalid)
argument.valid!
argument.valid?.should be_true
argument.aasm.current_state.should == :valid
expect(argument.valid?).to be_true
expect(argument.aasm.current_state).to eq(:valid)
end
end
describe 'aasm.states_for_select' do
it "should return a select friendly array of states" do
Foo.aasm.should respond_to(:states_for_select)
Foo.aasm.states_for_select.should == [['Open', 'open'], ['Closed', 'closed']]
expect(Foo.aasm).to respond_to(:states_for_select)
expect(Foo.aasm.states_for_select).to eq([['Open', 'open'], ['Closed', 'closed']])
end
end
describe 'aasm.from_states_for_state' do
it "should return all from states for a state" do
AuthMachine.aasm.should respond_to(:from_states_for_state)
expect(AuthMachine.aasm).to respond_to(:from_states_for_state)
froms = AuthMachine.aasm.from_states_for_state(:active)
[:pending, :passive, :suspended].each {|from| froms.should include(from)}
[:pending, :passive, :suspended].each {|from| expect(froms).to include(from)}
end
it "should return from states for a state for a particular transition only" do
froms = AuthMachine.aasm.from_states_for_state(:active, :transition => :unsuspend)
[:suspended].each {|from| froms.should include(from)}
[:suspended].each {|from| expect(froms).to include(from)}
end
end
@ -98,7 +98,7 @@ describe 'permissible events' do
let(:foo) {Foo.new}
it 'work' do
foo.aasm.permissible_events.should include(:close)
foo.aasm.permissible_events.should_not include(:null)
expect(foo.aasm.permissible_events).to include(:close)
expect(foo.aasm.permissible_events).not_to include(:null)
end
end

View file

@ -31,14 +31,14 @@ describe 'localized state names' do
it 'should localize' do
state = LocalizerTestModel.aasm.states.detect {|s| s == :opened}
state.localized_name.should == "It's open now!"
state.human_name.should == "It's open now!"
expect(state.localized_name).to eq("It's open now!")
expect(state.human_name).to eq("It's open now!")
end
it 'should use fallback' do
state = LocalizerTestModel.aasm.states.detect {|s| s == :closed}
state.localized_name.should == 'Closed'
state.human_name.should == 'Closed'
expect(state.localized_name).to eq('Closed')
expect(state.human_name).to eq('Closed')
end
end
@ -58,21 +58,21 @@ describe AASM::Localizer, "new style" do
context 'aasm.human_state' do
it 'should return translated state value' do
foo_opened.aasm.human_state.should == "It's open now!"
expect(foo_opened.aasm.human_state).to eq("It's open now!")
end
it 'should return humanized value if not localized' do
foo_closed.aasm.human_state.should == "Closed"
expect(foo_closed.aasm.human_state).to eq("Closed")
end
end
context 'aasm_human_event_name' do
it 'should return translated event name' do
LocalizerTestModel.aasm_human_event_name(:close).should == "Let's close it!"
expect(LocalizerTestModel.aasm_human_event_name(:close)).to eq("Let's close it!")
end
it 'should return humanized event name' do
LocalizerTestModel.aasm_human_event_name(:open).should == "Open"
expect(LocalizerTestModel.aasm_human_event_name(:open)).to eq("Open")
end
end
end
@ -93,21 +93,21 @@ describe AASM::Localizer, "deprecated style" do
context 'aasm.human_state' do
it 'should return translated state value' do
foo_opened.aasm.human_state.should == "It's open now!"
expect(foo_opened.aasm.human_state).to eq("It's open now!")
end
it 'should return humanized value if not localized' do
foo_closed.aasm.human_state.should == "Closed"
expect(foo_closed.aasm.human_state).to eq("Closed")
end
end
context 'aasm_human_event_name' do
it 'should return translated event name' do
LocalizerTestModel.aasm_human_event_name(:close).should == "Let's close it!"
expect(LocalizerTestModel.aasm_human_event_name(:close)).to eq("Let's close it!")
end
it 'should return humanized event name' do
LocalizerTestModel.aasm_human_event_name(:open).should == "Open"
expect(LocalizerTestModel.aasm_human_event_name(:open)).to eq("Open")
end
end
end

View file

@ -5,8 +5,8 @@ describe "the new dsl" do
let(:process) {ProcessWithNewDsl.new}
it 'should not conflict with other event or state methods' do
lambda {ProcessWithNewDsl.state}.should raise_error(RuntimeError, "wrong state method")
lambda {ProcessWithNewDsl.event}.should raise_error(RuntimeError, "wrong event method")
expect {ProcessWithNewDsl.state}.to raise_error(RuntimeError, "wrong state method")
expect {ProcessWithNewDsl.event}.to raise_error(RuntimeError, "wrong event method")
end
end

View file

@ -9,8 +9,8 @@ load_schema
shared_examples_for "aasm model" do
it "should include persistence mixins" do
klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
expect(klass.included_modules).to be_include(AASM::Persistence::ActiveRecordPersistence)
expect(klass.included_modules).to be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
end
end
@ -18,45 +18,45 @@ describe "instance methods" do
let(:gate) {Gate.new}
it "should respond to aasm persistence methods" do
gate.should respond_to(:aasm_read_state)
gate.should respond_to(:aasm_write_state)
gate.should respond_to(:aasm_write_state_without_persistence)
expect(gate).to respond_to(:aasm_read_state)
expect(gate).to respond_to(:aasm_write_state)
expect(gate).to respond_to(:aasm_write_state_without_persistence)
end
it "should return the initial state when new and the aasm field is nil" do
gate.aasm.current_state.should == :opened
expect(gate.aasm.current_state).to eq(:opened)
end
it "should return the aasm column when new and the aasm field is not nil" do
gate.aasm_state = "closed"
gate.aasm.current_state.should == :closed
expect(gate.aasm.current_state).to eq(:closed)
end
it "should return the aasm column when not new and the aasm_column is not nil" do
gate.stub(:new_record?).and_return(false)
allow(gate).to receive(:new_record?).and_return(false)
gate.aasm_state = "state"
gate.aasm.current_state.should == :state
expect(gate.aasm.current_state).to eq(:state)
end
it "should allow a nil state" do
gate.stub(:new_record?).and_return(false)
allow(gate).to receive(:new_record?).and_return(false)
gate.aasm_state = nil
gate.aasm.current_state.should be_nil
expect(gate.aasm.current_state).to be_nil
end
it "should call aasm_ensure_initial_state on validation before create" do
gate.should_receive(:aasm_ensure_initial_state).and_return(true)
expect(gate).to receive(:aasm_ensure_initial_state).and_return(true)
gate.valid?
end
it "should call aasm_ensure_initial_state before create, even if skipping validations" do
gate.should_receive(:aasm_ensure_initial_state).and_return(true)
expect(gate).to receive(:aasm_ensure_initial_state).and_return(true)
gate.save(:validate => false)
end
it "should not call aasm_ensure_initial_state on validation before update" do
gate.stub(:new_record?).and_return(false)
gate.should_not_receive(:aasm_ensure_initial_state)
allow(gate).to receive(:new_record?).and_return(false)
expect(gate).not_to receive(:aasm_ensure_initial_state)
gate.valid?
end
@ -64,36 +64,36 @@ end
describe 'subclasses' do
it "should have the same states as its parent class" do
DerivateNewDsl.aasm.states.should == SimpleNewDsl.aasm.states
expect(DerivateNewDsl.aasm.states).to eq(SimpleNewDsl.aasm.states)
end
it "should have the same events as its parent class" do
DerivateNewDsl.aasm.events.should == SimpleNewDsl.aasm.events
expect(DerivateNewDsl.aasm.events).to eq(SimpleNewDsl.aasm.events)
end
it "should have the same column as its parent even for the new dsl" do
SimpleNewDsl.aasm_column.should == :status
DerivateNewDsl.aasm_column.should == :status
expect(SimpleNewDsl.aasm_column).to eq(:status)
expect(DerivateNewDsl.aasm_column).to eq(:status)
end
end
describe "named scopes with the new DSL" do
context "Does not already respond_to? the scope name" do
it "should add a scope" do
SimpleNewDsl.should respond_to(:unknown_scope)
SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation).should be_true
expect(SimpleNewDsl).to respond_to(:unknown_scope)
expect(SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_true
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
SimpleNewDsl.should respond_to(:new)
SimpleNewDsl.new.class.should == SimpleNewDsl
expect(SimpleNewDsl).to respond_to(:new)
expect(SimpleNewDsl.new.class).to eq(SimpleNewDsl)
end
end
it "does not create scopes if requested" do
NoScope.should_not respond_to(:ignored_scope)
expect(NoScope).not_to respond_to(:ignored_scope)
end
end
@ -101,8 +101,8 @@ end
describe 'initial states' do
it 'should support conditions' do
Thief.new(:skilled => true).aasm.current_state.should == :rich
Thief.new(:skilled => false).aasm.current_state.should == :jailed
expect(Thief.new(:skilled => true).aasm.current_state).to eq(:rich)
expect(Thief.new(:skilled => false).aasm.current_state).to eq(:jailed)
end
end
@ -110,54 +110,54 @@ describe 'transitions with persistence' do
it "should work for valid models" do
valid_object = Validator.create(:name => 'name')
valid_object.should be_sleeping
expect(valid_object).to be_sleeping
valid_object.status = :running
valid_object.should be_running
expect(valid_object).to be_running
end
it 'should not store states for invalid models' do
validator = Validator.create(:name => 'name')
validator.should be_valid
validator.should be_sleeping
expect(validator).to be_valid
expect(validator).to be_sleeping
validator.name = nil
validator.should_not be_valid
validator.run!.should be_false
validator.should be_sleeping
expect(validator).not_to be_valid
expect(validator.run!).to be_false
expect(validator).to be_sleeping
validator.reload
validator.should_not be_running
validator.should be_sleeping
expect(validator).not_to be_running
expect(validator).to be_sleeping
validator.name = 'another name'
validator.should be_valid
validator.run!.should be_true
validator.should be_running
expect(validator).to be_valid
expect(validator.run!).to be_true
expect(validator).to be_running
validator.reload
validator.should be_running
validator.should_not be_sleeping
expect(validator).to be_running
expect(validator).not_to be_sleeping
end
it 'should store states for invalid models if configured' do
persistor = InvalidPersistor.create(:name => 'name')
persistor.should be_valid
persistor.should be_sleeping
expect(persistor).to be_valid
expect(persistor).to be_sleeping
persistor.name = nil
persistor.should_not be_valid
persistor.run!.should be_true
persistor.should be_running
expect(persistor).not_to be_valid
expect(persistor.run!).to be_true
expect(persistor).to be_running
persistor = InvalidPersistor.find(persistor.id)
persistor.valid?
persistor.should be_valid
persistor.should be_running
persistor.should_not be_sleeping
expect(persistor).to be_valid
expect(persistor).to be_running
expect(persistor).not_to be_sleeping
persistor.reload
persistor.should be_running
persistor.should_not be_sleeping
expect(persistor).to be_running
expect(persistor).not_to be_sleeping
end
describe 'transactions' do
@ -165,39 +165,56 @@ describe 'transitions with persistence' do
let(:transactor) { Transactor.create!(:name => 'transactor', :worker => worker) }
it 'should rollback all changes' do
transactor.should be_sleeping
worker.status.should == 'sleeping'
expect(transactor).to be_sleeping
expect(worker.status).to eq('sleeping')
lambda {transactor.run!}.should raise_error(StandardError, 'failed on purpose')
transactor.should be_running
worker.reload.status.should == 'sleeping'
expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
expect(transactor).to be_running
expect(worker.reload.status).to eq('sleeping')
end
it "should rollback all changes in nested transaction" do
transactor.should be_sleeping
worker.status.should == 'sleeping'
context "nested transactions" do
it "should rollback all changes in nested transaction" do
expect(transactor).to be_sleeping
expect(worker.status).to eq('sleeping')
Worker.transaction do
lambda { transactor.run! }.should raise_error(StandardError, 'failed on purpose')
Worker.transaction do
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
end
expect(transactor).to be_running
expect(worker.reload.status).to eq('sleeping')
end
transactor.should be_running
worker.reload.status.should == 'sleeping'
it "should only rollback changes in the main transaction not the nested one" do
# change configuration to not require new transaction
AASM::StateMachine[Transactor].config.requires_new_transaction = false
expect(transactor).to be_sleeping
expect(worker.status).to eq('sleeping')
Worker.transaction do
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
end
expect(transactor).to be_running
expect(worker.reload.status).to eq('running')
end
end
describe "after_commit callback" do
it "should fire :after_commit if transaction was successful" do
validator = Validator.create(:name => 'name')
validator.should be_sleeping
expect(validator).to be_sleeping
validator.run!
validator.should be_running
validator.name.should_not == "name"
expect(validator).to be_running
expect(validator.name).not_to eq("name")
end
it "should not fire :after_commit if transaction failed" do
validator = Validator.create(:name => 'name')
lambda { validator.fail! }.should raise_error(StandardError, 'failed on purpose')
validator.name.should == "name"
expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
expect(validator.name).to eq("name")
end
end
@ -209,20 +226,20 @@ describe "invalid states with persistence" do
it "should not store states" do
validator = Validator.create(:name => 'name')
validator.status = 'invalid_state'
validator.save.should be_false
lambda {validator.save!}.should raise_error(ActiveRecord::RecordInvalid)
expect(validator.save).to be_false
expect {validator.save!}.to raise_error(ActiveRecord::RecordInvalid)
validator.reload
validator.should be_sleeping
expect(validator).to be_sleeping
end
it "should store invalid states if configured" do
persistor = InvalidPersistor.create(:name => 'name')
persistor.status = 'invalid_state'
persistor.save.should be_true
expect(persistor.save).to be_true
persistor.reload
persistor.status.should == 'invalid_state'
expect(persistor.status).to eq('invalid_state')
end
end

View file

@ -27,15 +27,15 @@ describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version
context "Does not already respond_to? the scope name" do
it "should add a scope" do
SimpleMongoid.should respond_to(:unknown_scope)
SimpleMongoid.unknown_scope.class.should == Mongoid::Criteria
expect(SimpleMongoid).to respond_to(:unknown_scope)
expect(SimpleMongoid.unknown_scope.class).to eq(Mongoid::Criteria)
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
SimpleMongoid.should respond_to(:new)
SimpleMongoid.new.class.should == SimpleMongoid
expect(SimpleMongoid).to respond_to(:new)
expect(SimpleMongoid.new.class).to eq(SimpleMongoid)
end
end
@ -45,20 +45,20 @@ describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version
context "Does not already respond_to? the scope name" do
it "should add a scope" do
SimpleNewDslMongoid.should respond_to(:unknown_scope)
SimpleNewDslMongoid.unknown_scope.class.should == Mongoid::Criteria
expect(SimpleNewDslMongoid).to respond_to(:unknown_scope)
expect(SimpleNewDslMongoid.unknown_scope.class).to eq(Mongoid::Criteria)
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
SimpleNewDslMongoid.should respond_to(:new)
SimpleNewDslMongoid.new.class.should == SimpleNewDslMongoid
expect(SimpleNewDslMongoid).to respond_to(:new)
expect(SimpleNewDslMongoid.new.class).to eq(SimpleNewDslMongoid)
end
end
it "does not create scopes if requested" do
NoScopeMongoid.should_not respond_to(:ignored_scope)
expect(NoScopeMongoid).not_to respond_to(:ignored_scope)
end
end
@ -69,12 +69,12 @@ describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version
let!(:model_id) { model._id }
it "should respond to method" do
SimpleNewDslMongoid.should respond_to(:find_in_state)
expect(SimpleNewDslMongoid).to respond_to(:find_in_state)
end
it "should find the model when given the correct scope and model id" do
SimpleNewDslMongoid.find_in_state(model_id, 'unknown_scope').class.should == SimpleNewDslMongoid
SimpleNewDslMongoid.find_in_state(model_id, 'unknown_scope').should == model
expect(SimpleNewDslMongoid.find_in_state(model_id, 'unknown_scope').class).to eq(SimpleNewDslMongoid)
expect(SimpleNewDslMongoid.find_in_state(model_id, 'unknown_scope')).to eq(model)
end
it "should raise DocumentNotFound error when given incorrect scope" do
@ -94,17 +94,17 @@ describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version
end
it "should respond to method" do
SimpleNewDslMongoid.should respond_to(:count_in_state)
expect(SimpleNewDslMongoid).to respond_to(:count_in_state)
end
it "should return n for a scope with n records persisted" do
SimpleNewDslMongoid.count_in_state('unknown_scope').class.should == Fixnum
SimpleNewDslMongoid.count_in_state('unknown_scope').should == 3
expect(SimpleNewDslMongoid.count_in_state('unknown_scope').class).to eq(Fixnum)
expect(SimpleNewDslMongoid.count_in_state('unknown_scope')).to eq(3)
end
it "should return zero for a scope without records persisted" do
SimpleNewDslMongoid.count_in_state('new').class.should == Fixnum
SimpleNewDslMongoid.count_in_state('new').should == 0
expect(SimpleNewDslMongoid.count_in_state('new').class).to eq(Fixnum)
expect(SimpleNewDslMongoid.count_in_state('new')).to eq(0)
end
end
@ -117,16 +117,16 @@ describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version
end
it "should respond to method" do
SimpleNewDslMongoid.should respond_to(:with_state_scope)
expect(SimpleNewDslMongoid).to respond_to(:with_state_scope)
end
it "should correctly process block" do
SimpleNewDslMongoid.with_state_scope('unknown_scope') do
expect(SimpleNewDslMongoid.with_state_scope('unknown_scope') do
SimpleNewDslMongoid.count
end.should == 3
SimpleNewDslMongoid.with_state_scope('new') do
end).to eq(3)
expect(SimpleNewDslMongoid.with_state_scope('new') do
SimpleNewDslMongoid.count
end.should == 2
end).to eq(2)
end
end
@ -136,12 +136,12 @@ describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version
let(:simple) {SimpleNewDslMongoid.new}
it "should call aasm_ensure_initial_state on validation before create" do
simple.should_receive(:aasm_ensure_initial_state).and_return(true)
expect(simple).to receive(:aasm_ensure_initial_state).and_return(true)
simple.valid?
end
it "should call aasm_ensure_initial_state before create, even if skipping validations" do
simple.should_receive(:aasm_ensure_initial_state).and_return(true)
expect(simple).to receive(:aasm_ensure_initial_state).and_return(true)
simple.save(:validate => false)
end
end

View file

@ -20,39 +20,39 @@ describe 'state machine' do
let(:payment) {Payment.new}
it 'starts with an initial state' do
payment.aasm.current_state.should == :initialised
payment.should respond_to(:initialised?)
payment.should be_initialised
expect(payment.aasm.current_state).to eq(:initialised)
expect(payment).to respond_to(:initialised?)
expect(payment).to be_initialised
end
it 'allows transitions to other states' do
payment.should respond_to(:fill_out)
payment.should respond_to(:fill_out!)
expect(payment).to respond_to(:fill_out)
expect(payment).to respond_to(:fill_out!)
payment.fill_out!
payment.should respond_to(:filled_out?)
payment.should be_filled_out
expect(payment).to respond_to(:filled_out?)
expect(payment).to be_filled_out
payment.should respond_to(:authorise)
payment.should respond_to(:authorise!)
expect(payment).to respond_to(:authorise)
expect(payment).to respond_to(:authorise!)
payment.authorise
payment.should respond_to(:authorised?)
payment.should be_authorised
expect(payment).to respond_to(:authorised?)
expect(payment).to be_authorised
end
it 'denies transitions to other states' do
lambda {payment.authorise}.should raise_error(AASM::InvalidTransition)
lambda {payment.authorise!}.should raise_error(AASM::InvalidTransition)
expect {payment.authorise}.to raise_error(AASM::InvalidTransition)
expect {payment.authorise!}.to raise_error(AASM::InvalidTransition)
payment.fill_out
lambda {payment.fill_out}.should raise_error(AASM::InvalidTransition)
lambda {payment.fill_out!}.should raise_error(AASM::InvalidTransition)
expect {payment.fill_out}.to raise_error(AASM::InvalidTransition)
expect {payment.fill_out!}.to raise_error(AASM::InvalidTransition)
payment.authorise
lambda {payment.fill_out}.should raise_error(AASM::InvalidTransition)
lambda {payment.fill_out!}.should raise_error(AASM::InvalidTransition)
expect {payment.fill_out}.to raise_error(AASM::InvalidTransition)
expect {payment.fill_out!}.to raise_error(AASM::InvalidTransition)
end
it 'defines constants for each state name' do
Payment::STATE_INITIALISED.should eq(:initialised)
Payment::STATE_FILLED_OUT.should eq(:filled_out)
Payment::STATE_AUTHORISED.should eq(:authorised)
expect(Payment::STATE_INITIALISED).to eq(:initialised)
expect(Payment::STATE_FILLED_OUT).to eq(:filled_out)
expect(Payment::STATE_AUTHORISED).to eq(:authorised)
end
end

View file

@ -12,34 +12,34 @@ describe AASM::State do
it 'should set the name' do
state = new_state
state.name.should == :astate
expect(state.name).to eq(:astate)
end
it 'should set the display_name from name' do
new_state.display_name.should == 'Astate'
expect(new_state.display_name).to eq('Astate')
end
it 'should set the display_name from options' do
new_state(:display => "A State").display_name.should == 'A State'
expect(new_state(:display => "A State").display_name).to eq('A State')
end
it 'should set the options and expose them as options' do
new_state.options.should == @options
expect(new_state.options).to eq(@options)
end
it 'should be equal to a symbol of the same name' do
new_state.should == :astate
expect(new_state).to eq(:astate)
end
it 'should be equal to a State of the same name' do
new_state.should == new_state
expect(new_state).to eq(new_state)
end
it 'should send a message to the record for an action if the action is present as a symbol' do
state = new_state(:entering => :foo)
record = double('record')
record.should_receive(:foo)
expect(record).to receive(:foo)
state.fire_callbacks(:entering, record)
end
@ -48,7 +48,7 @@ describe AASM::State do
state = new_state(:entering => 'foo')
record = double('record')
record.should_receive(:foo)
expect(record).to receive(:foo)
state.fire_callbacks(:entering, record)
end
@ -57,10 +57,10 @@ describe AASM::State do
state = new_state(:entering => [:a, :b, "c", lambda {|r| r.foobar }])
record = double('record')
record.should_receive(:a)
record.should_receive(:b)
record.should_receive(:c)
record.should_receive(:foobar)
expect(record).to receive(:a)
expect(record).to receive(:b)
expect(record).to receive(:c)
expect(record).to receive(:foobar)
state.fire_callbacks(:entering, record)
end
@ -69,9 +69,9 @@ describe AASM::State do
state = new_state(:entering => [:a, :b, :c])
record = double('record')
record.should_receive(:a)
record.should_receive(:b).and_throw(:halt_aasm_chain)
record.should_not_receive(:c)
expect(record).to receive(:a)
expect(record).to receive(:b).and_throw(:halt_aasm_chain)
expect(record).not_to receive(:c)
state.fire_callbacks(:entering, record)
end
@ -80,7 +80,7 @@ describe AASM::State do
state = new_state(:entering => Proc.new {|r| r.foobar})
record = double('record')
record.should_receive(:foobar)
expect(record).to receive(:foobar)
state.fire_callbacks(:entering, record)
end

View file

@ -5,26 +5,26 @@ describe 'subclassing' do
it 'should have the parent states' do
Foo.aasm.states.each do |state|
FooTwo.aasm.states.should include(state)
expect(FooTwo.aasm.states).to include(state)
end
Baz.aasm.states.should == Bar.aasm.states
expect(Baz.aasm.states).to eq(Bar.aasm.states)
end
it 'should not add the child states to the parent machine' do
Foo.aasm.states.should_not include(:foo)
expect(Foo.aasm.states).not_to include(:foo)
end
it "should have the same events as its parent" do
Baz.aasm.events.should == Bar.aasm.events
expect(Baz.aasm.events).to eq(Bar.aasm.events)
end
it 'should know how to respond to `may_add_details?`' do
son.may_add_details?.should be_true
expect(son.may_add_details?).to be_true
end
it 'should not break if I call Son#update_state' do
son.update_state
son.aasm.current_state.should == :pending_details_confirmation
expect(son.aasm.current_state).to eq(:pending_details_confirmation)
end
end

View file

@ -4,46 +4,46 @@ describe 'transitions' do
it 'should raise an exception when whiny' do
process = ProcessWithNewDsl.new
lambda { process.stop! }.should raise_error(AASM::InvalidTransition)
process.should be_sleeping
expect { process.stop! }.to raise_error(AASM::InvalidTransition)
expect(process).to be_sleeping
end
it 'should not raise an exception when not whiny' do
silencer = Silencer.new
silencer.smile!.should be_false
silencer.should be_silent
expect(silencer.smile!).to be_false
expect(silencer).to be_silent
end
it 'should not raise an exception when superclass not whiny' do
sub = SubClassing.new
sub.smile!.should be_false
sub.should be_silent
expect(sub.smile!).to be_false
expect(sub).to be_silent
end
it 'should not raise an exception when from is nil even if whiny' do
silencer = Silencer.new
silencer.smile_any!.should be_true
silencer.should be_smiling
expect(silencer.smile_any!).to be_true
expect(silencer).to be_smiling
end
it 'should call the block when success' do
silencer = Silencer.new
success = false
lambda {
expect {
silencer.smile_any! do
success = true
end
}.should change { success }.to(true)
}.to change { success }.to(true)
end
it 'should not call the block when failure' do
silencer = Silencer.new
success = false
lambda {
expect {
silencer.smile! do
success = true
end
}.should_not change { success }.to(true)
}.not_to change { success }.to(true)
end
end
@ -56,9 +56,9 @@ describe AASM::Transition do
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
st = AASM::Transition.new(opts)
st.from.should == opts[:from]
st.to.should == opts[:to]
st.opts.should == opts
expect(st.from).to eq(opts[:from])
expect(st.to).to eq(opts[:to])
expect(st.opts).to eq(opts)
end
it 'should pass equality check if from and to are the same' do
@ -66,10 +66,10 @@ describe AASM::Transition do
st = AASM::Transition.new(opts)
obj = double('object')
obj.stub(:from).and_return(opts[:from])
obj.stub(:to).and_return(opts[:to])
allow(obj).to receive(:from).and_return(opts[:from])
allow(obj).to receive(:to).and_return(opts[:to])
st.should == obj
expect(st).to eq(obj)
end
it 'should fail equality check if from are not the same' do
@ -77,10 +77,10 @@ describe AASM::Transition do
st = AASM::Transition.new(opts)
obj = double('object')
obj.stub(:from).and_return('blah')
obj.stub(:to).and_return(opts[:to])
allow(obj).to receive(:from).and_return('blah')
allow(obj).to receive(:to).and_return(opts[:to])
st.should_not == obj
expect(st).not_to eq(obj)
end
it 'should fail equality check if to are not the same' do
@ -88,10 +88,10 @@ describe AASM::Transition do
st = AASM::Transition.new(opts)
obj = double('object')
obj.stub(:from).and_return(opts[:from])
obj.stub(:to).and_return('blah')
allow(obj).to receive(:from).and_return(opts[:from])
allow(obj).to receive(:to).and_return('blah')
st.should_not == obj
expect(st).not_to eq(obj)
end
end
@ -100,7 +100,7 @@ describe AASM::Transition, '- when performing guard checks' do
opts = {:from => 'foo', :to => 'bar'}
st = AASM::Transition.new(opts)
st.perform(nil).should be_true
expect(st.perform(nil)).to be_true
end
it 'should call the method on the object if guard is a symbol' do
@ -108,7 +108,7 @@ describe AASM::Transition, '- when performing guard checks' do
st = AASM::Transition.new(opts)
obj = double('object')
obj.should_receive(:test)
expect(obj).to receive(:test)
st.perform(obj)
end
@ -118,7 +118,7 @@ describe AASM::Transition, '- when performing guard checks' do
st = AASM::Transition.new(opts)
obj = double('object')
obj.should_receive(:test)
expect(obj).to receive(:test)
st.perform(obj)
end
@ -128,7 +128,7 @@ describe AASM::Transition, '- when performing guard checks' do
st = AASM::Transition.new(opts)
obj = double('object')
obj.should_receive(:test)
expect(obj).to receive(:test)
st.perform(obj)
end
@ -139,9 +139,9 @@ describe AASM::Transition, '- when executing the transition with a Proc' do
opts = {:from => 'foo', :to => 'bar', :on_transition => Proc.new {|o| o.test}}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = double('object')
obj = double('object', :aasm => 'aasm')
opts[:on_transition].should_receive(:call).with(any_args)
expect(opts[:on_transition]).to receive(:call).with(any_args)
st.execute(obj, args)
end
@ -150,9 +150,9 @@ describe AASM::Transition, '- when executing the transition with a Proc' do
opts = {:from => 'foo', :to => 'bar', :on_transition => Proc.new {||}}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = double('object')
obj = double('object', :aasm => 'aasm')
opts[:on_transition].should_receive(:call).with(no_args)
expect(opts[:on_transition]).to receive(:call).with(no_args)
st.execute(obj, args)
end
@ -163,9 +163,9 @@ describe AASM::Transition, '- when executing the transition with an :on_transtio
opts = {:from => 'foo', :to => 'bar', :on_transition => 'test'}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = double('object')
obj = double('object', :aasm => 'aasm')
obj.should_receive(:test)
expect(obj).to receive(:test)
st.execute(obj, args)
end
@ -174,9 +174,9 @@ describe AASM::Transition, '- when executing the transition with an :on_transtio
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = double('object')
obj = double('object', :aasm => 'aasm')
obj.should_receive(:test)
expect(obj).to receive(:test)
st.execute(obj, args)
end
@ -185,7 +185,7 @@ describe AASM::Transition, '- when executing the transition with an :on_transtio
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = double('object')
obj = double('object', :aasm => 'aasm')
def obj.test(args)
"arg1: #{args[:arg1]} arg2: #{args[:arg2]}"
@ -193,14 +193,14 @@ describe AASM::Transition, '- when executing the transition with an :on_transtio
return_value = st.execute(obj, args)
return_value.should == 'arg1: 1 arg2: 2'
expect(return_value).to eq('arg1: 1 arg2: 2')
end
it 'should NOT pass args if the target method does NOT accept them' do
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = double('object')
obj = double('object', :aasm => 'aasm')
def obj.test
'success'
@ -208,7 +208,22 @@ describe AASM::Transition, '- when executing the transition with an :on_transtio
return_value = st.execute(obj, args)
return_value.should == 'success'
expect(return_value).to eq('success')
end
it 'should allow accessing the from_state and the to_state' do
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = double('object', :aasm => AASM::InstanceBase.new('object'))
def obj.test(args)
"from: #{aasm.from_state} to: #{aasm.to_state}"
end
return_value = st.execute(obj, args)
expect(return_value).to eq('from: foo to: bar')
end
end