finite_machine 0.11.3 → 0.12.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 (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
data/Rakefile CHANGED
@@ -4,5 +4,9 @@ require "bundler/gem_tasks"
4
4
 
5
5
  FileList['tasks/**/*.rake'].each(&method(:import))
6
6
 
7
+ jruby = RUBY_ENGINE == 'ruby'
8
+ specs = ['spec']
9
+ specs << 'spec:perf' if jruby
10
+
7
11
  desc 'Run all specs'
8
- task ci: %w[ spec spec:perf ]
12
+ task ci: specs
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/finite_machine'
4
+ require 'ruby-prof'
5
+
6
+ result = RubyProf.profile do
7
+ FiniteMachine.new
8
+ end
9
+
10
+ printer = RubyProf::FlatPrinter.new(result)
11
+ printer.print($stdout)
@@ -1,21 +1,28 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'finite_machine'
3
+ require_relative '../lib/finite_machine'
4
4
 
5
- 3.times do
5
+ 5.times do
6
6
  puts
7
7
 
8
8
  GC.start
9
9
 
10
- before = GC.stat
11
- p ObjectSpace.count_objects
10
+ gc_before = GC.stat
11
+ objects_before = ObjectSpace.count_objects
12
+ p objects_before[:T_OBJECT]
12
13
 
13
14
  1_000.times do
14
- FiniteMachine.define
15
+ FiniteMachine.new do
16
+ initial :green
17
+
18
+ events { event :slow, :green => :yellow }
19
+ end
15
20
  end
16
21
 
17
- p ObjectSpace.count_objects
18
- after = GC.stat
22
+ objects_after = ObjectSpace.count_objects
23
+ gc_after = GC.stat
24
+ p objects_after[:T_OBJECT]
19
25
 
20
- p "GC count: #{after[:count] - before[:count]}"
26
+ p "GC count: #{gc_after[:count] - gc_before[:count]}"
27
+ p "Objects count: #{objects_after[:T_OBJECT] - objects_before[:T_OBJECT]}"
21
28
  end
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  lib = File.expand_path('../lib', __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'finite_machine/version'
@@ -10,14 +9,22 @@ Gem::Specification.new do |spec|
10
9
  spec.email = [""]
11
10
  spec.description = %q{A minimal finite state machine with a straightforward syntax. You can quickly model states, add callbacks and use object-oriented techniques to integrate with ORMs.}
12
11
  spec.summary = %q{A minimal finite state machine with a straightforward syntax.}
13
- spec.homepage = "http://peter-murach.github.io/finite_machine/"
12
+ spec.homepage = "http://piotrmurach.github.io/finite_machine/"
14
13
  spec.license = "MIT"
15
14
 
16
- spec.files = `git ls-files`.split($/)
15
+ spec.files = Dir['{lib,spec,examples,benchmarks}/**/*.rb']
16
+ spec.files += Dir['tasks/*', 'finite_machine.gemspec']
17
+ spec.files += Dir['README.md', 'CHANGELOG.md', 'LICENSE.txt', 'Rakefile']
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
20
  spec.require_paths = ["lib"]
20
21
 
22
+ spec.required_ruby_version = '>= 2.0.0'
23
+
24
+ spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
25
+
21
26
  spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.1'
28
+ spec.add_development_dependency 'rspec-benchmark', '~> 0.4.0'
22
29
  spec.add_development_dependency 'rake'
23
30
  end
@@ -1,33 +1,12 @@
1
- # encoding: utf-8
2
-
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/message_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"
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
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)
@@ -74,21 +50,33 @@ module FiniteMachine
74
50
  class << self
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)
89
- StateMachine.new(*args, &block)
63
+ def new(*args, **options, &block)
64
+ StateMachine.new(*args, **options, &block)
65
+ end
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)
90
79
  end
91
- alias_method :new, :define
92
80
  end
93
81
  end # FiniteMachine
94
82
 
@@ -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: []
@@ -1,43 +1,43 @@
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
42
  alias_method :default, :choice
43
43
  end # ChoiceMerger
@@ -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
@@ -1,59 +1,68 @@
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
+ # Configure state machine properties
42
+ #
43
+ # @api private
36
44
  def call(&block)
37
- sync_exclusive { instance_eval(&block) }
38
- # top_level.instance_eval(&block)
45
+ instance_eval(&block)
39
46
  end
