finite_machine 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'logger'
3
+ require "logger"
4
4
 
5
- require_relative 'finite_machine/const'
6
- require_relative 'finite_machine/logger'
7
- require_relative 'finite_machine/definition'
8
- require_relative 'finite_machine/state_machine'
9
- require_relative 'finite_machine/version'
5
+ require_relative "finite_machine/const"
6
+ require_relative "finite_machine/logger"
7
+ require_relative "finite_machine/definition"
8
+ require_relative "finite_machine/state_machine"
9
+ require_relative "finite_machine/version"
10
10
 
11
11
  module FiniteMachine
12
12
  # Default state name
@@ -33,7 +33,7 @@ module FiniteMachine
33
33
  if block_given?
34
34
  options[:with] = block
35
35
  else
36
- raise ArgumentError, 'Need to provide error handler.'
36
+ raise ArgumentError, "Need to provide error handler."
37
37
  end
38
38
  end
39
39
  evaluate_exceptions(exceptions, options)
@@ -71,7 +71,7 @@ module FiniteMachine
71
71
  #
72
72
  # @api private
73
73
  def extract_const(class_name)
74
- class_name.split('::').reduce(FiniteMachine) do |constant, part|
74
+ class_name.split("::").reduce(FiniteMachine) do |constant, part|
75
75
  constant.const_get(part)
76
76
  end
77
77
  end
@@ -85,9 +85,9 @@ module FiniteMachine
85
85
  target.method(handler)
86
86
  when Proc
87
87
  if handler.arity.zero?
88
- proc { instance_exec(&handler) }
88
+ -> { instance_exec(&handler) }
89
89
  else
90
- proc { |_exception| instance_exec(_exception, &handler) }
90
+ ->(exception) { instance_exec(exception, &handler) }
91
91
  end
92
92
  end
93
93
  end
@@ -101,16 +101,24 @@ module FiniteMachine
101
101
  # @api private
102
102
  def evaluate_exceptions(exceptions, options)
103
103
  exceptions.each do |exception|
104
- key = if exception.is_a?(Class) && exception <= Exception
105
- exception.name
106
- elsif exception.is_a?(String)
107
- exception
108
- else
109
- raise ArgumentError, "#{exception} isn't an Exception"
110
- end
111
-
104
+ key = extract_exception_name(exception)
112
105
  error_handlers << [key, options[:with]]
113
106
  end
114
107
  end
108
+
109
+ # Extract exception name
110
+ #
111
+ # @param [Class,Exception,String] exception
112
+ #
113
+ # @api private
114
+ def extract_exception_name(exception)
115
+ if exception.is_a?(Class) && exception <= Exception
116
+ exception.name
117
+ elsif exception.is_a?(String)
118
+ exception
119
+ else
120
+ raise ArgumentError, "#{exception} isn't an Exception"
121
+ end
122
+ end
115
123
  end # Catchable
116
124
  end # FiniteMachine
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'transition_builder'
3
+ require_relative "transition_builder"
4
4
 
5
5
  module FiniteMachine
6
6
  # A class responsible for merging choice options
@@ -39,6 +39,6 @@ module FiniteMachine
39
39
  @transitions.merge(conditions))
40
40
  transition_builder.call(@transitions[:from] => to)
41
41
  end
42
- alias_method :default, :choice
42
+ alias default choice
43
43
  end # ChoiceMerger
44
44
  end # FiniteMachine
@@ -49,7 +49,7 @@ module FiniteMachine
49
49
  def self.inherited(subclass)
50
50
  super
51
51
 
52
- self.deferreds.each { |d| subclass.add_deferred(d) }
52
+ deferreds.each { |d| subclass.add_deferred(d) }
53
53
  end
54
54
 
55
55
  # Delay lookup of DSL method
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'choice_merger'
4
- require_relative 'safety'
5
- require_relative 'transition_builder'
3
+ require_relative "choice_merger"
4
+ require_relative "safety"
5
+ require_relative "transition_builder"
6
6
 
7
7
  module FiniteMachine
8
8
  # A generic DSL for describing the state machine
