lib/fsm/machine.rb in simplificator-fsm-0.1.0 vs lib/fsm/machine.rb in simplificator-fsm-0.2.0

- old
+ new

@@ -1,13 +1,13 @@ module FSM class Machine - attr_accessor(:current_state, :states, :state_attribute) + attr_accessor(:initial_state_name, :current_state_attribute_name, :states) - def initialize(klass) - @klass = klass + def initialize(target_class) + @target_class = target_class self.states = {} - self.state_attribute = :state + self.current_state_attribute_name = :state end def self.[](includer) (@machines ||= {})[includer] end @@ -17,79 +17,72 @@ end def state(name, options = {}) raise "State is already defined: '#{name}'" if self.states[name] self.states[name] = State.new(name, options) - initial(name) unless self.current_state - nil + self.initial_state_name=(name) unless self.initial_state_name end - def initial(name) - self.current_state = self.states[name] - raise UnknownState.new("Unknown state '#{name}'. Define states first with state(name)") unless self.current_state - nil + def initial_state_name=(value) + raise UnknownState.new("Unknown state '#{value}'. Define states first with state(name)") unless self.states[value] + @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] - define_transition_method(name, to_name) + from_state = self.states[from_name] to_state = self.states[to_name] + transition = Transition.new(name, from_state, to_state, options) - from_state.transitions[to_state.name] = transition - nil + from_state.add_transition(transition) + + define_transition_method(name, to_name) + end - def attribute(name) - self.state_attribute = name + 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 end - def reachable_states() - self.states.map do |name, state| - state.to_states if state == current_state - end.flatten.compact + def self.set_current_state_name(target, value) + target.send("#{Machine[target.class].current_state_attribute_name}=", value) end + - def available_transitions() - current_state.transitions.values + def reachable_states(target) + reachables = [] + current_state_name = Machine.get_current_state_name(target) + self.states.map do |name, state| + reachables += state.to_states if state.name == current_state_name + end + reachables end - - def post_process - define_state_attribute_methods(self.state_attribute) + def available_transitions(target) + self.states[Machine.get_current_state_name(target)].transitions.values end - - private - def define_state_attribute_methods(name) - @klass.instance_eval do - define_method("#{name}") do - Machine[self.class].current_state.name - end - - define_method("#{name}=") do |value| - Machine[self.class].current_state = Machine[self.class].states[value] - raise("Unknown State #{value}") unless Machine[self.class].current_state - end - end - end - def define_transition_method(name, to_name) - @klass.instance_eval do + @target_class.instance_eval do define_method(name) do |*args| - from_state = Machine[self.class].current_state - to_state = Machine[self.class].states[to_name] + machine = Machine[self.class] + from_name = Machine.get_current_state_name(self) + from_state = machine.states[from_name] + to_state = machine.states[to_name] transition = from_state.transitions[to_name] - raise InvalidStateTransition.new("No transition defined from #{from_state.name} -> #{to_state.name}") unless transition + 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[self.class].current_state = to_state + 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 end end end \ No newline at end of file