lib/fsm/machine.rb in simplificator-fsm-0.2.1 vs lib/fsm/machine.rb in simplificator-fsm-0.2.2
- old
+ new
@@ -1,12 +1,13 @@
module FSM
class Machine
- attr_accessor(:initial_state_name, :current_state_attribute_name, :states)
+ attr_accessor(:initial_state_name, :current_state_attribute_name, :states, :transitions)
def initialize(target_class)
@target_class = target_class
- self.states = {}
+ self.states = []
+ self.transitions = []
self.current_state_attribute_name = :state
end
def self.[](includer)
(@machines ||= {})[includer]
@@ -15,33 +16,28 @@
def self.[]=(*args)
(@machines ||= {})[args.first] = args.last
end
def state(name, options = {})
- raise "State is already defined: '#{name}'" if self.states[name]
- self.states[name] = State.new(name, options)
+ raise "State is already defined: '#{name}'" if self.state_for_name(name, true)
+ self.states << State.new(name, options)
self.initial_state_name=(name) unless self.initial_state_name
end
def initial_state_name=(value)
- raise UnknownState.new("Unknown state '#{value}'. Define states first with state(name)") unless self.states[value]
+ raise UnknownState.new("Unknown state '#{value}'. Define states first with state(name)") unless self.state_for_name(value, true)
@initial_state_name = value
end
def transition(name, from_name, to_name, options = {})
raise ArgumentError.new("name, from_name and to_name are required") if name.nil? || from_name.nil? || to_name.nil?
- raise UnknownState.new("Unknown source state '#{from}'") unless self.states[from_name]
- raise UnknownState.new("Unknown target state '#{to}'") unless self.states[to_name]
- from_state = self.states[from_name]
- to_state = self.states[to_name]
-
+ from_state = self.state_for_name(from_name)
+ to_state = self.state_for_name(to_name)
transition = Transition.new(name, from_state, to_state, options)
from_state.add_transition(transition)
-
- define_transition_method(name)
-
+ self.transitions << transition
end
def self.get_current_state_name(target)
value = target.send(Machine[target.class].current_state_attribute_name)
(value && value.is_a?(String)) ? value.intern : value
@@ -53,50 +49,59 @@
def reachable_states(target)
reachables = []
current_state_name = Machine.get_current_state_name(target)
- self.states.map do |name, state|
+ self.states.map do |state|
reachables += state.to_states if state.name == current_state_name
end
reachables
end
def available_transitions(target)
- self.states[Machine.get_current_state_name(target)].transitions.values
+ current_state_name = Machine.get_current_state_name(target)
+ state = state_for_name(current_state_name)
+ state.transitions.values
end
+
+ def build_transition_methods
+ names = self.transitions.map() {|transition| transition.name}.uniq
+ names.each do |name|
+ define_transition_method(name)
+ end
+ end
+
+ # Lookup a State by it's name
+ # raises ArgumentError if state can not be found unless quiet is set to true
+ def state_for_name(name, quiet = false)
+ state = self.states.detect() {|state| state.name == name}
+ raise ArgumentError.new("Unknonw state '#{name}'") unless quiet || state
+ state
+ end
+
private
+ # defines a transition method on the target class.
+ # this is the method triggering the state change.
+ #
def define_transition_method(name)
@target_class.instance_eval do
define_method(name) do |*args|
machine = Machine[self.class]
from_name = Machine.get_current_state_name(self)
- from_state = machine.states[from_name]
+ from_state = machine.state_for_name(from_name)
entry = from_state.transitions.detect() {|to_name, tr| tr.name == name}
transition = entry.last if entry
raise InvalidStateTransition.new("No transition with name '#{name}' defined from '#{from_name}'") unless transition
to_state = transition.to
from_state.exit(self)
transition.fire_event(self, args)
to_state.enter(self)
Machine.set_current_state_name(self, to_state.name)
- true
-
-
-
- #to_state = machine.states[to_name]
- #transition = from_state.transitions[to_name]
- #raise InvalidStateTransition.new("No transition defined from #{from_name} -> #{to_name}") unless transition
-
- #from_state.exit(self)
- #transition.fire_event(self, args)
- #to_state.enter(self)
- #Machine.set_current_state_name(self, to_name)
- #true # at the moment always return true ... as soon as we have guards or thelike this could be false as well
+ true # at the moment always return true ... as soon as we have guards or thelike this could be false as well
end
end
end
\ No newline at end of file