From 5f8be13c8cbcf1abace79831b9f987542ba1bb18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20B=C3=B6ttger?= <boettger@mt7.de> Date: Fri, 24 Jan 2014 12:02:22 +0100 Subject: [PATCH] support event guards #85 --- CHANGELOG.md | 5 +++-- README.md | 11 +++++++++++ lib/aasm/event.rb | 19 +++++++++++++++---- spec/models/guardian.rb | 31 +++++++++++++++++++++---------- spec/unit/guard_spec.rb | 30 ++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e834f4..be8626c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,10 @@ ## 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)) + * 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)) ## 3.0.26 diff --git a/README.md b/README.md index 8f708d0..2adc07a 100644 --- a/README.md +++ b/README.md @@ -196,11 +196,22 @@ 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 AASM comes with support for ActiveRecord and allows automatical persisting of the object's diff --git a/lib/aasm/event.rb b/lib/aasm/event.rb index 89aac8f..338c669 100644 --- a/lib/aasm/event.rb +++ b/lib/aasm/event.rb @@ -6,6 +6,7 @@ module AASM def initialize(name, options = {}, &block) @name = name @transitions = [] + @guards = Array(options[:guard] || options[:guards]) update(options, &block) end @@ -57,18 +58,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 diff --git a/spec/models/guardian.rb b/spec/models/guardian.rb index 3e254d4..2f90d0a 100644 --- a/spec/models/guardian.rb +++ b/spec/models/guardian.rb @@ -11,6 +11,7 @@ class Guardian 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 @@ -20,18 +21,28 @@ class Guardian 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 fail; false; end - def succeed - true - end - - def another_succeed - true - end + def succeed; true; end + def another_succeed; true; end + def more_succeed; true; end end diff --git a/spec/unit/guard_spec.rb b/spec/unit/guard_spec.rb index 085b842..1ab4ac1 100644 --- a/spec/unit/guard_spec.rb +++ b/spec/unit/guard_spec.rb @@ -28,3 +28,33 @@ describe "per-transition guards" do 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