@@ -38,6 +38,13 @@ module FiniteMachine
38
38
  end
39
39
  end
40
40
 
41
+ # Check if message can be handled by this DSL
42
+ #
43
+ # @api private
44
+ def respond_to_missing?(method_name, include_private = false)
45
+ @machine.respond_to?(method_name) || super
46
+ end
47
+
41
48
  # Configure state machine properties
42
49
  #
43
50
  # @api private
@@ -65,6 +72,29 @@ module FiniteMachine
65
72
  log_transitions(@attrs.fetch(:log_transitions, false))
66
73
  end
67
74
 
75
+ # Add aliases for the target object
76
+ #
77
+ # @example
78
+ # FiniteMachine.define do
79
+ # target_alias :engine
80
+ #
81
+ # on_transition do |event|
82
+ # engine.state = event.to
83
+ # end
84
+ # end
85
+ #
86
+ # @param [Array<Symbol>] aliases
87
+ # the names for target alias
88
+ #
89
+ # @api public
90
+ def alias_target(*aliases)
91
+ aliases.each do |alias_name|
92
+ next if env.aliases.include?(alias_name)
93
+
94
+ env.aliases << alias_name
95
+ end
96
+ end
97
+
68
98
  # Define initial state
69
99
  #
70
100
  # @param [Symbol] value
@@ -197,8 +227,8 @@ module FiniteMachine
197
227
  #
198
228
  # @api private
199
229
  def raise_missing_state
200
- fail MissingInitialStateError,
201
- 'Provide state to transition :to for the initial event'
230
+ raise MissingInitialStateError,
231
+ "Provide state to transition :to for the initial event"
202
232
  end
203
233
  end # DSL
204
234
  end # FiniteMachine
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'threadable'
3
+ require_relative "threadable"
4
4
 
5
5
  module FiniteMachine
6
6
  # Holds references to targets and aliases
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/map'
4
- require 'forwardable'
3
+ require "concurrent/map"
4
+ require "forwardable"
5
5
 
6
- require_relative 'threadable'
7
- require_relative 'undefined_transition'
6
+ require_relative "threadable"
7
+ require_relative "undefined_transition"
8
8
 
9
9
  module FiniteMachine
10
10
  # A class responsible for storing mappings between event namess and
@@ -182,8 +182,7 @@ module FiniteMachine
182
182
  # @api public
183
183
  def match_transition_with(name, from_state, *conditions)
184
184
  find(name).find do |trans|
185
- trans.matches?(from_state) &&
186
- trans.check_conditions(*conditions)
185
+ trans.matches?(from_state) && trans.check_conditions(*conditions)
187
186
  end
188
187
  end
189
188
 
@@ -29,7 +29,7 @@ module FiniteMachine
29
29
  #
30
30
  # @api public
31
31
  def self.event_name
32
- name.split('::').last.downcase.to_sym
32
+ name.split("::").last.downcase.to_sym
33
33
  end
34
34
 
35
35
  # String representation
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/map'
3
+ require "concurrent/map"
4
4
 
5
- require_relative 'hook_event'
5
+ require_relative "hook_event"
6
6
 
7
7
  module FiniteMachine
8
8
  # A class reponsible for registering callbacks
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'listener'
4
- require 'thread'
3
+ require_relative "listener"
4
+ require "thread"
5
5
 
6
6
  module FiniteMachine
7
7
  # Allows for storage of asynchronous messages such as events
@@ -31,6 +31,7 @@ module FiniteMachine
31
31
  # @api private
32
32
  def start
33
33
  return if running?
34
+
34
35
  @mutex.synchronize { spawn_thread }
35
36
  end
36
37
 
@@ -114,6 +115,7 @@ module FiniteMachine
114
115
  # @api public
115
116
  def join(timeout = nil)
116
117
  return unless @thread
118
+
117
119
  timeout.nil? ? @thread.join : @thread.join(timeout)
118
120
  end
119
121
 
@@ -126,7 +128,7 @@ module FiniteMachine
126
128
  #
127
129
  # @api public
128
130
  def shutdown
129
- fail EventQueueDeadError, 'event queue already dead' if @dead
131
+ raise EventQueueDeadError, "event queue already dead" if @dead
130
132
 
