Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Change Log

## [Unreleased]
* Add ability to pass keyword arguments to callbacks.

## [v0.14.1] - 2023-10-08

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ gemspec
gem "json", "2.4.1" if RUBY_VERSION == "2.0.0"

group :development do
gem "ruby-prof", "~> 0.17.0", platforms: :mri
gem "pry", "~> 0.10.1"
gem "ruby-prof", platforms: :mri
gem "pry"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bundler/OrderedGems: Gems should be sorted in an alphabetical order within their section of the Gemfile. Gem pry should appear before ruby-prof.

gem "rspec-benchmark", RUBY_VERSION < "2.1.0" ? "~> 0.4" : "~> 0.6"
end

Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1021,21 +1021,29 @@ All callbacks as a first argument yielded to a block receive the `TransitionEven
* `from` - the state transitioning from`
* `to` - the state transitioning to`

followed by the rest of arguments that were passed to the event method.
followed by the rest of arguments and keyword arguments that were passed to the event method.

```ruby
fm = FiniteMachine.new do
initial :red

event :ready, :red => :yellow
event :abort, :yellow => :red

on_before_ready do |event, time|
puts "lights switching from #{event.from} to #{event.to} in #{time} seconds"
end

on_before_abort do |event, reason:|
puts "lights switching from #{event.from} to #{event.to} because of #{reason}"
end
end

fm.ready(3)
# => "lights switching from red to yellow in 3 seconds"

