finite_machine 0.11.3 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +564 -569
  4. data/Rakefile +5 -1
  5. data/benchmarks/memory_profile.rb +11 -0
  6. data/benchmarks/memory_usage.rb +16 -9
  7. data/finite_machine.gemspec +10 -3
  8. data/lib/finite_machine.rb +34 -46
  9. data/lib/finite_machine/async_call.rb +5 -21
  10. data/lib/finite_machine/callable.rb +4 -4
  11. data/lib/finite_machine/catchable.rb +4 -2
  12. data/lib/finite_machine/choice_merger.rb +19 -19
  13. data/lib/finite_machine/const.rb +16 -0
  14. data/lib/finite_machine/definition.rb +2 -2
  15. data/lib/finite_machine/dsl.rb +66 -149
  16. data/lib/finite_machine/env.rb +4 -2
  17. data/lib/finite_machine/event_definition.rb +7 -15
  18. data/lib/finite_machine/{events_chain.rb → events_map.rb} +39 -51
  19. data/lib/finite_machine/hook_event.rb +60 -61
  20. data/lib/finite_machine/hooks.rb +44 -36
  21. data/lib/finite_machine/listener.rb +2 -2
  22. data/lib/finite_machine/logger.rb +5 -4
  23. data/lib/finite_machine/message_queue.rb +39 -30
  24. data/lib/finite_machine/observer.rb +55 -37
  25. data/lib/finite_machine/safety.rb +12 -10
  26. data/lib/finite_machine/state_definition.rb +3 -5
  27. data/lib/finite_machine/state_machine.rb +83 -64
  28. data/lib/finite_machine/state_parser.rb +51 -79
  29. data/lib/finite_machine/subscribers.rb +1 -1
  30. data/lib/finite_machine/threadable.rb +3 -1
  31. data/lib/finite_machine/transition.rb +30 -31
  32. data/lib/finite_machine/transition_builder.rb +23 -32
  33. data/lib/finite_machine/transition_event.rb +12 -11
  34. data/lib/finite_machine/two_phase_lock.rb +3 -1
  35. data/lib/finite_machine/undefined_transition.rb +5 -6
  36. data/lib/finite_machine/version.rb +2 -2
  37. data/spec/integration/system_spec.rb +36 -38
  38. data/spec/performance/benchmark_spec.rb +13 -21
  39. data/spec/unit/alias_target_spec.rb +22 -41
  40. data/spec/unit/async_callbacks_spec.rb +8 -13
  41. data/spec/unit/auto_methods_spec.rb +44 -0
  42. data/spec/unit/callable/call_spec.rb +1 -3
  43. data/spec/unit/callbacks_spec.rb +372 -463
  44. data/spec/unit/can_spec.rb +13 -23
  45. data/spec/unit/cancel_callbacks_spec.rb +46 -0
  46. data/spec/unit/choice_spec.rb +105 -141
  47. data/spec/unit/define_spec.rb +31 -31
  48. data/spec/unit/definition_spec.rb +24 -41
  49. data/spec/unit/event_names_spec.rb +6 -10
  50. data/spec/unit/events_map/add_spec.rb +23 -0
  51. data/spec/unit/events_map/choice_transition_spec.rb +25 -0
  52. data/spec/unit/events_map/clear_spec.rb +13 -0
  53. data/spec/unit/events_map/events_spec.rb +16 -0
  54. data/spec/unit/events_map/inspect_spec.rb +22 -0
  55. data/spec/unit/{events_chain → events_map}/match_transition_spec.rb +12 -14
  56. data/spec/unit/{events_chain → events_map}/move_to_spec.rb +14 -17
  57. data/spec/unit/events_map/states_for_spec.rb +17 -0
  58. data/spec/unit/events_spec.rb +91 -160
  59. data/spec/unit/handlers_spec.rb +34 -66
  60. data/spec/unit/hook_event/any_state_or_event_spec.rb +13 -0
  61. data/spec/unit/hook_event/build_spec.rb +1 -3
  62. data/spec/unit/hook_event/eql_spec.rb +1 -3
  63. data/spec/unit/hook_event/initialize_spec.rb +2 -4
  64. data/spec/unit/hook_event/notify_spec.rb +2 -4
  65. data/spec/unit/hooks/clear_spec.rb +1 -1
  66. data/spec/unit/hooks/{call_spec.rb → find_spec.rb} +4 -9
  67. data/spec/unit/hooks/inspect_spec.rb +16 -8
  68. data/spec/unit/hooks/register_spec.rb +4 -9
  69. data/spec/unit/if_unless_spec.rb +76 -115
  70. data/spec/unit/initial_spec.rb +50 -82
  71. data/spec/unit/inspect_spec.rb +14 -9
  72. data/spec/unit/is_spec.rb +12 -18
  73. data/spec/unit/log_transitions_spec.rb +4 -10
  74. data/spec/unit/logger_spec.rb +1 -3
  75. data/spec/unit/{event_queue_spec.rb → message_queue_spec.rb} +15 -8
  76. data/spec/unit/new_spec.rb +50 -0
  77. data/spec/unit/respond_to_spec.rb +2 -6
  78. data/spec/unit/state_parser/parse_spec.rb +9 -12
  79. data/spec/unit/states_spec.rb +12 -18
  80. data/spec/unit/subscribers_spec.rb +1 -3
  81. data/spec/unit/target_spec.rb +60 -93
  82. data/spec/unit/terminated_spec.rb +15 -25
  83. data/spec/unit/transition/check_conditions_spec.rb +16 -15
  84. data/spec/unit/transition/inspect_spec.rb +6 -6
  85. data/spec/unit/transition/matches_spec.rb +5 -7
  86. data/spec/unit/transition/states_spec.rb +5 -7
  87. data/spec/unit/transition/to_state_spec.rb +5 -13
  88. data/spec/unit/trigger_spec.rb +5 -9
  89. data/spec/unit/undefined_transition/eql_spec.rb +1 -3
  90. metadata +86 -49
  91. data/.gitignore +0 -18
  92. data/.rspec +0 -5
  93. data/.travis.yml +0 -27
  94. data/Gemfile +0 -16
  95. data/assets/finite_machine_logo.png +0 -0
  96. data/lib/finite_machine/async_proxy.rb +0 -55
  97. data/spec/unit/async_events_spec.rb +0 -107
  98. data/spec/unit/events_chain/add_spec.rb +0 -25
  99. data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
  100. data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
  101. data/spec/unit/events_chain/clear_spec.rb +0 -15
  102. data/spec/unit/events_chain/events_spec.rb +0 -18
  103. data/spec/unit/events_chain/inspect_spec.rb +0 -24
  104. data/spec/unit/events_chain/states_for_spec.rb +0 -17
  105. data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
  106. data/spec/unit/state_parser/inspect_spec.rb +0 -25
