lib/finite_machine/dsl.rb in finite_machine-0.1.0 vs lib/finite_machine/dsl.rb in finite_machine-0.2.0
- old
+ new
@@ -1,17 +1,22 @@
# encoding: utf-8
module FiniteMachine
+ # A generic DSL for describing the state machine
class GenericDSL
+ include Threadable
+
class << self
# @api private
attr_accessor :top_level
end
+ attr_threadsafe :machine
+
def initialize(machine)
- @machine = machine
+ self.machine = machine
end
def method_missing(method_name, *args, &block)
if @machine.respond_to?(method_name)
@machine.send(method_name, *args, &block)
@@ -19,44 +24,38 @@
super
end
end
def call(&block)
- instance_eval(&block)
+ sync_exclusive { instance_eval(&block) }
# top_level.instance_eval(&block)
end
end # GenericDSL
class DSL < GenericDSL
- attr_reader :machine
+ attr_threadsafe :defer
- attr_reader :defer
+ attr_threadsafe :initial_event
- attr_reader :initial_event
-
+ # Initialize top level DSL
+ #
+ # @api public
def initialize(machine)
super(machine)
machine.state = FiniteMachine::DEFAULT_STATE
- @defer = true
+ self.defer = true
end
# Define initial state
#
# @params [String, Hash] value
#
# @api public
def initial(value)
- if value.is_a?(String) || value.is_a?(Symbol)
- state, name = value, FiniteMachine::DEFAULT_EVENT_NAME
- @defer = false
- else
- state = value[:state]
- name = value.has_key?(:event) ? value[:event] : FiniteMachine::DEFAULT_EVENT_NAME
- @defer = value[:defer] || true
- end
- @initial_event = name
+ state, name, self.defer = parse(value)
+ self.initial_event = name
event = proc { event name, from: FiniteMachine::DEFAULT_STATE, to: state }
machine.events.call(&event)
end
def target(value)
@@ -85,50 +84,66 @@
end
# Error handler that throws exception when machine is in illegal state
#
# @api public
- def error
+ def handlers(&block)
+ machine.errors.call(&block)
end
- end # DSL
- class EventsDSL < GenericDSL
+ private
- attr_reader :machine
-
- def initialize(machine)
- super(machine)
+ # Parse initial options
+ #
+ # @params [String, Hash] value
+ #
+ # @return [Array[Symbol,String]]
+ #
+ # @api private
+ def parse(value)
+ if value.is_a?(String) || value.is_a?(Symbol)
+ [value, FiniteMachine::DEFAULT_EVENT_NAME, false]
+ else
+ [value[:state], value.fetch(:event, FiniteMachine::DEFAULT_EVENT_NAME),
+ !!value[:defer]]
+ end
end
+ end # DSL
+ class EventsDSL < GenericDSL
+
# Create event and associate transition
#
+ # @example
+ # event :go, :green => :yellow
+ # event :go, :green => :yellow, if: :lights_on?
+ #
+ # @return [Transition]
+ #
# @api public
def event(name, attrs = {}, &block)
- _transition = Transition.new(machine, attrs.merge!(name: name))
- add_transition(_transition)
- define_event(_transition)
+ sync_exclusive do
+ _transition = Transition.new(machine, attrs.merge!(name: name))
+ _transition.define
+ _transition.define_event
+ end
end
+ end # EventsDSL
- # Add transition
- #
- # @param [Transition] _transition
- #
- # @api private
- def add_transition(_transition)
- _transition.from.each do |from|
- machine.transitions[_transition.name][from] = _transition.to || from
- end
+ class ErrorsDSL < GenericDSL
+
+ def initialize(machine)
+ super(machine)
+ machine.error_handlers = []
end
- # Define event
+ # Add error handler
#
- # @param [String] name
- # @param [Transition] _transition
+ # @param [Array] exceptions
#
- # @api private
- def define_event(_transition)
- machine.class.__send__ :define_method, _transition.name do |*args, &block|
- transition(_transition, *args, &block)
- end
+ # @api public
+ def handle(*exceptions, &block)
+ machine.handle(*exceptions, &block)
end
- end # EventsDSL
+
+ end # ErrorsDSL
end # FiniteMachine