fm.abort(reason: "emergency")
# => "lights switching from yellow to red because of emergency"
```

### 4.6 Duplicate callbacks
Expand Down
13 changes: 7 additions & 6 deletions lib/finite_machine/async_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ class AsyncCall
# AsyncCall.new(context, Callable.new(:method), :a, :b)
#
# @api public
def initialize(context, callable, *args, &block)
@context = context
@callable = callable
@arguments = args.dup
@block = block
def initialize(context, callable, *args, **kwargs, &block)
@context = context
@callable = callable
@arguments = args.dup
@kwarguments = kwargs.dup
@block = block
freeze
end

Expand All @@ -33,7 +34,7 @@ def initialize(context, callable, *args, &block)
#
# @api private
def dispatch
@callable.call(@context, *@arguments, &@block)
@callable.call(@context, *@arguments, **@kwarguments, &@block)
end
end # AsyncCall
end # FiniteMachine
10 changes: 5 additions & 5 deletions lib/finite_machine/callable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,24 @@ def initialize(object)
#
# @api public
def invert
->(*args, &block) { !call(*args, &block) }
->(*args, **kwargs, &block) { !call(*args, **kwargs, &block) }
end

# Execute action
#
# @param [Object] target
#
# @api public
def call(target, *args, &block)
def call(target, *args, **kwargs, &block)
case object
when Symbol
target.public_send(object.to_sym, *args, &block)
target.public_send(object.to_sym, *args, **kwargs, &block)
when String
string = args.empty? ? "-> { #{object} }" : "-> { #{object}(*#{args}) }"
string = args.empty? ? "-> { #{object} }" : "-> { #{object}(*#{args}, **#{kwargs}) }"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [93/80]

value = eval(string)
target.instance_exec(&value)
when ::Proc
object.arity.zero? ? object.call : object.call(target, *args)
object.arity.zero? ? object.call : object.call(target, *args, **kwargs)
else
raise ArgumentError, "Unknown callable #{object}"
end
Expand Down
8 changes: 4 additions & 4 deletions lib/finite_machine/event_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ def apply(event_name, silent = false)
#
# @api private
def define_event_transition(event_name, silent)
machine.send(:define_singleton_method, event_name) do |*data, &block|
machine.send(:define_singleton_method, event_name) do |*args, **kwargs, &block|

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [85/80]

method = silent ? :transition : :trigger
machine.public_send(method, event_name, *data, &block)
machine.public_send(method, event_name, *args, **kwargs, &block)
end
end

Expand All @@ -65,9 +65,9 @@ def define_event_transition(event_name, silent)
#
# @api private
def define_event_bang(event_name, silent)
machine.send(:define_singleton_method, "#{event_name}!") do |*data, &block|
machine.send(:define_singleton_method, "#{event_name}!") do |*args, **kwargs, &block|

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [91/80]

method = silent ? :transition! : :trigger!
machine.public_send(method, event_name, *data, &block)
machine.public_send(method, event_name, *args, **kwargs, &block)
end
end
end # EventBuilder
Expand Down
4 changes: 2 additions & 2 deletions lib/finite_machine/hook_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ def initialize(name, event_name, from)
# @return [nil]
#
# @api public
def notify(subscriber, *data)
def notify(subscriber, *args, **kwargs)
return unless subscriber.respond_to?(MESSAGE)

subscriber.public_send(MESSAGE, self, *data)
subscriber.public_send(MESSAGE, self, *args, **kwargs)
end

# Compare whether the instance is greater, less then or equal to other
Expand Down
4 changes: 2 additions & 2 deletions lib/finite_machine/listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def on_delivery(&block)
# Invoke event handler
#
# @api private
def call(*args)
@on_delivery.call(*args) if @on_delivery
def call(*args, **kwargs)
@on_delivery.call(*args, **kwargs) if @on_delivery

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style/SafeNavigation: Use safe navigation (&.) instead of checking if an object exists before calling the method.

end
alias handle_delivery call
end # Listener
Expand Down
4 changes: 2 additions & 2 deletions lib/finite_machine/message_queue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ def <<(event)
# @return [void]
#
# @api public
def subscribe(*args, &block)
def subscribe(*args, **kwargs, &block)
@mutex.synchronize do
listener = Listener.new(*args)
listener = Listener.new(*args, **kwargs)
listener.on_delivery(&block)
@listeners << listener
end
Expand Down
64 changes: 32 additions & 32 deletions lib/finite_machine/observer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,44 +76,44 @@ module Once; end

module Async; end

def on_enter(*args, &callback)
on HookEvent::Enter, *args, &callback
def on_enter(*args, **kwargs, &callback)
on HookEvent::Enter, *args, **kwargs, &callback
end

def on_transition(*args, &callback)
on HookEvent::Transition, *args, &callback
def on_transition(*args, **kwargs, &callback)
on HookEvent::Transition, *args, **kwargs, &callback
end

def on_exit(*args, &callback)
on HookEvent::Exit, *args, &callback
def on_exit(*args, **kwargs, &callback)
on HookEvent::Exit, *args, **kwargs, &callback
end

def once_on_enter(*args, &callback)
on HookEvent::Enter, *args, &callback.extend(Once)
def once_on_enter(*args, **kwargs, &callback)
on HookEvent::Enter, *args, **kwargs, &callback.extend(Once)
end

def once_on_transition(*args, &callback)
on HookEvent::Transition, *args, &callback.extend(Once)
def once_on_transition(*args, **kwargs, &callback)
on HookEvent::Transition, *args, **kwargs, &callback.extend(Once)
end

def once_on_exit(*args, &callback)
on HookEvent::Exit, *args, &callback.extend(Once)
def once_on_exit(*args, **kwargs, &callback)
on HookEvent::Exit, *args, **kwargs, &callback.extend(Once)
end

def on_before(*args, &callback)
on HookEvent::Before, *args, &callback
def on_before(*args, **kwargs, &callback)
on HookEvent::Before, *args, **kwargs, &callback
end

def on_after(*args, &callback)
on HookEvent::After, *args, &callback
def on_after(*args, **kwargs, &callback)
on HookEvent::After, *args, **kwargs, &callback
end

def once_on_before(*args, &callback)
on HookEvent::Before, *args, &callback.extend(Once)
def once_on_before(*args, **kwargs, &callback)
on HookEvent::Before, *args, **kwargs, &callback.extend(Once)
end

def once_on_after(*args, &callback)
on HookEvent::After, *args, &callback.extend(Once)
def once_on_after(*args, **kwargs, &callback)
on HookEvent::After, *args, **kwargs, &callback.extend(Once)
end

# Execute each of the hooks in order with supplied data
Expand All @@ -126,13 +126,13 @@ def once_on_after(*args, &callback)
# @return [nil]
#
# @api public
def emit(event, *data)
def emit(event, *args, **kwargs)
sync_exclusive do
[event.type].each do |hook_type|
any_state_or_event = HookEvent.any_state_or_event(hook_type)
[any_state_or_event, event.name].each do |event_name|
hooks[hook_type][event_name].each do |hook|
handle_callback(hook, event, *data)
handle_callback(hook, event, *args, **kwargs)
off(hook_type, event_name, &hook) if hook.is_a?(Once)
end
end
Expand Down Expand Up @@ -166,23 +166,23 @@ def cancel_event(msg = nil)
# @param [Array[Object]] :data
#
# @api private
def handle_callback(hook, event, *data)
to = machine.events_map.move_to(event.event_name, event.from, *data)
def handle_callback(hook, event, *args, **kwargs)
to = machine.events_map.move_to(event.event_name, event.from, *args, **kwargs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [84/80]

trans_event = TransitionEvent.new(event.event_name, event.from, to)
callable = create_callable(hook)

if hook.is_a?(Async)
defer(callable, trans_event, *data)
defer(callable, trans_event, *args, **kwargs)
else
callable.(trans_event, *data)
callable.(trans_event, *args, **kwargs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style/LambdaCall: Prefer the use of lambda.call(...) over lambda.(...).

end
end

# Defer callback execution
#
# @api private
def defer(callable, trans_event, *data)
async_call = AsyncCall.new(machine, callable, trans_event, *data)
def defer(callable, trans_event, *args, **kwargs)
async_call = AsyncCall.new(machine, callable, trans_event, *args, **kwargs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [81/80]

callback_queue.start unless callback_queue.running?
callback_queue << async_call
end
Expand Down Expand Up @@ -220,8 +220,8 @@ def cleanup_callback_queue
#
# @api private
def create_callable(hook)
callback = proc do |trans_event, *data|
machine.instance_exec(trans_event, *data, &hook)
callback = proc do |trans_event, *args, **kwargs|
machine.instance_exec(trans_event, *args, **kwargs, &hook)
end
Callable.new(callback)
end
Expand All @@ -245,10 +245,10 @@ def callback_names
# @return [self]
#
# @api private
def method_missing(method_name, *args, &block)
def method_missing(method_name, *args, **kwargs, &block)
_, event_name, callback_name = *method_name.to_s.match(/^(\w*?on_\w+?)_(\w+)$/)
if callback_name && callback_names.include?(callback_name.to_sym)
public_send(event_name, :"#{callback_name}", *args, &block)
public_send(event_name, :"#{callback_name}", *args, **kwargs, &block)
else
super
end
Expand Down
Loading