Skip to content

Commit

Permalink
support multiple guards per transition
Browse files Browse the repository at this point in the history
  • Loading branch information
alto committed Jan 24, 2014
1 parent cc19ab6 commit 39f85db
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@

## 3.1.0 (not yet released)

* support multiple guards per transition
* allow configuring behavior of nested transactions (see [issue #107](https://github.com/aasm/aasm/issues/107))
* validating the current state (see [issue #95](https://github.com/aasm/aasm/issues/95), thanks to [@ivantsepp](https://github.com/ivantsepp))

Expand Down
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -193,6 +193,13 @@ 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
event :sleep do
transitions :from => :running, :to => :sleeping, :guards => [:cleaning_needed?, :walked_the_dog?]
end
```

### ActiveRecord

Expand Down
16 changes: 10 additions & 6 deletions lib/aasm/transition.rb
Expand Up @@ -4,20 +4,24 @@ class Transition
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)
Expand Down
37 changes: 37 additions & 0 deletions spec/models/guardian.rb
@@ -0,0 +1,37 @@
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
end

def fail
false
end

def succeed
true
end

def another_succeed
true
end

end
30 changes: 30 additions & 0 deletions spec/unit/guard_spec.rb
@@ -0,0 +1,30 @@
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

0 comments on commit 39f85db

Please sign in to comment.