finite_machine 0.11.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +80 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +679 -624
  5. data/lib/finite_machine.rb +35 -45
  6. data/lib/finite_machine/async_call.rb +5 -21
  7. data/lib/finite_machine/callable.rb +4 -4
  8. data/lib/finite_machine/catchable.rb +24 -14
  9. data/lib/finite_machine/choice_merger.rb +20 -20
  10. data/lib/finite_machine/const.rb +16 -0
  11. data/lib/finite_machine/definition.rb +3 -3
  12. data/lib/finite_machine/dsl.rb +98 -151
  13. data/lib/finite_machine/env.rb +4 -2
  14. data/lib/finite_machine/event_definition.rb +7 -15
  15. data/lib/finite_machine/{events_chain.rb → events_map.rb} +40 -53
  16. data/lib/finite_machine/hook_event.rb +60 -61
  17. data/lib/finite_machine/hooks.rb +44 -36
  18. data/lib/finite_machine/listener.rb +2 -2
  19. data/lib/finite_machine/logger.rb +5 -4
  20. data/lib/finite_machine/{event_queue.rb → message_queue.rb} +76 -32
  21. data/lib/finite_machine/observer.rb +71 -34
  22. data/lib/finite_machine/safety.rb +16 -14
  23. data/lib/finite_machine/state_definition.rb +3 -5
  24. data/lib/finite_machine/state_machine.rb +93 -76
  25. data/lib/finite_machine/state_parser.rb +55 -83
  26. data/lib/finite_machine/subscribers.rb +2 -2
  27. data/lib/finite_machine/threadable.rb +3 -1
  28. data/lib/finite_machine/transition.rb +34 -34
  29. data/lib/finite_machine/transition_builder.rb +23 -32
  30. data/lib/finite_machine/transition_event.rb +12 -11
  31. data/lib/finite_machine/two_phase_lock.rb +8 -6
  32. data/lib/finite_machine/undefined_transition.rb +5 -6
  33. data/lib/finite_machine/version.rb +2 -2
  34. metadata +58 -142
  35. data/.gitignore +0 -18
  36. data/.rspec +0 -5
  37. data/.ruby-gemset +0 -1
  38. data/.ruby-version +0 -1
  39. data/.travis.yml +0 -26
  40. data/Gemfile +0 -15
  41. data/Rakefile +0 -8
  42. data/assets/finite_machine_logo.png +0 -0
  43. data/examples/atm.rb +0 -45
  44. data/examples/bug_system.rb +0 -145
  45. data/finite_machine.gemspec +0 -23
  46. data/lib/finite_machine/async_proxy.rb +0 -30
  47. data/spec/integration/system_spec.rb +0 -95
  48. data/spec/spec_helper.rb +0 -33
  49. data/spec/unit/alias_target_spec.rb +0 -108
  50. data/spec/unit/async_events_spec.rb +0 -138
  51. data/spec/unit/callable/call_spec.rb +0 -113
  52. data/spec/unit/callbacks_spec.rb +0 -942
  53. data/spec/unit/can_spec.rb +0 -98
  54. data/spec/unit/choice_spec.rb +0 -331
  55. data/spec/unit/define_spec.rb +0 -55
  56. data/spec/unit/definition_spec.rb +0 -115
  57. data/spec/unit/event_names_spec.rb +0 -19
  58. data/spec/unit/event_queue_spec.rb +0 -52
  59. data/spec/unit/events_chain/add_spec.rb +0 -25
  60. data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
  61. data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
  62. data/spec/unit/events_chain/clear_spec.rb +0 -15
  63. data/spec/unit/events_chain/events_spec.rb +0 -18
  64. data/spec/unit/events_chain/inspect_spec.rb +0 -24
  65. data/spec/unit/events_chain/match_transition_spec.rb +0 -37
  66. data/spec/unit/events_chain/move_to_spec.rb +0 -48
  67. data/spec/unit/events_chain/states_for_spec.rb +0 -17
  68. data/spec/unit/events_spec.rb +0 -459
  69. data/spec/unit/handlers_spec.rb +0 -152
  70. data/spec/unit/hook_event/build_spec.rb +0 -15
  71. data/spec/unit/hook_event/eql_spec.rb +0 -36
  72. data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
  73. data/spec/unit/hook_event/initialize_spec.rb +0 -25
  74. data/spec/unit/hook_event/notify_spec.rb +0 -14
  75. data/spec/unit/hooks/call_spec.rb +0 -24
  76. data/spec/unit/hooks/clear_spec.rb +0 -16
  77. data/spec/unit/hooks/inspect_spec.rb +0 -17
  78. data/spec/unit/hooks/register_spec.rb +0 -22
  79. data/spec/unit/if_unless_spec.rb +0 -353
  80. data/spec/unit/initial_spec.rb +0 -222
  81. data/spec/unit/inspect_spec.rb +0 -17
  82. data/spec/unit/is_spec.rb +0 -55
  83. data/spec/unit/log_transitions_spec.rb +0 -30
  84. data/spec/unit/logger_spec.rb +0 -38
  85. data/spec/unit/respond_to_spec.rb +0 -38
  86. data/spec/unit/state_parser/inspect_spec.rb +0 -25
  87. data/spec/unit/state_parser/parse_spec.rb +0 -59
  88. data/spec/unit/states_spec.rb +0 -34
  89. data/spec/unit/subscribers_spec.rb +0 -42
  90. data/spec/unit/target_spec.rb +0 -225
  91. data/spec/unit/terminated_spec.rb +0 -95
  92. data/spec/unit/transition/check_conditions_spec.rb +0 -54
  93. data/spec/unit/transition/inspect_spec.rb +0 -25
  94. data/spec/unit/transition/matches_spec.rb +0 -23
  95. data/spec/unit/transition/states_spec.rb +0 -31
  96. data/spec/unit/transition/to_state_spec.rb +0 -27
  97. data/spec/unit/trigger_spec.rb +0 -22
  98. data/spec/unit/undefined_transition/eql_spec.rb +0 -17
  99. data/tasks/console.rake +0 -11
  100. data/tasks/coverage.rake +0 -11
  101. data/tasks/spec.rake +0 -29
