lib/state_fu/binding.rb in davidlee-state-fu-0.10.0 vs lib/state_fu/binding.rb in davidlee-state-fu-0.11.0

- old
+ new

@@ -25,14 +25,14 @@ # define event methods on this binding and its @object MethodFactory.new( self ).install! @machine.helpers.inject_into( self ) end - alias_method :o, :object - alias_method :obj, :object - alias_method :model, :object - alias_method :instance, :object + alias_method :o, :object + alias_method :obj, :object + alias_method :model, :object + alias_method :instance, :object alias_method :workflow, :machine alias_method :state_machine, :machine # @@ -68,16 +68,10 @@ e.can_transition_from? current_state end.extend EventArray end alias_method :events_from_current_state, :events - # the subset of events() whose requirements for firing are NOT met - # (with the arguments supplied, if any) - def invalid_events( *args ) - ( events - valid_events( *args ) ).extend StateArray - end - # all states which can be reached from the current_state. # Does not check transition requirements, etc. def next_states events.map(&:targets).compact.flatten.uniq.extend StateArray end @@ -93,52 +87,30 @@ def valid_transitions(*args) transitions.valid.with(*args) end def valid_next_states(*args) - transitions.with(*args).targets + valid_transitions(*args).targets end def valid_events(*args) - transitions.with(*args).events + valid_transitions(*args).events end + + def invalid_events(*args) + (events - valid_events(*args)).extend StateArray + end + # initializes a new Transition to the given destination, with the # given *args (to be passed to requirements and hooks). # # If a block is given, it yields the Transition or is executed in # its evaluation context, depending on the arity of the block. def transition( event_or_array, *args, &block ) return transitions.with(*args, &block).find(event_or_array) end - alias_method :fire, :transition - alias_method :fire_event, :transition - alias_method :trigger, :transition - alias_method :trigger_event, :transition - alias_method :begin_transition, :transition - - # check that the event and target are valid (all requirements are - # met) with the given (optional) arguments - def fireable?( event_or_array, *args ) - begin - return nil unless t = transition( event_or_array, *args ) - !! t.requirements_met? - rescue InvalidTransition => e - nil - end - end - - # construct an event transition and fire it, returning the transition. - # (which is == true if the transition completed successfully.) - def fire!( event_or_array, *args, &block) - # TODO rather than die, try to find the next valid transition and fire that - t = transition(event_or_array, *args, &block ) - t.fire! - end - alias_method :trigger!, :fire! - alias_method :transition!, :fire! - # # next_transition and friends: when there's exactly one valid move # # if there is exactly one legal & valid transition which can be fired with @@ -153,32 +125,32 @@ end # if there is exactly one state reachable via a transition which # is valid with the given optional arguments, return it. def next_state(*args, &block) - transitions.with(*args, &block).next_state + transitions.with(*args, &block).next_state end # if there is exactly one event which is valid with the given # optional arguments, return it def next_event( *args ) transitions.with(*args, &block).next_event end # if there is a next_transition, create, fire & return it - # otherwise raise an InvalidTransition + # otherwise raise an IllegalTransition def next!( *args, &block ) if t = next_transition( *args, &block ) t.fire! else raise TransitionNotFound.new( self, valid_transitions(*args), "Exactly 1 valid transition required.") end end alias_method :next_transition!, :next! alias_method :next_event!, :next! alias_method :next_state!, :next! - + # if there is a next_transition, return true / false depending on # whether its requirements are met # otherwise, nil def next?( *args, &block ) if t = next_transition( *args, &block ) @@ -200,17 +172,16 @@ transitions.cyclic.with(*args, &block).find(event_or_array) end end # if there is a single possible cycle() transition, fire and return it - # otherwise raise an InvalidTransition + # otherwise raise an IllegalTransition def cycle!(event_or_array=nil, *args, &block ) - if t = cycle(event_or_array, *args, &block ) + returning cycle(event_or_array, *args, &block ) do |t| + raise TransitionNotFound.new( self, transitions.cyclic.with(*args,&block), "Cannot cycle! unless there is exactly one cyclic event") \ + if t.nil? t.fire! - t - else - raise TransitionNotFound.new( self, transitions.cyclic.with(*args,&block), "Cannot cycle! unless there is exactly one cyclic event") end end # if there is one possible cyclical event, evaluate its # requirements (true/false), else nil @@ -231,17 +202,17 @@ persister.current_state=( machine.states[target] ) end # display something sensible that doesn't take up the whole screen def inspect - '|<= ' + self.class.to_s + ' ' + + '<#' + self.class.to_s + ' ' + attrs = [[:current_state, state_name.inspect], [:object_type , @object.class], [:method_name , method_name.inspect], [:field_name , field_name.inspect], - [:machine , machine.inspect]]. - map {|x| x.join('=') }.join( " " ) + ' =>|' + [:machine , machine.to_s]]. + map {|x| x.join('=') }.join( " " ) + '>' end # let's be == (and hence ===) the current_state_name as a symbol. # a nice little convenience. def == other @@ -265,34 +236,45 @@ end persister.reload self end - # This method is called from methods defined by MethodFactory. - # You don't want to call it directly. - def _event_method(action, event, *args) - target_or_options = args.shift - options = {} - case target_or_options - when Hash - options = target_or_options.symbolize_keys! - target = target_or_options.delete[:to] - when Symbol, String - target = target_or_options.to_sym - when nil - target = nil - end + def inspect + s = self.to_s + s = s[0,s.length-1] + s << " object=#{object}" + s << " current_state=#{current_state.to_sym.inspect rescue nil}" + s << " events=#{events.map(&:to_sym).inspect rescue nil}" + s << " machine=#{machine.to_s}" + s << ">" + s + end - case action - when :get_transition - transition [event, target], *args, &lambda {|t| t.options = options} - when :query_transition - fireable? [event, target], *args, &lambda {|t| t.options = options} - when :fire_transition - fire! [event, target], *args, &lambda {|t| t.options = options} - else - raise ArgumentError.new(action) - end + # These methods are called from methods defined by MethodFactory. + # You probably don't want to call them directly. + + # event_name + def find_transition(event, target=nil, *args) + target ||= args.last[:to].to_sym rescue nil + query = transitions.for_event(event).to(target).with(*args) + query.find || query.valid.singular || NilTransition.new + # transition = binding.transitions.with(*args).search([event, target]) + end + + # event_name? + def can_transition?(event, target=nil, *args) + begin + if t = find_transition(event, target, *args) + t.valid?(*args) + end + rescue IllegalTransition, UnknownTarget + nil + end + end + + # event_name! + def fire_transition!(event, target=nil, *args) + find_transition(event, target, *args).fire! end # # transition constructor #