Skip to content

Commit

Permalink
Merge PR gh-227 allow event arguments to be taken into account when s…
Browse files Browse the repository at this point in the history
…electing event; fix tests
  • Loading branch information
geekq committed May 16, 2024
2 parents 9cb38d2 + d53bd7e commit b118f24
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 46 deletions.
8 changes: 7 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,8 @@ When calling a `device.can_<fire_event>?` check, or attempting a `device.<event>
* If an `:if` check is present, proceed if it evaluates to true, or drop to the next event.
* If you've run out of events to check (eg. `battery_level == 0`), then the transition isn't possible.
You can also pass additional arguments, which can be evaluated by :if methods or procs. See examples in
[conditionals_test.rb](test/conditionals_test.rb#L45)
### Advanced transition hooks
Expand Down Expand Up @@ -513,6 +515,10 @@ end
Changelog
---------
=== New in the version 3.1.0
* gh-227 allow event arguments to be taken into account when selecting the event
=== New in the version 3.0.0
* gh-228 Support for Ruby 3 keyword args, provided by @agirling
Expand Down Expand Up @@ -552,7 +558,7 @@ integration with https://github.com/activeadmin/activeadmin[ActiveAdmin].

Author: Vladimir Dobriakov, <https://infrastructure-as-code.de>

Copyright (c) 2010-2022 Vladimir Dobriakov and Contributors
Copyright (c) 2010-2024 Vladimir Dobriakov and Contributors

Copyright (c) 2008-2009 Vodafone

Expand Down
6 changes: 3 additions & 3 deletions lib/workflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ def assign_workflow(specification_object)
process_event!(event_name, *args, **kwargs)
end

define_method "can_#{event_name}?" do
return !!current_state.events.first_applicable(event_name, self)
define_method "can_#{event_name}?".to_sym do |*args, **kwargs|
return !!current_state.events.first_applicable(event_name, self, args)
end
end
end
Expand Down Expand Up @@ -95,7 +95,7 @@ def halted_because
end

def process_event!(name, *args, **kwargs)
event = current_state.events.first_applicable(name, self)
event = current_state.events.first_applicable(name, self, args)
raise NoTransitionAllowed.new(
"There is no event #{name.to_sym} defined for the #{current_state} state") \
if event.nil?
Expand Down
6 changes: 3 additions & 3 deletions lib/workflow/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ def initialize(name, transitions_to, condition = nil, meta = {}, &action)
end
end

def condition_applicable?(object)
def condition_applicable?(object, event_arguments)
if condition
if condition.is_a?(Symbol)
object.send(condition)
object.send(condition, *event_arguments)
else
condition.call(object)
condition.call(object, *event_arguments)
end
else
true
Expand Down
4 changes: 2 additions & 2 deletions lib/workflow/event_collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ def include?(name_or_obj)
end
end

def first_applicable(name, object_context)
def first_applicable(name, object_context, event_arguments)
(self[name] || []).detect do |event|
event.condition_applicable?(object_context) && event
event.condition_applicable?(object_context, event_arguments) && event
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/workflow/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Workflow
VERSION = "3.0.0"
VERSION = "3.1.0"
end
99 changes: 99 additions & 0 deletions test/conditionals_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
require File.join(File.dirname(__FILE__), 'test_helper')

$VERBOSE = false
require 'workflow'
require 'mocha/minitest'

class ConditionalsTest < Minitest::Test

test 'can_<fire_event>? with conditions' do
c = Class.new do
include Workflow
workflow do
state :off do
event :turn_on, :transitions_to => :on, :if => :sufficient_battery_level?
event :turn_on, :transitions_to => :low_battery, :if => proc { |obj| obj.battery > 0 }
end
state :on
state :low_battery
end
attr_reader :battery
def initialize(battery)
@battery = battery
end

def sufficient_battery_level?
@battery > 10
end
end

device = c.new 0
assert_equal false, device.can_turn_on?

device = c.new 5
assert device.can_turn_on?
device.turn_on!
assert device.low_battery?
assert_equal false, device.on?

device = c.new 50
assert device.can_turn_on?
device.turn_on!
assert device.on?
end

test 'gh-227 allow event arguments in conditions - test with proc' do
c = Class.new do
include Workflow
# define more advanced workflow, where event methods allow arguments
workflow do
state :off do
# turn_on accepts additional argument `power_adapter`
event :turn_on, :transitions_to => :on, :if => :sufficient_battery_level?
event :turn_on, :transitions_to => :low_battery # otherwise
end
state :on
state :low_battery
end
attr_reader :battery
def initialize(battery)
@battery = battery
end

def sufficient_battery_level?(power_adapter=false)
power_adapter || @battery > 10
end
end

# test for conditions in a proc
device = c.new 5
device.turn_on!(true) # case with event arguments to be taken into account
assert device.on?
end

test 'gh-227 allow event arguments in conditions - test with a method' do
c = Class.new do
include Workflow
# define more advanced workflow, where event methods allow arguments
workflow do
state :off do
# turn_on accepts additional argument `power_adapter`
event :turn_on, :transitions_to => :on, :if => proc { |obj, power_adapter| power_adapter || obj.battery > 10 }
event :turn_on, :transitions_to => :low_battery # otherwise
end
state :on
state :low_battery
end
attr_reader :battery
def initialize(battery)
@battery = battery
end
end

device = c.new 5
device.turn_on!(true) # case with event arguments to be taken into account
assert device.on?
end

end

36 changes: 0 additions & 36 deletions test/main_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -328,42 +328,6 @@ def reject(reason)
assert_equal false, human.can_go_to_college?
end

test 'can_<fire_event>? with conditions' do
c = Class.new do
include Workflow
workflow do
state :off do
event :turn_on, :transitions_to => :on, :if => :sufficient_battery_level?
event :turn_on, :transitions_to => :low_battery, :if => proc { |obj| obj.battery > 0 }
end
state :on
state :low_battery
end
attr_reader :battery
def initialize(battery)
@battery = battery
end

def sufficient_battery_level?
@battery > 10
end
end

device = c.new 0
assert_equal false, device.can_turn_on?

device = c.new 5
assert device.can_turn_on?
device.turn_on!
assert device.low_battery?
assert_equal false, device.on?

device = c.new 50
assert device.can_turn_on?
device.turn_on!
assert device.on?
end

test 'workflow graph generation' do
require 'workflow/draw'
Dir.chdir('/tmp') do
Expand Down

0 comments on commit b118f24

Please sign in to comment.