@@ -1,33 +1,12 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "logger"
4
- require "thread"
5
- require "sync"
6
- require "forwardable"
7
-
8
- require "finite_machine/version"
9
- require "finite_machine/threadable"
10
- require "finite_machine/safety"
11
- require "finite_machine/callable"
12
- require "finite_machine/catchable"
13
- require "finite_machine/choice_merger"
14
- require "finite_machine/async_proxy"
15
- require "finite_machine/async_call"
16
- require "finite_machine/hook_event"
17
- require "finite_machine/env"
18
- require "finite_machine/event_queue"
19
- require "finite_machine/events_chain"
20
- require "finite_machine/logger"
21
- require "finite_machine/transition"
22
- require "finite_machine/transition_builder"
23
- require "finite_machine/transition_event"
24
- require "finite_machine/dsl"
25
- require "finite_machine/definition"
26
- require "finite_machine/state_machine"
27
- require "finite_machine/subscribers"
28
- require "finite_machine/observer"
29
- require "finite_machine/listener"
30
- require "finite_machine/two_phase_lock"
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"
31
10
 
32
11
  module FiniteMachine
33
12
  # Default state name
@@ -36,22 +15,19 @@ module FiniteMachine
36
15
  # Initial default event name
37
16
  DEFAULT_EVENT_NAME = :init
38
17
 
39
- # Describe any state transition
40
- ANY_STATE = :any
18
+ # Describe any transition state
19
+ ANY_STATE = Const.new(:any)
41
20
 
42
21
  # Describe any event name
