lib/state_fu/event.rb in davidlee-state-fu-0.2.0 vs lib/state_fu/event.rb in davidlee-state-fu-0.3.1

- old
+ new

@@ -1,71 +1,97 @@ module StateFu class Event < StateFu::Sprocket attr_reader :origins, :targets, :requirements - # - # TODO - event guards - # - + # called by Lathe when a new event is constructed def initialize(machine, name, options={}) @requirements = [].extend ArrayWithSymbolAccessor super( machine, name, options ) end + # the names of all possible origin states def origin_names origins ? origins.map(&:to_sym) : nil end + # the names of all possible target states def target_names targets ? targets.map(&:to_sym) : nil end + # tests if a state or state name is in the list of targets def to?( state ) 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 ) end - def origins=( *args ) - if [args].flatten == [:ALL] - @origins = machine.states - else - @origins = machine.find_or_create_states_by_name( *args.flatten ).extend( StateArray ) + # 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 ) end + # *adds to* the origin states given a list of symbols / States + def origins=( *args ) + update_state_collection( '@origins', *args ) + end + + # *adds to* the target states given a list of symbols / States def targets=( *args ) - if [args].flatten == [:ALL] - @targets = machine.states - else - @targets = machine.find_or_create_states_by_name( *args.flatten ).extend( StateArray ) - end + update_state_collection( '@targets', *args ) end - # complete?(:origins) # do we have an origins? - # complete? # do we have an origins and targets? + + # 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 + # if there is a single state in #origins, returns it def target targets && targets.length == 1 && targets[0] || nil end + # 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 ) 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) if args.empty? && !to @@ -79,20 +105,29 @@ self.origins= *args self.targets= to unless to.nil? end end + # sets the target states for the event. def to *args options = args.extract_options!.symbolize_keys! 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? + end + + # adds an event requirement. + # TODO MOREDOC + def requires( *args, &block ) + lathe.requires( *args, &block ) end end end