From 5e6849af790369d971fdbeb32102c9db656e4c92 Mon Sep 17 00:00:00 2001 From: Vladimir Dobriakov Date: Wed, 29 May 2024 18:56:41 +0200 Subject: [PATCH] Improve backward compatibility for #227 - let `:if` methods and procs ignore additional arguments In case the method referenced by `:if` does not accept additional arguments, but the transition action is called with some, avoid exception "1 argument provided, but 0 were expected" by ignoring superfluous arguments if needed. --- lib/workflow/event.rb | 12 +++++++++++- test/conditionals_test.rb | 26 +++++++++++++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/workflow/event.rb b/lib/workflow/event.rb index fd3187007..341c7e578 100644 --- a/lib/workflow/event.rb +++ b/lib/workflow/event.rb @@ -18,8 +18,18 @@ def initialize(name, transitions_to, condition = nil, meta = {}, &action) def condition_applicable?(object, event_arguments) if condition if condition.is_a?(Symbol) - object.send(condition, *event_arguments) + m = object.method(condition) + # Conditionals can now take the arguments of the trasition action into account #227 + # But in case the current conditional wants to ignore any event_argument on its decision - + # does not accept parameters, also support that. + if m.arity == 0 # no additional parameters accepted + object.send(condition) + else + object.send(condition, *event_arguments) + end else + # since blocks can ignore extra arguments without raising an error in Ruby, + # no `if` is needed - compare with `arity` switch in above methods handling condition.call(object, *event_arguments) end else diff --git a/test/conditionals_test.rb b/test/conditionals_test.rb index 852cb454a..f4352bbc1 100644 --- a/test/conditionals_test.rb +++ b/test/conditionals_test.rb @@ -42,7 +42,7 @@ def sufficient_battery_level? assert device.on? end - test 'gh-227 allow event arguments in conditions - test with proc' do + 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 @@ -52,7 +52,10 @@ def sufficient_battery_level? event :turn_on, :transitions_to => :on, :if => :sufficient_battery_level? event :turn_on, :transitions_to => :low_battery # otherwise end - state :on + state :on do + event :check, :transitions_to => :low_battery, :if => :check_low_battery? + event :check, :transitions_to => :on # stay in on state otherwise + end state :low_battery end attr_reader :battery @@ -60,18 +63,25 @@ def initialize(battery) @battery = battery end - def sufficient_battery_level?(power_adapter=false) + def sufficient_battery_level?(power_adapter) power_adapter || @battery > 10 end + + def check_low_battery?() # supports no arguments, lets test below, what happens if the action uses addtional args + # 'in check_low_battery? method' + 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? + device.check!('foo') # the conditional in the definition above does not support arguments, but make it work + # by ignoring superfluous arguments for compatibility + assert device.on? end - test 'gh-227 allow event arguments in conditions - test with a method' do + test 'gh-227 allow event arguments in conditions - test with a proc' do c = Class.new do include Workflow # define more advanced workflow, where event methods allow arguments @@ -81,7 +91,10 @@ def sufficient_battery_level?(power_adapter=false) 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 :on do + event :check, :transitions_to => :low_battery, :if => proc { |obj| return false } + event :check, :transitions_to => :on # stay in on state otherwise + end state :low_battery end attr_reader :battery @@ -93,6 +106,9 @@ def initialize(battery) device = c.new 5 device.turn_on!(true) # case with event arguments to be taken into account assert device.on? + device.check!('foo') # also ensure that if conditional in the definition above does not support arguments, + # it still works and just ignores superfluous arguments + assert device.on? end end