43
- ANY_EVENT = :any_event
44
-
45
- # Returned when transition has successfully performed
46
- SUCCEEDED = 1
47
-
48
- # Returned when transition is cancelled in callback
49
- CANCELLED = 2
22
+ ANY_EVENT = Const.new(:any_event)
50
23
 
51
24
  # When transition between states is invalid
52
25
  TransitionError = Class.new(::StandardError)
53
26
 
54
- # Raised when transitining to invalid state
27
+ # When failed to process callback
28
+ CallbackError = Class.new(::StandardError)
29
+
30
+ # Raised when transitioning to invalid state
55
31
  InvalidStateError = Class.new(::ArgumentError)
56
32
 
57
33
  InvalidEventError = Class.new(::NoMethodError)
@@ -66,30 +42,44 @@ module FiniteMachine
66
42
  MissingInitialStateError = Class.new(::StandardError)
67
43
 
68
44
  # Raised when event queue is already dead
69
- EventQueueDeadError = Class.new(::StandardError)
45
+ MessageQueueDeadError = Class.new(::StandardError)
70
46
 
71
47
  # Raised when argument is already defined
72
48
  AlreadyDefinedError = Class.new(::ArgumentError)
73
49
 
74
- class << self
50
+ module ClassMethods
75
51
  attr_accessor :logger
76
52
 
77
- # TODO: this should instantiate system not the state machine
78
- # and then delegate calls to StateMachine instance etc...
53
+ # Initialize an instance of finite machine
79
54
  #
80
55
  # @example
81
- # FiniteMachine.define do
56
+ # FiniteMachine.new do
82
57
  # ...
83
58
  # end
84
59
  #
85
60
  # @return [FiniteMachine::StateMachine]
86
61
  #
87
62
  # @api public
88
- def define(*args, &block)
63
+ def new(*args, &block)
89
64
  StateMachine.new(*args, &block)
90
65
  end
91
- alias_method :new, :define
66
+
67
+ # A factory method for creating reusable FiniteMachine definitions
68
+ #
69
+ # @example
70
+ # TrafficLights = FiniteMachine.define
71
+ # lights_fm_a = TrafficLights.new
72
+ # lights_fm_b = TrafficLights.new
73
+ #
74
+ # @return [Class]
75
+ #
76
+ # @api public
77
+ def define(&block)
78
+ Class.new(Definition, &block)
79
+ end
92
80
  end
81
+
82
+ extend ClassMethods
93
83
  end # FiniteMachine
94
84
 
95
85
  FiniteMachine.logger = Logger.new(STDERR)
@@ -1,14 +1,13 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module FiniteMachine
4
- # An asynchronouse call representation
4
+ # An immutable asynchronouse call representation that wraps
5
+ # the {Callable} object
5
6
  #
6
- # Used internally by {EventQueue} to schedule events
7
+ # Used internally by {MessageQueue} to dispatch events
7
8
  #
8
9
  # @api private
9
10
  class AsyncCall
10
- include Threadable
11
-
12
11
  # Create asynchronous call instance
13
12
  #
14
13
  # @param [Object] context
@@ -19,15 +18,12 @@ module FiniteMachine
19
18
  # @example
20
19
  # AsyncCall.new(context, Callable.new(:method), :a, :b)
21
20
  #
22
- # @return [self]
23
- #
24
21
  # @api public
25
22
  def initialize(context, callable, *args, &block)
26
23
  @context = context
27
24
  @callable = callable
28
25
  @arguments = args.dup
29
26
  @block = block
30
- @mutex = Mutex.new
31
27
  freeze
32
28
  end
33
29
 
@@ -37,19 +33,7 @@ module FiniteMachine
37
33
  #
38
34
  # @api private
39
35
  def dispatch
40
- @mutex.synchronize do
41
- callable.call(context, *arguments, &block)
42
- end
36
+ @callable.call(@context, *@arguments, &@block)
43
37
  end
