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