lib/state_fu/binding.rb in davidlee-state-fu-0.0.2 vs lib/state_fu/binding.rb in davidlee-state-fu-0.2.0
- old
+ new
@@ -15,15 +15,15 @@
# ensure state field is set up (in case we created this binding
# manually, instead of via Machine.bind!)
StateFu::Persistence.prepare_field( object.class, field_name )
# add a persister
@persister = StateFu::Persistence.for( self, field_name )
- Logger.info( "Persister added: #@persister ")
+ Logger.info( "Persister (#{@persister.class}) added: #{method_name} as field #{field_name}" )
# define event methods on self( binding ) and @object
StateFu::MethodFactory.new( self ).install!
-
+ machine.helpers.inject_into( self )
# StateFu::Persistence.prepare_field( @object.class, field_name )
end
alias_method :o, :object
alias_method :obj, :object
alias_method :model, :object
@@ -43,11 +43,15 @@
alias_method :at, :current_state
alias_method :now, :current_state
alias_method :state, :current_state
def current_state_name
- current_state.name
+ begin
+ current_state.name.to_sym
+ rescue NoMethodError
+ nil
+ end
end
alias_method :name, :current_state_name
alias_method :state_name, :current_state_name
alias_method :to_sym, :current_state_name
@@ -56,60 +60,70 @@
machine.events.select {|e| e.complete? && e.from?( current_state ) }.extend EventArray
end
alias_method :events_from_current_state, :events
# the subset of events() whose requirements for firing are met
- def valid_events
+ def valid_events( *args )
return nil unless current_state
- return [] unless current_state.exitable_by?( self )
- events.select {|e| e.fireable_by?( self ) }.extend EventArray
+ return [] unless current_state.exitable_by?( self, *args )
+ events.select {|e| e.fireable_by?( self, *args ) }.extend EventArray
end
- def invalid_events
- (events - valid_events).extend StateArray
+ def invalid_events( *args )
+ ( events - valid_events( *args ) ).extend StateArray
end
- def unmet_requirements_for(event, target)
+ def unmet_requirements_for( event, target )
raise NotImplementedError
end
# the counterpart to valid_events - the states we can arrive at in
# the firing of one event, taking into account event and state
# transition requirements
- def valid_next_states
- valid_transitions.values.flatten.uniq.extend StateArray
+ def valid_next_states( *args )
+ vt = valid_transitions( *args )
+ vt && vt.values.flatten.uniq.extend( StateArray )
end
def next_states
events.map(&:targets).compact.flatten.uniq.extend StateArray
end
- def invalid_next_states
- states - valid_states
- end
-
# returns a hash of { event => [states] } whose transition
# requirements are met
- def valid_transitions
- h = {}
- return nil if valid_events.nil?
- valid_events.each do |e|
+ def valid_transitions( *args )
+ h = {}
+ return nil unless ve = valid_events( *args )
+ ve.each do |e|
h[e] = e.targets.select do |s|
- s.enterable_by?( self )
+ s.enterable_by?( self, *args )
end
end
h
end
# initialize a new transition
def transition( event_or_array, *args, &block )
event, target = parse_destination( event_or_array )
StateFu::Transition.new( self, event, target, *args, &block )
end
- alias_method :fire, :transition
- alias_method :trigger, :transition
+ alias_method :fire, :transition
+ alias_method :fire_event, :transition
+ alias_method :trigger, :transition
+ alias_method :trigger_event, :transition
+ alias_method :begin_transition, :transition
+ def blank_mock_transition( *args, &block )
+ StateFu::MockTransition.new( self, nil, nil, *args, &block )
+ end
+
+ def mock_transition( event_or_array, *args, &block )
+ event, target = nil
+ event, target = parse_destination( event_or_array )
+ StateFu::MockTransition.new( self, event, target, *args, &block )
+ end
+
# sanitize args for fire! and fireable?
def parse_destination( event_or_array )
case event_or_array
when StateFu::Event, Symbol
event = event_or_array
@@ -123,24 +137,31 @@
[StateFu::State, Symbol, NilClass].include?( target.class )
[event, target]
end
# check that the event and target are "valid" (all requirements met)
- def fireable?( event_or_array )
+ def fireable?( event_or_array, *args )
event, target = parse_destination( event_or_array )
begin
t = transition( [event, target] )
!! t.requirements_met?
rescue InvalidTransition => e
nil
end
end
- alias_method :event?, :fireable?
- alias_method :trigger?, :fireable?
- alias_method :triggerable?, :fireable?
- alias_method :transition?, :fireable?
- alias_method :transitionable?,:fireable?
+ alias_method :event?, :fireable?
+ alias_method :event_fireable?, :fireable?
+ alias_method :can_fire?, :fireable?
+ alias_method :can_fire_event?, :fireable?
+ alias_method :trigger?, :fireable?
+ alias_method :triggerable?, :fireable?
+ alias_method :can_trigger?, :fireable?
+ alias_method :can_trigger_event?, :fireable?
+ alias_method :event_triggerable?, :fireable?
+ alias_method :transition?, :fireable?
+ alias_method :can_transition?, :fireable?
+ alias_method :transitionable?, :fireable?
# construct an event transition and fire it
def fire!( event_or_array, *args, &block)
event, target = parse_destination( event_or_array )
t = transition( [event, target], *args, &block )
@@ -151,66 +172,86 @@
alias_method :trigger!, :fire!
alias_method :transition!, :fire!
# evaluate a requirement depending whether it's a method or proc,
# and its arity - see helper.rb (ContextualEval) for the smarts
+
+ # TODO - enable requirement block / method to know the target
def evaluate_requirement( name )
- evaluate_named_proc_or_method( name )
+ puts "DEPRECATED: evaluate_requirement #{name}"
+ evaluate_requirement_with_args( name )
end
- def evaluate_requirement_message( name, dest )
- msg = machine.requirement_messages[name]
- if [String, NilClass].include?( msg.class )
- return msg
- else
- if dest.is_a?( StateFu::Transition )
- t = dest
- else
- event, target = parse_destination( event_or_array )
- t = transition( event, target )
- end
- case msg
- when Symbol
- t.evaluate_named_proc_or_method( msg )
- when Proc
- t.evaluate &msg
- end
- end
+ def evaluate_requirement_with_args( name, *args )
+ t = blank_mock_transition( *args )
+ evaluate_named_proc_or_method( name, t )
end
+ def evaluate_requirement_with_transition( name, t )
+ evaluate_named_proc_or_method( name, t )
+ end
+
+ # TODO SPECME HACKERY CRUFT FIXME THISSUCKS and needs *args
+ # def evaluate_requirement_message( name, dest )
+ # # puts "#{name} #{dest}"
+ # msg = machine.requirement_messages[name]
+ # if [String, NilClass].include?( msg.class )
+ # return msg
+ # else
+ # if dest.is_a?( StateFu::Transition )
+ # t = dest
+ # else
+ # event, target = parse_destination( event_or_array )
+ # t = transition( event, target )
+ # end
+ # case msg
+ # when Symbol, Proc
+ # puts t.class
+ # evaluate_named_proc_or_method( msg, t )
+ # # when Proc
+ # # t.evaluate &msg
+ # end
+ # end
+ # end
+
# if there is one simple event, return a transition for it
# else return nil
# TODO - not convinced about the method name / aliases - but next
# is reserved :/
def next_transition( *args, &block )
- return nil if valid_transitions.nil?
- next_transition_candidates = valid_transitions.select {|e, s| s.length == 1 }
+ vts = valid_transitions( *args )
+ return nil if vts.nil?
+ next_transition_candidates = vts.select {|e, s| s.length == 1 }
if next_transition_candidates.length == 1
nt = next_transition_candidates.first
evt = nt[0]
targ = nt[1][0]
return transition( [ evt, targ], *args, &block )
end
end
- def next_state
- next_transition && next_transition.target
+ def next_state( *args )
+ nt = next_transition( *args )
+ nt && nt.target
end
- alias_method :next_event, :next_transition
+ def next_event( *args )
+ nt = next_transition( *args )
+ nt && nt.event
+ end
# if there is a next_transition, create, fire & return it
# otherwise raise an InvalidTransition
def next!( *args, &block )
if t = next_transition( *args, &block )
t.fire!
t
else
- n = valid_transitions && valid_transitions.length
+ vts = valid_transitions( *args )
+ n = vts && vts.length
raise InvalidTransition.
- new( self, current_state, valid_transitions,
- "there are #{n} candidate transitions, need exactly 1")
+ new( self, current_state, vts, "there are #{n} candidate transitions, need exactly 1")
end
end
alias_method :next_state!, :next!
alias_method :next_event!, :next!
@@ -245,18 +286,32 @@
end
end
# if there is one possible cyclical event, evaluate its
# requirements (true/false), else nil
- def cycle?
- if t = cycle
+ def cycle?( *args )
+ if t = cycle( *args )
t.requirements_met?
end
end
# display something sensible that doesn't take up the whole screen
def inspect
- "#<#{self.class} ##{__id__} object_type=#{@object.class} method_name=#{method_name.inspect} field_name=#{persister.field_name.inspect} machine=#{@machine.inspect} options=#{options.inspect}>"
+ '|<= ' + self.class.to_s + ' ' +
+ attrs = [[:current_state, state_name.inspect],
+ [:object_type , @object.class],
+ [:method_name , method_name.inspect],
+ [:field_name , persister.field_name.inspect],
+ [:machine , machine.inspect]].
+ map {|x| x.join('=') }.join( " " ) + ' =>|'
+ end
+
+ def == other
+ if other.respond_to?(:to_sym) && current_state_name.is_a?(Symbol)
+ other.to_sym == current_state_name || super( other )
+ else
+ super( other )
+ end
end
end
end