44
-
45
- protected
46
-
47
- attr_threadsafe :context
48
-
49
- attr_threadsafe :callable
50
-
51
- attr_threadsafe :arguments
52
-
53
- attr_threadsafe :block
54
38
  end # AsyncCall
55
39
  end # FiniteMachine
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module FiniteMachine
4
4
  # A generic interface for executing strings, symbol methods or procs.
@@ -20,7 +20,7 @@ module FiniteMachine
20
20
  #
21
21
  # @api public
22
22
  def invert
23
- lambda { |*args, &block| !call(*args, &block) }
23
+ ->(*args, &block) { !call(*args, &block) }
24
24
  end
25
25
 
26
26
  # Execute action
@@ -34,10 +34,10 @@ module FiniteMachine
34
34
  target.public_send(object.to_sym, *args, &block)
35
35
  when String
36
36
  string = args.empty? ? "-> { #{object} }" : "-> { #{object}(*#{args}) }"
37
- value = eval string
37
+ value = eval(string)
38
38
  target.instance_exec(&value)
39
39
  when ::Proc
40
- object.arity.zero? ? object.call : object.call(target, *args)
40
+ object.arity.zero? ? object.call : object.call(target, *args)
41
41
  else
42
42
  raise ArgumentError, "Unknown callable #{object}"
43
43
  end
@@ -1,9 +1,11 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module FiniteMachine
4
4
  # A mixin to allow for specifying error handlers
5
5
  module Catchable
6
-
6
+ # Extends object with error handling methods
7
+ #
8
+ # @api private
7
9
  def self.included(base)
8
10
  base.module_eval do
9
11
  attr_threadsafe :error_handlers, default: []
@@ -31,7 +33,7 @@ module FiniteMachine
31
33
  if block_given?
32
34
  options[:with] = block
33
35
  else
34
- raise ArgumentError, 'Need to provide error handler.'
36
+ raise ArgumentError, "Need to provide error handler."
35
37
  end
36
38
  end
37
39
  evaluate_exceptions(exceptions, options)
@@ -69,7 +71,7 @@ module FiniteMachine
69
71
  #
70
72
  # @api private
71
73
  def extract_const(class_name)
72
- class_name.split('::').reduce(FiniteMachine) do |constant, part|
74
+ class_name.split("::").reduce(FiniteMachine) do |constant, part|
73
75
  constant.const_get(part)
74
76
  end
75
77
  end
@@ -83,9 +85,9 @@ module FiniteMachine
83
85
  target.method(handler)
84
86
  when Proc
85
87
  if handler.arity.zero?
86
- proc { instance_exec(&handler) }
88
+ -> { instance_exec(&handler) }
87
89
  else
88
- proc { |_exception| instance_exec(_exception, &handler) }
90
+ ->(exception) { instance_exec(exception, &handler) }
89
91
  end
90
92
  end
91
93
  end
@@ -99,16 +101,24 @@ module FiniteMachine
99
101
  # @api private
100
102
  def evaluate_exceptions(exceptions, options)
101
103
  exceptions.each do |exception|
102
- key = if exception.is_a?(Class) && exception <= Exception
103
- exception.name
104
- elsif exception.is_a?(String)
105
- exception
106
- else
107
- raise ArgumentError, "#{exception} isn't an Exception"
108
- end
109
-
104
+ key = extract_exception_name(exception)
110
105
  error_handlers << [key, options[:with]]
111
106
  end
112
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
113
123
  end # Catchable
114
124
  end # FiniteMachine
@@ -1,44 +1,44 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "transition_builder"
2
4
 
3
5
  module FiniteMachine
4
6
  # A class responsible for merging choice options
5
7
  class ChoiceMerger
6
- include Threadable
7
-
8
- # The context where choice is executed
9
- attr_threadsafe :machine
10
-
11
- # The options passed in to the machine
12
- attr_threadsafe :options
13
-
14
8
  # Initialize a ChoiceMerger