131
133
  queue = []
132
134
  @mutex.synchronize do
@@ -184,6 +186,7 @@ module FiniteMachine
184
186
  end
185
187
  event = @queue.pop
186
188
  break unless event
189
+
187
190
  notify_listeners(event)
188
191
  event.dispatch
189
192
  end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'async_call'
4
- require_relative 'callable'
5
- require_relative 'hook_event'
6
- require_relative 'hooks'
7
- require_relative 'message_queue'
8
- require_relative 'safety'
9
- require_relative 'transition_event'
3
+ require_relative "async_call"
4
+ require_relative "callable"
5
+ require_relative "hook_event"
6
+ require_relative "hooks"
7
+ require_relative "message_queue"
8
+ require_relative "safety"
9
+ require_relative "transition_event"
10
10
 
11
11
  module FiniteMachine
12
12
  # A class responsible for observing state changes
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'hook_event'
3
+ require_relative "hook_event"
4
4
 
5
5
  module FiniteMachine
6
6
  # Module responsible for safety checks against known methods
@@ -39,7 +39,7 @@ module FiniteMachine
39
39
  name: event_name,
40
40
  type: :instance,
41
41
  method: method_name,
42
- source: 'FiniteMachine'
42
+ source: "FiniteMachine"
43
43
  }
44
44
  end
45
45
  end
@@ -89,8 +89,8 @@ module FiniteMachine
89
89
  # @api private
90
90
  def wrong_event_name?(name, event_type)
91
91
  machine.states.include?(name) &&
92
- !machine.events.include?(name) &&
93
- event_type < HookEvent::Anyaction
92
+ !machine.events.include?(name) &&
93
+ event_type < HookEvent::Anyaction
94
94
  end
95
95
 
96
96
  # Check if state name exists
@@ -104,8 +104,8 @@ module FiniteMachine
104
104
  # @api private
105
105
  def wrong_state_name?(name, event_type)
106
106
  machine.events.include?(name) &&
107
- !machine.states.include?(name) &&
108
- event_type < HookEvent::Anystate
107
+ !machine.states.include?(name) &&
108
+ event_type < HookEvent::Anystate
109
109
  end
110
110
 
111
111
  def raise_invalid_callback_error(message)
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
3
+ require "forwardable"
4
4
 
5
- require_relative 'catchable'
6
- require_relative 'dsl'
7
- require_relative 'env'
8
- require_relative 'events_map'
9
- require_relative 'hook_event'
10
- require_relative 'observer'
11
- require_relative 'threadable'
12
- require_relative 'subscribers'
5
+ require_relative "catchable"
6
+ require_relative "dsl"
7
+ require_relative "env"
8
+ require_relative "events_map"
9
+ require_relative "hook_event"
10
+ require_relative "observer"
11
+ require_relative "threadable"
12
+ require_relative "subscribers"
13
13
 
14
14
  module FiniteMachine
15
15
  # Base class for state machine
@@ -64,7 +64,8 @@ module FiniteMachine
64
64
  # Allow or not logging of transitions
65
65
  attr_threadsafe :log_transitions
66
66
 
67
- def_delegators :dsl, :initial, :terminal, :event, :trigger_init
67
+ def_delegators :dsl, :initial, :terminal, :event, :trigger_init,
68
+ :alias_target
68
69
 
69
70
  # Initialize state machine
70
71
  #
@@ -194,7 +195,7 @@ module FiniteMachine
194
195
  # fsm.can?(:go) # => true
195
196
  #
196
197
  # @example
197
- # fsm.can?(:go, 'Piotr') # checks condition with parameter 'Piotr'
198
+ # fsm.can?(:go, "Piotr") # checks condition with parameter "Piotr"
198
199
  #
199
200
  # @param [String] event
200
201
  #
@@ -202,7 +203,7 @@ module FiniteMachine
202
203
  #
203
204
  # @api public
204
205
  def can?(*args)
205
- event_name = args.shift
206
+ event_name = args.shift
206
207
  events_map.can_perform?(event_name, current, *args)