@@ -1,7 +1,9 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'hook_event'
2
4
 
3
5
  module FiniteMachine
4
- # Module for responsible for safety checks against known methods
6
+ # Module responsible for safety checks against known methods
5
7
  module Safety
6
8
  EVENT_CONFLICT_MESSAGE = \
7
9
  "You tried to define an event named \"%{name}\", however this would " \
@@ -33,7 +35,7 @@ module FiniteMachine
33
35
  # @api public
34
36
  def detect_event_conflict!(event_name, method_name = event_name)
35
37
  if method_already_implemented?(method_name)
36
- fail FiniteMachine::AlreadyDefinedError, EVENT_CONFLICT_MESSAGE % {
38
+ raise FiniteMachine::AlreadyDefinedError, EVENT_CONFLICT_MESSAGE % {
37
39
  name: event_name,
38
40
  type: :instance,
39
41
  method: method_name,
@@ -55,12 +57,12 @@ module FiniteMachine
55
57
  def ensure_valid_callback_name!(event_type, name)
56
58
  message = if wrong_event_name?(name, event_type)
57
59
  EVENT_CALLBACK_CONFLICT_MESSAGE % {
58
- type: "on_#{event_type.to_s}",
60
+ type: "on_#{event_type}",
59
61
  name: name
60
62
  }
61
63
  elsif wrong_state_name?(name, event_type)
62
64
  STATE_CALLBACK_CONFLICT_MESSAGE % {
63
- type: "on_#{event_type.to_s}",
65
+ type: "on_#{event_type}",
64
66
  name: name
65
67
  }
66
68
  elsif !callback_names.include?(name)
@@ -71,7 +73,7 @@ module FiniteMachine
71
73
  else
72
74
  nil
73
75
  end
74
- message && fail_invalid_callback_error(message)
76
+ message && raise_invalid_callback_error(message)
75
77
  end
76
78
 
77
79
  private
@@ -87,7 +89,7 @@ module FiniteMachine
87
89
  # @api private
88
90
  def wrong_event_name?(name, event_type)
89
91
  machine.states.include?(name) &&
90
- !machine.event_names.include?(name) &&
92
+ !machine.events.include?(name) &&
91
93
  event_type < HookEvent::Anyaction
92
94
  end
93
95
 
@@ -101,14 +103,14 @@ module FiniteMachine
101
103
  #
102
104
  # @api private
103
105
  def wrong_state_name?(name, event_type)
104
- machine.event_names.include?(name) &&
106
+ machine.events.include?(name) &&
105
107
  !machine.states.include?(name) &&
106
108
  event_type < HookEvent::Anystate
107
109
  end
108
110
 
109
- def fail_invalid_callback_error(message)
111
+ def raise_invalid_callback_error(message)
110
112
  exception = InvalidCallbackNameError
111
- machine.catch_error(exception) || fail(exception, message)
113
+ machine.catch_error(exception) || raise(exception, message)
112
114
  end
113
115
 
114
116
  # Check if method is already implemented inside StateMachine
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module FiniteMachine
4
4
  # A class responsible for defining state query methods on state machine
@@ -8,15 +8,13 @@ module FiniteMachine
8
8
  #
9
9
  # @api private
10
10
  class StateDefinition
11
- include Threadable
12
-
13
11
  # Initialize a StateDefinition
14
12
  #
15
13
  # @param [StateMachine] machine
16
14
  #
17
15
  # @api public
18
16
  def initialize(machine)
19
- self.machine = machine
17
+ @machine = machine
20
18
  end
21
19
 
22
20
  # Define query methods for states
@@ -34,7 +32,7 @@ module FiniteMachine
34
32
  private
35
33
 
36
34
  # The current state machine
37
- attr_threadsafe :machine
35
+ attr_reader :machine
38
36
 
39
37
  # Define helper state mehods for the transition states
40
38
  #
@@ -1,4 +1,15 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
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'
2
13
 
3
14
  module FiniteMachine
4
15
  # Base class for state machine
@@ -7,6 +18,13 @@ module FiniteMachine
7
18
  include Catchable
8
19
  extend Forwardable
9
20
 
21
+ # Current state
22
+ #
23
+ # @return [Symbol]
24
+ #
25
+ # @api private
26
+ attr_threadsafe :state
27
+
10
28
  # Initial state, defaults to :none
11
29
  attr_threadsafe :initial_state
12
30
 
@@ -20,21 +38,14 @@ module FiniteMachine
20
38
  attr_threadsafe :env
21
39
 
22
40
  # The state machine event definitions
23
- attr_threadsafe :events_chain
24
-
25
- # Errors DSL
26
- #
27
- # @return [ErrorsDSL]
28
- #
29
- # @api private
30
- attr_threadsafe :errors_dsl
41
+ attr_threadsafe :events_map
31
42
 
32
- # Events DSL
43
+ # Machine dsl
33
44
  #
34
- # @return [EventsDSL]
45
+ # @return [DSL]
35
46
  #
36
47
  # @api private
37
- attr_threadsafe :events_dsl
48
+ attr_threadsafe :dsl
38
49
 
39
50
  # The state machine observer
40
51
  #
@@ -43,13 +54,6 @@ module FiniteMachine
43
54
  # @api private
44
55
  attr_threadsafe :observer
45
56
 
46
- # Current state
47
- #
48
- # @return [Symbol]
49
- #
50
- # @api private
51
- attr_threadsafe :state
52
-
53
57
  # The state machine subscribers
54
58
  #
55
59
  # @return [Subscribers]
@@ -60,64 +64,76 @@ module FiniteMachine
60
64
  # Allow or not logging of transitions
61
65
  attr_threadsafe :log_transitions
62
66
 
63
- def_delegators :@dsl, :initial, :terminal, :target, :trigger_init,
64
- :alias_target
65
-
66
- def_delegator :events_dsl, :event
67
-
68
- def_delegator :@async_proxy, :event_queue
67
+ def_delegators :dsl, :initial, :terminal, :event, :trigger_init
69
68
 
70
69
  # Initialize state machine
71
70
  #
71
+ # @example
72
+ # fsm = FiniteMachine::StateMachine.new(target_alias: :car) do
73
+ # initial :red
74
+ #
75
+ # event :go, :red => :green
76
+ #
77
+ # on_transition do |event|
78
+ # car.state = event.to
79
+ # end
80
+ # end
81
+ #
82
+ # @param [Hash] options
83
+ # the options to create state machine with
84
+ # @option options [String] :alias_target
85
+ # the alias for target object
86
+ #
72
87
  # @api private
73
- def initialize(*args, &block)
74
- attributes = args.last.is_a?(Hash) ? args.pop : {}
75
-
88
+ def initialize(*args, **options, &block)
76
89
  @initial_state = DEFAULT_STATE
77
- @async_proxy = AsyncProxy.new(self)
90
+ @auto_methods = options.fetch(:auto_methods, true)
78
91
  @subscribers = Subscribers.new
79
92
  @observer = Observer.new(self)
80
- @events_chain = EventsChain.new
93
+ @events_map = EventsMap.new
81
94
  @env = Env.new(self, [])
82
- @events_dsl = EventsDSL.new(self)
83
- @errors_dsl = ErrorsDSL.new(self)
84
- @dsl = DSL.new(self, attributes)
95
+ @dsl = DSL.new(self, options)
85
96
 
86
- @dsl.call(&block) if block_given?
97
+ env.target = args.pop unless args.empty?
98
+ env.aliases << options[:alias_target] if options[:alias_target]
99
+ dsl.call(&block) if block_given?
87
100
  trigger_init
88
101
  end
89
102
 
90
- # Subscribe observer for event notifications
103
+ # Check if event methods should be auto generated
91
104
  #
92
- # @example
93
- # machine.subscribe(Observer.new(machine))
105
+ # @return [Boolean]
94
106
  #
95
107
  # @api public
96
- def subscribe(*observers)
97
- sync_exclusive { subscribers.subscribe(*observers) }
108
+ def auto_methods?
109
+ @auto_methods
98
110
  end
99
111
 
100
- # Help to mark the event as synchronous
112
+ # Attach state machine to an object
113
+ #
114
+ # This allows state machine to initiate events in the context
115
+ # of a particular object
101
116
  #
102
117
  # @example
103
- # fsm.sync.go
118
+ # FiniteMachine.define(target: object) do
119
+ # ...
120
+ # end
104
121
  #
105
- # @return [self]
122
+ # @return [Object|FiniteMachine::StateMachine]
106
123
  #
107
124
  # @api public
108
- alias_method :sync, :method_missing
125
+ def target
126
+ env.target
127
+ end
109
128
 
110
- # Explicitly invoke event on proxy or delegate to proxy
129
+ # Subscribe observer for event notifications
111
130
  #
112
- # @return [AsyncProxy]
131
+ # @example
132
+ # machine.subscribe(Observer.new(machine))
113
133
  #
114
134
  # @api public
115
- def async(method_name = nil, *args, &block)
116
- if method_name
117
- @async_proxy.method_missing(method_name, *args, &block)
118
- else
119
- @async_proxy
120
- end
135
+ def subscribe(*observers)
136
+ sync_exclusive { subscribers.subscribe(*observers) }
121
137
  end
122
138
 
123
139
  # Get current state
@@ -126,7 +142,7 @@ module FiniteMachine
126
142
  #
127
143
  # @api public
128
144
  def current
129
- state
145
+ sync_shared { state }
130
146
  end
131
147
 
132
148
  # Check if current state matches provided state
@@ -156,19 +172,19 @@ module FiniteMachine
156
172
  #
157
173
  # @api public
158
174
  def states
159
- sync_shared { events_chain.states }
175
+ sync_shared { events_map.states }
160
176
  end
161
177
 
162
178
  # Retireve all event names
163
179
  #
164
180
  # @example
165
- # fsm.event_names # => [:init, :start, :stop]
181
+ # fsm.events # => [:init, :start, :stop]
166
182
  #
167
183
  # @return [Array[Symbol]]
168
184
  #
169
185
  # @api public
170
- def event_names
171
- sync_shared { events_chain.events }
186
+ def events
187
+ events_map.events
172
188
  end
173
189
 
174
190
  # Checks if event can be triggered
@@ -186,7 +202,7 @@ module FiniteMachine
186
202
  # @api public
187
203
  def can?(*args)
188
204
  event_name = args.shift
189
- events_chain.can_perform?(event_name, current, *args)
205
+ events_map.can_perform?(event_name, current, *args)
190
206
  end
191
207
 
192
208
  # Checks if event cannot be triggered
@@ -232,7 +248,7 @@ module FiniteMachine
232
248
  #
233
249
  # @api private
234
250
  def valid_state?(event_name)
235
- current_states = events_chain.states_for(event_name)
251
+ current_states = events_map.states_for(event_name)
236
252
  current_states.any? { |state| state == current || state == ANY_STATE }
237
253
  end
238
254
 
@@ -284,9 +300,9 @@ module FiniteMachine
284
300
  #
285
301
  # @api public
286
302
  def trigger!(event_name, *data, &block)
287
- sync_exclusive do
288
- from = current # Save away current state
303
+ from = current # Save away current state
289
304
 
305
+ sync_exclusive do
290
306
  notify HookEvent::Before, event_name, from, *data
291
307
 
292
308
  status = try_trigger(event_name) do
@@ -307,6 +323,9 @@ module FiniteMachine
307
323
 
308
324
  status
309
325
  end
326
+ rescue Exception => err
327
+ self.state = from # rollback transition
328
+ raise err
310
329
  end
311
330
 
312
331
  # Trigger transition event without raising any errors
@@ -319,7 +338,7 @@ module FiniteMachine
319
338
  # @api public
320
339
  def trigger(event_name, *data, &block)
321
340
  trigger!(event_name, *data, &block)
322
- rescue InvalidStateError, TransitionError
341
+ rescue InvalidStateError, TransitionError, CallbackError
323
342
  false
324
343
  end
325
344
 
@@ -330,7 +349,7 @@ module FiniteMachine
330
349
  # @api private
331
350
  def transition!(event_name, *data, &block)
332
351
  from_state = current
333
- to_state = events_chain.move_to(event_name, from_state, *data)
352
+ to_state = events_map.move_to(event_name, from_state, *data)
334
353
 
335
354
  block.call(from_state, to_state) if block
336
355
 
@@ -371,8 +390,8 @@ module FiniteMachine
371
390
  def inspect
372
391
  sync_shared do
373
392
  "<##{self.class}:0x#{object_id.to_s(16)} @states=#{states}, " \
374
- "@events=#{event_names}, " \
375
- "@transitions=#{events_chain.state_transitions}>"
393
+ "@events=#{events}, " \
394
+ "@transitions=#{events_map.state_transitions}>"
376
395
  end
377
396
  end
378
397
 
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module FiniteMachine
4
4
  # A class responsible for converting transition arguments to states
@@ -7,114 +7,98 @@ module FiniteMachine
7
7
  #
8
8
  # @api private
9
9
  class StateParser
10
- include Threadable
11
-
12
- attr_threadsafe :attrs
13
-
14
10
  BLACKLIST = [:name, :if, :unless, :silent].freeze
15
11
 
16
- # Initialize a StateParser
12
+ # Extract states from user defined attributes
17
13
  #
18
14
  # @example
19
- # StateParpser.new({from: [:green, :blue], to: :red})
20
- #
21
- # @param [Hash] attrs
22
- #
23
- # @api public
24
- def initialize(attrs)
25
- @attrs = ensure_only_states!(attrs)
26
- freeze
27
- end
28
-
29
- # Extract states from attributes
15
+ # StateParser.parse({from: [:green, :blue], to: :red})
16
+ # # => {green: :red, green: :blue}
30
17
  #
31
18
  # @param [Proc] block
32
19
  #
33
- # @example
34
- # StateParpser.new(attr).parase_states
35
- #
36
20
  # @yield [Hash[Symbol]] the resolved states
37
21
  #
38
22
  # @return [Hash[Symbol]] the resolved states
39
23
  #
40
24
  # @api public
41
- def parse(&block)
42
- states = extract_states
25
+ def self.parse(attributes, &block)
26
+ attrs = ensure_only_states!(attributes)
27
+ states = extract_states(attrs)
43
28
  block ? states.each(&block) : states
44
29
  end
45
30
 
46
- # Check if attributes contain :from or :to key
47
- #
48
- # @example
49
- # parser = StateParser.new({from: :green, to: :red})
50
- # parser.contains_from_to_keys? # => true
51
- #
52
- # @example
53
- # parser = StateParser.new({:green => :red})
54
- # parser.contains_from_to_keys? # => false
31
+ # Extract only states from attributes
55
32
  #
56
- # @return [Boolean]
33
+ # @return [Hash[Symbol]]
57
34
  #
58
- # @api public
59
- def contains_from_to_keys?
60
- [:from, :to].any? { |key| attrs.keys.include?(key) }
35
+ # @api private
36
+ def self.ensure_only_states!(attrs)
37
+ attributes = attrs.dup
38
+ BLACKLIST.each { |key| attributes.delete(key) }
39
+ raise_not_enough_transitions unless attributes.any?
40
+ attributes
61
41
  end
42
+ private_class_method :ensure_only_states!
62
43
 
63
- # Return parser attributes
44
+ # Perform extraction of states from user supplied definitions
64
45
  #
65
- # @return [String]
46
+ # @return [Hash[Symbol]] the resolved states
66
47
  #
67
- # @api public
68
- def to_s
69
- attrs.to_s
48
+ # @api private
49
+ def self.extract_states(attrs)
50
+ if contains_from_to_keys?(attrs)
51
+ convert_from_to_attributes_to_states_hash(attrs)
52
+ else
53
+ convert_attributes_to_states_hash(attrs)
54
+ end
70
55
  end
56
+ private_class_method :extract_states
71
57
 
72
- # Return string representation
58
+ # Check if attributes contain :from or :to key
73
59
  #
74
- # @return [String]
60
+ # @example
61
+ # StateParser.contains_from_to_keys?({from: :green, to: :red})
62
+ # # => true
75
63
  #
76
- # @api public
77
- def inspect
78
- attributes = @attrs.map { |k, v| "#{k}:#{v}" }.join(', ')
79
- "<##{self.class} @attrs=#{attributes}>"
80
- end
81
-
82
- private
83
-
84
- # Extract only states from attributes
64
+ # @example
65
+ # StateParser.contains_from_to_keys?({:green => :red})
66
+ # # => false
85
67
  #
86
- # @return [Hash[Symbol]]
68
+ # @return [Boolean]
87
69
  #
88
- # @api private
89
- def ensure_only_states!(attrs)
90
- attributes = attrs.dup
91
- BLACKLIST.each { |key| attributes.delete(key) }
92
- raise_not_enough_transitions unless attributes.any?
93
- attributes
70
+ # @api public
71
+ def self.contains_from_to_keys?(attrs)
72
+ [:from, :to].any? { |key| attrs.keys.include?(key) }
94
73
  end
74
+ private_class_method :contains_from_to_keys?
95
75
 
96
76
  # Convert attrbiutes with :from, :to keys to states hash
97
77
  #
98
78
  # @return [Hash[Symbol]]
99
79
  #
100
80
  # @api private
101
- def convert_from_to_attributes_to_states_hash
81
+ def self.convert_from_to_attributes_to_states_hash(attrs)
102
82
  Array(attrs[:from] || ANY_STATE).reduce({}) do |hash, state|
103
83
  hash[state] = attrs[:to] || state
104
84
  hash
105
85
  end
106
86
  end
87
+ private_class_method :convert_from_to_attributes_to_states_hash
107
88
 
108
89
  # Convert collapsed attributes to states hash
109
90
  #
110
91
  # @example
111
- # parser = StateParser.new([:green, :red] => :yellow)
112
- # parser.parse # => {green: :yellow, red: :yellow}
92
+ # StateParser.convert_attributes_to_states_hash([:green, :red] => :yellow)
93
+ # # => {green: :yellow, red: :yellow}
94
+ #
95
+ # @param [Hash] attrs
96
+ # the attributes to convert to a simple hash
113
97
  #
114
98
  # @return [Hash[Symbol]]
115
99
  #
116
100
  # @api private
117
- def convert_attributes_to_states_hash
101
+ def self.convert_attributes_to_states_hash(attrs)
118
102
  attrs.reduce({}) do |hash, (k, v)|
119
103
  if k.respond_to?(:to_ary)
120
104
  k.each { |el| hash[el] = v }
@@ -124,31 +108,19 @@ module FiniteMachine
124
108
  hash
125
109
  end
126
110
  end
127
-
128
- # Perform extraction of states from user supplied definitions
129
- #
130
- # @return [Hash[Symbol]] the resolved states
131
- #
132
- # @api private
133
- def extract_states
134
- if contains_from_to_keys?
135
- convert_from_to_attributes_to_states_hash
136
- else
137
- convert_attributes_to_states_hash
138
- end
139
- end
111
+ private_class_method :convert_attributes_to_states_hash
140
112
 
141
113
  # Raise error when not enough transitions are provided
142
114
  #
143
115
  # @raise [NotEnoughTransitionsError]
144
- # if the event has not enough transition arguments
116
+ # if the event has no transitions
145
117
  #
146
118
  # @return [nil]
147
119
  #
148
120
  # @api private
149
- def raise_not_enough_transitions
150
- fail NotEnoughTransitionsError, "please provide state transitions for" \
151
- " '#{attrs.inspect}'"
121
+ def self.raise_not_enough_transitions
122
+ raise NotEnoughTransitionsError, 'please provide state transitions'
152
123
  end
124
+ private_class_method :raise_not_enough_transitions
153
125
  end # StateParser
154
126
  end # FiniteMachine