15
9
  #
10
+ # @param [StateMachine] machine
11
+ # @param [String] name
12
+ # @param [Hash] transitions
13
+ # the transitions and attributes
14
+ #
16
15
  # @api private
17
- def initialize(machine, options)
18
- self.machine = machine
19
- self.options = options
16
+ def initialize(machine, name, transitions = {})
17
+ @machine = machine
18
+ @name = name
19
+ @transitions = transitions
20
20
  end
21
21
 
22
22
  # Create choice transition
23
23
  #
24
24
  # @example
25
- # event from: :green do
25
+ # event :stop, from: :green do
26
26
  # choice :yellow
27
27
  # end
28
28
  #
29
29
  # @param [Symbol] to
30
30
  # the to state
31
- # @param [Hash] attrs
31
+ # @param [Hash] conditions
32
+ # the conditions associated with this choice
32
33
  #
33
34
  # @return [FiniteMachine::Transition]
34
35
  #
35
36
  # @api public
36
- def choice(to, attrs = {})
37
- opts = options.dup
38
- opts.merge!(attrs)
39
- transition_builder = TransitionBuilder.new(machine, opts)
40
- transition_builder.call(options[:from] => to)
37
+ def choice(to, conditions = {})
38
+ transition_builder = TransitionBuilder.new(@machine, @name,
39
+ @transitions.merge(conditions))
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
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FiniteMachine
4
+ class Const
5
+ def initialize(name)
6
+ @name = name.to_s
7
+ freeze
8
+ end
9
+
10
+ def to_s
11
+ @name
12
+ end
13
+ alias to_str to_s
14
+ alias inspect to_s
15
+ end # Const
16
+ end # FiniteMachine
@@ -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 standalone state machine
@@ -38,7 +38,7 @@ module FiniteMachine
38
38
  # @api public
39
39
  def self.new(*args)
40
40
  context = self
41
- FiniteMachine.define(*args) do
41
+ FiniteMachine.new(*args) do
42
42
  context.deferreds.each { |d| d.call(self) }
43
43
  end
44
44
  end
@@ -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,59 +1,98 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "choice_merger"
4
+ require_relative "safety"
5
+ require_relative "transition_builder"
2
6
 
3
7
  module FiniteMachine
4
8
  # A generic DSL for describing the state machine
5
9
  class GenericDSL
6
- include Threadable
7
-
8
- class << self
9
- # @api private
10
- attr_accessor :top_level
10
+ # Initialize a generic DSL
11
+ #
12
+ # @api public
13
+ def initialize(machine, attrs)
14
+ @machine = machine
15
+ @attrs = attrs
11
16
  end
12
17
 
13
- attr_threadsafe :machine
14
-
15
- attr_threadsafe :attrs
18
+ # Expose any state constant
19
+ # @api public
20
+ def any_state
21
+ ANY_STATE
22
+ end
16
23
 
17
- # Initialize a generic DSL
18
- #
24
+ # Expose any event constant
19
25
  # @api public
20
- def initialize(machine, attrs = {})
21
- self.attrs = attrs
22
- self.machine = machine
26
+ def any_event
27
+ ANY_EVENT
23
28
  end
24
29
 
25
30
  # Delegate attributes to machine instance
26
31
  #
27
32
  # @api private
28
33
  def method_missing(method_name, *args, &block)
29
- if machine.respond_to?(method_name)
30
- machine.send(method_name, *args, &block)
34
+ if @machine.respond_to?(method_name)
35
+ @machine.send(method_name, *args, &block)
31
36
  else
32
37
  super
33
38
  end
34
39
  end
35
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
+
48
+ # Configure state machine properties
49
+ #
50
+ # @api private
36
51
  def call(&block)
37
- sync_exclusive { instance_eval(&block) }
38
- # top_level.instance_eval(&block)
52
+ instance_eval(&block)
39
53
  end