207
208
  end
208
209
 
@@ -284,7 +285,7 @@ module FiniteMachine
284
285
  else
285
286
  exception = InvalidStateError
286
287
  catch_error(exception) ||
287
- fail(exception, "inappropriate current state '#{current}'")
288
+ raise(exception, "inappropriate current state '#{current}'")
288
289
 
289
290
  false
290
291
  end
@@ -390,8 +391,10 @@ module FiniteMachine
390
391
  # @api public
391
392
  def inspect
392
393
  sync_shared do
393
- "<##{self.class}:0x#{object_id.to_s(16)} @states=#{states}, " \
394
- "@events=#{events}, " \
394
+ "<##{self.class}:0x#{object_id.to_s(16)} " \
395
+ "@current=#{current.inspect} " \
396
+ "@states=#{states} " \
397
+ "@events=#{events} " \
395
398
  "@transitions=#{events_map.state_transitions}>"
396
399
  end
397
400
  end
@@ -407,7 +410,7 @@ module FiniteMachine
407
410
  #
408
411
  # @api private
409
412
  def raise_transition_error(error)
410
- fail TransitionError, Logger.format_error(error)
413
+ raise TransitionError, Logger.format_error(error)
411
414
  end
412
415
 
413
416
  # Forward the message to observer or self
@@ -7,7 +7,9 @@ module FiniteMachine
7
7
  #
8
8
  # @api private
9
9
  class StateParser
10
- BLACKLIST = [:name, :if, :unless, :silent].freeze
10
+ NON_STATE_KEYS = %i[name if unless silent].freeze
11
+
12
+ STATE_KEYS = %i[from to].freeze
11
13
 
12
14
  # Extract states from user defined attributes
13
15
  #
@@ -35,7 +37,7 @@ module FiniteMachine
35
37
  # @api private
36
38
  def self.ensure_only_states!(attrs)
37
39
  attributes = attrs.dup
38
- BLACKLIST.each { |key| attributes.delete(key) }
40
+ NON_STATE_KEYS.each { |key| attributes.delete(key) }
39
41
  raise_not_enough_transitions unless attributes.any?
40
42
  attributes
41
43
  end
@@ -69,7 +71,7 @@ module FiniteMachine
69
71
  #
70
72
  # @api public
71
73
  def self.contains_from_to_keys?(attrs)
72
- [:from, :to].any? { |key| attrs.keys.include?(key) }
74
+ STATE_KEYS.any? { |key| attrs.keys.include?(key) }
73
75
  end
74
76
  private_class_method :contains_from_to_keys?
75
77
 
@@ -79,9 +81,8 @@ module FiniteMachine
79
81
  #
80
82
  # @api private
81
83
  def self.convert_from_to_attributes_to_states_hash(attrs)
82
- Array(attrs[:from] || ANY_STATE).reduce({}) do |hash, state|
84
+ Array(attrs[:from] || ANY_STATE).each_with_object({}) do |state, hash|
83
85
  hash[state] = attrs[:to] || state
84
- hash
85
86
  end
86
87
  end
87
88
  private_class_method :convert_from_to_attributes_to_states_hash
@@ -99,13 +100,12 @@ module FiniteMachine
99
100
  #
100
101
  # @api private
101
102
  def self.convert_attributes_to_states_hash(attrs)
102
- attrs.reduce({}) do |hash, (k, v)|
103
+ attrs.each_with_object({}) do |(k, v), hash|
103
104
  if k.respond_to?(:to_ary)
104
105
  k.each { |el| hash[el] = v }
105
106
  else
106
107
  hash[k] = v
107
108
  end
108
- hash
109
109
  end
110
110
  end
111
111
  private_class_method :convert_attributes_to_states_hash
@@ -119,7 +119,7 @@ module FiniteMachine
119
119
  #
120
120
  # @api private
121
121
  def self.raise_not_enough_transitions
122
- raise NotEnoughTransitionsError, 'please provide state transitions'
122
+ raise NotEnoughTransitionsError, "please provide state transitions"
123
123
  end
124
124
  private_class_method :raise_not_enough_transitions
125
125
  end # StateParser