lib/state_fu/event.rb in davidlee-state-fu-0.3.1 vs lib/state_fu/event.rb in davidlee-state-fu-0.10.0
- old
+ new
@@ -1,16 +1,56 @@
module StateFu
class Event < StateFu::Sprocket
- attr_reader :origins, :targets, :requirements
+ attr_reader :origins, :targets, :requirements, :sequence
# called by Lathe when a new event is constructed
def initialize(machine, name, options={})
- @requirements = [].extend ArrayWithSymbolAccessor
+ @requirements = [].extend ArrayWithSymbolAccessor
+ @sequence = {}
super( machine, name, options )
end
+ #
+ # build a hash of target => [origins]
+ #
+ def add_to_sequence origin_states, target_state
+ origin_states = [origin_states].flatten
+ existing = origin_states.select {|s| target_for_origin(s) }
+ raise ArgumentError.new unless existing.empty? && !targets
+ @sequence[target_state] ||= []
+ [origin_states].flatten.each do |o|
+ @sequence[target_state] << o
+ end
+ @sequence
+ end
+
+ def target_for_origin origin_state
+ raise ArgumentError.new if origin_state.nil?
+ name = sequence.detect do |k,v|
+ v.include?(origin_state.to_sym)
+ end[0] rescue nil
+ machine.states[name] if name
+ # if t
+ # puts t.inspect + " <============================" if t
+ # puts "======================"
+ # puts origin_state.class
+ # puts origin_state.name rescue origin_state.inspect
+ # end
+ # machine.states[t.first] if t
+ end
+
+ def can_transition_from?(origin_state)
+ ( origins && origins.include?(origin_state.to_sym) && !targets.blank?) ||
+ target_for_origin(origin_state)
+ end
+
+ def sequence?
+ !sequence.empty?
+ end
+
+
# the names of all possible origin states
def origin_names
origins ? origins.map(&:to_sym) : nil
end
@@ -24,30 +64,15 @@
target_names.include?( state.to_sym )
end
# tests if a state or state name is in the list of origins
def from?( state )
- origin_names.include?( state.to_sym )
+ origin_names.include?( state.to_sym ) || target_for_origin(state)
end
-
- # internal method which accumulates states into an instance
- # variable with successive invocations.
- # ensures that calling #from multiple times adds to, rather than
- # clobbering, the list of origins / targets.
- def update_state_collection( ivar_name, *args)
- new_states = if [args].flatten == [:ALL]
- machine.states
- else
- machine.find_or_create_states_by_name( *args.flatten )
- end
- unless new_states.is_a?( Array )
- new_states = [new_states]
- end
- existing = instance_variable_get( ivar_name )
- # return existing if new_states.empty?
- new_value = ((existing || [] ) + new_states).flatten.compact.uniq.extend( StateArray )
- instance_variable_set( ivar_name, new_value )
+
+ def cycle?
+ origin && origin == target
end
# *adds to* the origin states given a list of symbols / States
def origins=( *args )
update_state_collection( '@origins', *args )
@@ -56,21 +81,10 @@
# *adds to* the target states given a list of symbols / States
def targets=( *args )
update_state_collection( '@targets', *args )
end
-
- # used internally
- #
- # <tt>complete?(:origins) # do we have origins?<tt>
- # <tt>complete? # do we have origins and targets?<tt>
- def complete?( field = nil )
- ( field && [field] || [:origins, :targets] ).
- map{ |s| send(s) }.
- all?{ |f| !(f.nil? || f.empty?) }
- end
-
# if there is a single state in #origins, returns it
def origin
origins && origins.length == 1 && origins[0] || nil
end
@@ -81,31 +95,46 @@
# a simple event has exactly one target, and any number of
# origins. It's simple because it can be triggered without
# supplying a target name - ie, <tt>go!<tt> vs <tt>go!(:home)<tt>
def simple?
- !! ( origins && target )
+ !! ( origins && target || sequence? )
end
+ def fireable?( transition )
+ transition.valid?(true)
+ end
+
+
+ #
+ # Lathe methods
+ #
+
+ # adds an event requirement.
+ # DOCME // TODO - can this be removed?
+ def requires( *args, &block )
+ lathe.requires( *args, &block )
+ end
+
# generally called from a Lathe. Sets the origin(s) and optionally
# target(s) - that is, if you supply the :to option, or a single element
# hash of origins => targets ) of the event. Both origins= and
# targets= are accumulators.
def from *args
options = args.extract_options!.symbolize_keys!
args.flatten!
- to = options.delete(:to)
+ to = options.delete(:to) || options.delete(:transitions_to)
if args.empty? && !to
if options.length == 1
- self.origins= options.keys[0]
- self.targets= options.values[0]
+ self.origins = options.keys[0]
+ self.targets = options.values[0]
else
raise options.inspect
end
else
- self.origins= *args
- self.targets= to unless to.nil?
+ self.origins = *args
+ self.targets = to unless to.nil?
end
end
# sets the target states for the event.
def to *args
@@ -113,21 +142,56 @@
args.flatten!
raise options.inspect unless options.empty?
self.targets= *args
end
- # is the event legal for the given binding, with the given
- # (optional) arguments?
- def fireable_by?( binding, *args )
- requirements.reject do |r|
- binding.evaluate_requirement_with_args( r, *args )
- end.empty?
+ alias_method :transitions_to, :to
+ alias_method :transitions_from, :from
+
+ #
+ # misc
+ #
+
+ # display nice and short
+ def inspect
+ s = self.to_s
+ s = s[0,s.length-1]
+ display_hooks = hooks.dup
+ display_hooks.each do |k,v|
+ display_hooks.delete(k) if v.empty?
+ end
+ unless display_hooks.empty?
+ s << " hooks=#{display_hooks.inspect}"
+ end
+ unless requirements.empty?
+ s << " requirements=#{requirements.inspect}"
+ end
+ s << " targets=#{targets.map(&:to_sym).inspect}" if targets
+ s << " origins=#{origins.map(&:to_sym).inspect}" if origins
+ s << ">"
+ s
end
- # adds an event requirement.
- # TODO MOREDOC
- def requires( *args, &block )
- lathe.requires( *args, &block )
+ private
+
+ # internal method which accumulates states into an instance
+ # variable with successive invocations.
+ # ensures that calling #from multiple times adds to, rather than
+ # clobbering, the list of origins / targets.
+ def update_state_collection( ivar_name, *args)
+ raise ArgumentError if sequence?
+ new_states = if [args].flatten == [:ALL]
+ machine.states
+ else
+ machine.find_or_create_states_by_name( *args.flatten )
+ end
+ unless new_states.is_a?( Array )
+ new_states = [new_states]
+ end
+ existing = instance_variable_get( ivar_name )
+ # return existing if new_states.empty?
+ new_value = ((existing || [] ) + new_states).flatten.compact.uniq.extend( StateArray )
+ instance_variable_set( ivar_name, new_value )
end
end
end