40
54
  end # GenericDSL
41
55
 
42
56
  # A class responsible for adding state machine specific dsl
43
57
  class DSL < GenericDSL
44
- attr_threadsafe :defer
45
-
46
- attr_threadsafe :initial_event
58
+ include Safety
47
59
 
48
60
  # Initialize top level DSL
49
61
  #
50
62
  # @api public
51
- def initialize(machine, attrs = {})
63
+ def initialize(machine, attrs)
52
64
  super(machine, attrs)
53
- machine.state = FiniteMachine::DEFAULT_STATE
54
- self.defer = true
55
65
 
56
- initialize_attrs
66
+ @machine.state = FiniteMachine::DEFAULT_STATE
67
+ @defer_initial = true
68
+ @silent_initial = true
69
+
70
+ initial(@attrs[:initial]) if @attrs[:initial]
71
+ terminal(@attrs[:terminal]) if @attrs[:terminal]
72
+ log_transitions(@attrs.fetch(:log_transitions, false))
73
+ end
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
57
96
  end
58
97
 
59
98
  # Define initial state
@@ -89,9 +128,9 @@ module FiniteMachine
89
128
  # @api public
90
129
  def initial(value, options = {})
91
130
  state = (value && !value.is_a?(Hash)) ? value : raise_missing_state
92
- name, self.defer, silent = *parse_initial(options)
93
- self.initial_event = name
94
- event(name, FiniteMachine::DEFAULT_STATE => state, silent: silent)
131
+ name, @defer_initial, @silent_initial = *parse_initial(options)
132
+ @initial_event = name
133
+ event(name, FiniteMachine::DEFAULT_STATE => state, silent: @silent_initial)
95
134
  end
96
135
 
97
136
  # Trigger initial event
@@ -100,51 +139,8 @@ module FiniteMachine
100
139
  #
101
140
  # @api private
102
141
  def trigger_init
103
- public_send(:"#{initial_event}") unless defer
104
- end
105
-
106
- # Attach state machine to an object
107
- #
108
- # This allows state machine to initiate events in the context
109
- # of a particular object
110
- #
111
- # @example
112
- # FiniteMachine.define do
113
- # target :red
114
- # end
115
- #
116
- # @param [Object] object
117
- #
118
- # @return [FiniteMachine::StateMachine]
119
- #
120
- # @api public
121
- def target(object = nil)
122
- if object.nil?
123
- env.target
124
- else
125
- env.target = object
126
- end
127
- end
128
-
129
- # Use alternative name for target
130
- #
131
- # @example
132
- # target_alias: :car
133
- #
134
- # callbacks {
135
- # on_transition do |event|
136
- # car.state = event.to
137
- # end
138
- # }
139
- #
140
- # @param [Symbol] alias_name
141
- # the name to alias target to
142
- #
143
- # @return [FiniteMachine::StateMachine]
144
- #
145
- # @api public
146
- def alias_target(alias_name)
147
- env.aliases << alias_name.to_sym
142
+ method = @silent_initial ? :transition : :trigger
143
+ @machine.public_send(method, :"#{@initial_event}") unless @defer_initial
148
144
  end
149
145
 
150
146
  # Define terminal state
@@ -156,42 +152,47 @@ module FiniteMachine
156
152
  #
157
153
  # @api public
158
154
  def terminal(*values)
159
- self.final_state = values
155
+ self.terminal_states = values
160
156
  end
161
157
 
162
- # Define state machine events
158
+ # Create event and associate transition
163
159
  #
164
160
  # @example
165
- # events do
166
- # event :start, :red => :green
167
- # end
161
+ # event :go, :green => :yellow
162
+ # event :go, :green => :yellow, if: :lights_on?
168
163
  #