40
47
  end # GenericDSL
41
48
 
42
49
  # A class responsible for adding state machine specific dsl
43
50
  class DSL < GenericDSL
44
- attr_threadsafe :defer
45
-
46
- attr_threadsafe :initial_event
51
+ include Safety
47
52
 
48
53
  # Initialize top level DSL
49
54
  #
50
55
  # @api public
51
- def initialize(machine, attrs = {})
56
+ def initialize(machine, **attrs)
52
57
  super(machine, attrs)
53
- machine.state = FiniteMachine::DEFAULT_STATE
54
- self.defer = true
55
58
 
56
- initialize_attrs
59
+ @machine.state = FiniteMachine::DEFAULT_STATE
60
+ @defer_initial = true
61
+ @silent_initial = true
62
+
63
+ initial(@attrs[:initial]) if @attrs[:initial]
64
+ terminal(@attrs[:terminal]) if @attrs[:terminal]
65
+ log_transitions(@attrs.fetch(:log_transitions, false))
57
66
  end
58
67
 
59
68
  # Define initial state
@@ -87,11 +96,11 @@ module FiniteMachine
87
96
  # @return [StateMachine]
88
97
  #
89
98
  # @api public
90
- def initial(value, options = {})
99
+ def initial(value, **options)
91
100
  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)
101
+ name, @defer_initial, @silent_initial = *parse_initial(options)
102
+ @initial_event = name
103
+ event(name, FiniteMachine::DEFAULT_STATE => state, silent: @silent_initial)
95
104
  end
96
105
 
97
106
  # Trigger initial event
@@ -100,51 +109,8 @@ module FiniteMachine
100
109
  #
101
110
  # @api private
102
111
  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
112
+ method = @silent_initial ? :transition : :trigger
113
+ @machine.public_send(method, :"#{@initial_event}") unless @defer_initial
148
114
  end
149
115
 
150
116
  # Define terminal state
@@ -159,39 +125,44 @@ module FiniteMachine
159
125
  self.final_state = values
160
126
  end
161
127
 
162
- # Define state machine events
128
+ # Create event and associate transition
163
129
  #
164
130
  # @example
165
- # events do
166
- # event :start, :red => :green
167
- # end
131
+ # event :go, :green => :yellow
132
+ # event :go, :green => :yellow, if: :lights_on?
168
133
  #
169
- # @return [FiniteMachine::StateMachine]
134
+ # @param [Symbol] name
135
+ # the event name
136
+ # @param [Hash] transitions
137
+ # the event transitions and conditions
138
+ #
139
+ # @return [Transition]
170
140
  #
171
141
  # @api public
172
- def events(&block)
173
- events_dsl.call(&block)
142
+ def event(name, transitions = {}, &block)
143
+ detect_event_conflict!(name) if machine.auto_methods?
144
+
145
+ if block_given?
146
+ merger = ChoiceMerger.new(machine, name, transitions)
147
+ merger.instance_eval(&block)
148
+ else
149
+ transition_builder = TransitionBuilder.new(machine, name, transitions)
150
+ transition_builder.call(transitions)
151
+ end
174
152
  end
175
153
 
176
- # Define state machine callbacks
154
+ # Add error handler
177
155
  #
178
- # @example
179
- # callbacks do
180
- # on_enter :green do |event| ... end
181
- # end
156
+ # @param [Array] exceptions
182
157
  #
183
- # @return [FiniteMachine::Observer]
158
+ # @example
159
+ # handle InvalidStateError, with: :log_errors
184
160
  #
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
161
+ # @return [Array[Exception]]
191
162
  #
192
163
  # @api public
193
- def handlers(&block)
194
- errors_dsl.call(&block)
164
+ def handle(*exceptions, &block)
165
+ @machine.handle(*exceptions, &block)
195
166
  end
196
167
 
197
168
  # Decide whether to log transitions
@@ -203,16 +174,6 @@ module FiniteMachine
203
174
 
204
175
  private
205
176
 
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
177
  # Parse initial options
217
178
  #
218
179
  # @param [Hash] options
@@ -240,48 +201,4 @@ module FiniteMachine
240
201
  'Provide state to transition :to for the initial event'
241
202
  end
242
203
  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
204
  end # FiniteMachine