169
- # @return [FiniteMachine::StateMachine]
164
+ # @param [Symbol] name
165
+ # the event name
166
+ # @param [Hash] transitions
167
+ # the event transitions and conditions
168
+ #
169
+ # @return [Transition]
170
170
  #
171
171
  # @api public
172
- def events(&block)
173
- events_dsl.call(&block)
172
+ def event(name, transitions = {}, &block)
173
+ detect_event_conflict!(name) if machine.auto_methods?
174
+
175
+ if block_given?
176
+ merger = ChoiceMerger.new(machine, name, transitions)
177
+ merger.instance_eval(&block)
178
+ else
179
+ transition_builder = TransitionBuilder.new(machine, name, transitions)
180
+ transition_builder.call(transitions)
181
+ end
174
182
  end
175
183
 
176
- # Define state machine callbacks
184
+ # Add error handler
177
185
  #
178
- # @example
179
- # callbacks do
180
- # on_enter :green do |event| ... end
181
- # end
186
+ # @param [Array] exceptions
182
187
  #
183
- # @return [FiniteMachine::Observer]
188
+ # @example
189
+ # handle InvalidStateError, with: :log_errors
184
190
  #
185
- # @api public
186
- def callbacks(&block)
187
- observer.call(&block)
188
- end
189
-
190
- # Error handler that throws exception when machine is in illegal state
191
+ # @return [Array[Exception]]
191
192
  #
192
193
  # @api public
193
- def handlers(&block)
194
- errors_dsl.call(&block)
194
+ def handle(*exceptions, &block)
195
+ @machine.handle(*exceptions, &block)
195
196
  end
196
197
 
197
198
  # Decide whether to log transitions
@@ -203,16 +204,6 @@ module FiniteMachine
203
204
 
204
205
  private
205
206
 
206
- # Initialize state machine properties based off attributes
207
- #
208
- # @api private
209
- def initialize_attrs
210
- attrs[:initial] && initial(attrs[:initial])
211
- attrs[:target] && target(attrs[:target])
212
- attrs[:terminal] && terminal(attrs[:terminal])
213
- log_transitions(attrs.fetch(:log_transitions, false))
214
- end
215
-
216
207
  # Parse initial options
217
208
  #
218
209
  # @param [Hash] options
@@ -236,52 +227,8 @@ module FiniteMachine
236
227
  #
237
228
  # @api private
238
229
  def raise_missing_state
239
- fail MissingInitialStateError,
240
- 'Provide state to transition :to for the initial event'
230
+ raise MissingInitialStateError,
231
+ "Provide state to transition :to for the initial event"
241
232
  end
242
233
  end # DSL
243
-
244
- # A DSL for describing events
245
- class EventsDSL < GenericDSL
246
- include Safety
247
- # Create event and associate transition
248
- #
249
- # @example
250
- # event :go, :green => :yellow
251
- # event :go, :green => :yellow, if: :lights_on?
252
- #
253
- # @return [Transition]
254
- #
255
- # @api public
256
- def event(name, attrs = {}, &block)
257
- sync_exclusive do
258
- detect_event_conflict!(name)
259
- attributes = attrs.merge!(name: name)
260
- if block_given?
261
- merger = ChoiceMerger.new(machine, attributes)
262
- merger.instance_eval(&block)
263
- else
264
- transition_builder = TransitionBuilder.new(machine, attributes)
265
- transition_builder.call(attrs)
266
- end
267
- end
268
- end
269
- end # EventsDSL
270
-
271
- # A DSL for describing error conditions
272
- class ErrorsDSL < GenericDSL
273
- # Add error handler
274
- #
275
- # @param [Array] exceptions
276
- #
277
- # @example
278
- # handle InvalidStateError, with: :log_errors
279
- #
280
- # @return [Array[Exception]]
281
- #
282
- # @api public
283
- def handle(*exceptions, &block)
284
- machine.handle(*exceptions, &block)
285
- end
286
- end # ErrorsDSL
287
234
  end # FiniteMachine