davidlee-state-fu 0.3.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. data/README.textile +124 -34
  2. data/Rakefile +36 -30
  3. data/lib/no_stdout.rb +1 -1
  4. data/lib/state-fu.rb +9 -8
  5. data/lib/state_fu/active_support_lite/array/access.rb +12 -5
  6. data/lib/state_fu/active_support_lite/array/conversions.rb +10 -4
  7. data/lib/state_fu/active_support_lite/array/extract_options.rb +5 -4
  8. data/lib/state_fu/active_support_lite/array/grouping.rb +7 -4
  9. data/lib/state_fu/active_support_lite/array/random_access.rb +4 -3
  10. data/lib/state_fu/active_support_lite/array/wrapper.rb +4 -3
  11. data/lib/state_fu/active_support_lite/array.rb +3 -1
  12. data/lib/state_fu/active_support_lite/blank.rb +18 -9
  13. data/lib/state_fu/active_support_lite/cattr_reader.rb +4 -1
  14. data/lib/state_fu/active_support_lite/keys.rb +8 -3
  15. data/lib/state_fu/active_support_lite/misc.rb +6 -4
  16. data/lib/state_fu/active_support_lite/module/delegation.rb +130 -0
  17. data/lib/state_fu/active_support_lite/module.rb +1 -0
  18. data/lib/state_fu/active_support_lite/object.rb +5 -2
  19. data/lib/state_fu/active_support_lite/string.rb +6 -1
  20. data/lib/state_fu/active_support_lite/symbol.rb +2 -1
  21. data/lib/state_fu/applicable.rb +41 -0
  22. data/lib/state_fu/{helper.rb → arrays.rb} +45 -121
  23. data/lib/state_fu/binding.rb +136 -159
  24. data/lib/state_fu/core_ext.rb +78 -10
  25. data/lib/state_fu/event.rb +112 -48
  26. data/lib/state_fu/exceptions.rb +80 -34
  27. data/lib/state_fu/executioner.rb +149 -0
  28. data/lib/state_fu/has_options.rb +16 -0
  29. data/lib/state_fu/hooks.rb +21 -16
  30. data/lib/state_fu/interface.rb +80 -83
  31. data/lib/state_fu/lathe.rb +361 -148
  32. data/lib/state_fu/logger.rb +122 -45
  33. data/lib/state_fu/machine.rb +60 -32
  34. data/lib/state_fu/method_factory.rb +180 -72
  35. data/lib/state_fu/methodical.rb +17 -0
  36. data/lib/state_fu/persistence/active_record.rb +6 -1
  37. data/lib/state_fu/persistence/attribute.rb +1 -0
  38. data/lib/state_fu/persistence/base.rb +8 -6
  39. data/lib/state_fu/persistence.rb +94 -23
  40. data/lib/state_fu/sprocket.rb +26 -11
  41. data/lib/state_fu/state.rb +8 -27
  42. data/lib/state_fu/transition.rb +207 -98
  43. data/lib/state_fu/transition_query.rb +214 -0
  44. data/lib/state_fu.rb +1 -0
  45. data/lib/tasks/spec_last.rake +46 -0
  46. data/lib/tasks/state_fu.rake +57 -0
  47. data/lib/vizier.rb +61 -61
  48. data/spec/custom_formatter.rb +49 -0
  49. data/spec/features/binding_and_transition_helper_mixin_spec.rb +2 -2
  50. data/spec/features/method_missing_only_once_spec.rb +28 -0
  51. data/spec/features/not_requirements_spec.rb +83 -46
  52. data/spec/features/plotter_spec.rb +97 -0
  53. data/spec/features/shared_log_spec.rb +7 -0
  54. data/spec/features/singleton_machine_spec.rb +39 -0
  55. data/spec/features/state_and_array_options_accessor_spec.rb +1 -1
  56. data/spec/features/{transition_boolean_comparison.rb → transition_boolean_comparison_spec.rb} +29 -18
  57. data/spec/helper.rb +6 -117
  58. data/spec/integration/active_record_persistence_spec.rb +18 -4
  59. data/spec/integration/binding_extension_spec.rb +1 -1
  60. data/spec/integration/class_accessor_spec.rb +49 -59
  61. data/spec/integration/event_definition_spec.rb +20 -20
  62. data/spec/integration/example_01_document_spec.rb +13 -8
  63. data/spec/integration/example_02_string_spec.rb +3 -2
  64. data/spec/integration/instance_accessor_spec.rb +16 -19
  65. data/spec/integration/lathe_extension_spec.rb +2 -2
  66. data/spec/integration/machine_duplication_spec.rb +59 -37
  67. data/spec/integration/relaxdb_persistence_spec.rb +6 -3
  68. data/spec/integration/requirement_reflection_spec.rb +66 -57
  69. data/spec/integration/state_definition_spec.rb +72 -66
  70. data/spec/integration/transition_spec.rb +169 -173
  71. data/spec/spec.opts +5 -3
  72. data/spec/spec_helper.rb +132 -0
  73. data/spec/state_fu_spec.rb +870 -0
  74. data/spec/units/binding_spec.rb +33 -22
  75. data/spec/units/event_spec.rb +3 -22
  76. data/spec/units/exceptions_spec.rb +7 -0
  77. data/spec/units/lathe_spec.rb +7 -7
  78. data/spec/units/machine_spec.rb +67 -75
  79. data/spec/units/method_factory_spec.rb +55 -48
  80. data/spec/units/sprocket_spec.rb +5 -7
  81. data/spec/units/state_spec.rb +33 -24
  82. metadata +31 -19
  83. data/lib/state_fu/active_support_lite/inheritable_attributes.rb +0 -1
  84. data/lib/state_fu/fu_space.rb +0 -51
  85. data/lib/state_fu/mock_transition.rb +0 -38
  86. data/spec/BDD/plotter_spec.rb +0 -115
  87. data/spec/integration/dynamic_requirement_spec.rb +0 -160
  88. data/spec/integration/ex_machine_for_accounts_spec.rb +0 -79
  89. data/spec/integration/sanity_spec.rb +0 -31
  90. data/spec/units/fu_space_spec.rb +0 -95
@@ -1,14 +1,54 @@
1
1
  module StateFu
2
2
  class Event < StateFu::Sprocket
3
3
 
4
- attr_reader :origins, :targets, :requirements
4
+ attr_reader :origins, :targets, :requirements, :sequence
5
5
 
6
6
  # called by Lathe when a new event is constructed
7
7
  def initialize(machine, name, options={})
8
- @requirements = [].extend ArrayWithSymbolAccessor
8
+ @requirements = [].extend ArrayWithSymbolAccessor
9
+ @sequence = {}
9
10
  super( machine, name, options )
10
11
  end
11
12
 
13
+ #
14
+ # build a hash of target => [origins]
15
+ #
16
+ def add_to_sequence origin_states, target_state
17
+ origin_states = [origin_states].flatten
18
+ existing = origin_states.select {|s| target_for_origin(s) }
19
+ raise ArgumentError.new unless existing.empty? && !targets
20
+ @sequence[target_state] ||= []
21
+ [origin_states].flatten.each do |o|
22
+ @sequence[target_state] << o
23
+ end
24
+ @sequence
25
+ end
26
+
27
+ def target_for_origin origin_state
28
+ raise ArgumentError.new if origin_state.nil?
29
+ name = sequence.detect do |k,v|
30
+ v.include?(origin_state.to_sym)
31
+ end[0] rescue nil
32
+ machine.states[name] if name
33
+ # if t
34
+ # puts t.inspect + " <============================" if t
35
+ # puts "======================"
36
+ # puts origin_state.class
37
+ # puts origin_state.name rescue origin_state.inspect
38
+ # end
39
+ # machine.states[t.first] if t
40
+ end
41
+
42
+ def can_transition_from?(origin_state)
43
+ ( origins && origins.include?(origin_state.to_sym) && !targets.blank?) ||
44
+ target_for_origin(origin_state)
45
+ end
46
+
47
+ def sequence?
48
+ !sequence.empty?
49
+ end
50
+
51
+
12
52
  # the names of all possible origin states
13
53
  def origin_names
14
54
  origins ? origins.map(&:to_sym) : nil
@@ -26,26 +66,11 @@ module StateFu
26
66
 
27
67
  # tests if a state or state name is in the list of origins
28
68
  def from?( state )
29
- origin_names.include?( state.to_sym )
69
+ origin_names.include?( state.to_sym ) || target_for_origin(state)
30
70
  end
31
-
32
- # internal method which accumulates states into an instance
33
- # variable with successive invocations.
34
- # ensures that calling #from multiple times adds to, rather than
35
- # clobbering, the list of origins / targets.
36
- def update_state_collection( ivar_name, *args)
37
- new_states = if [args].flatten == [:ALL]
38
- machine.states
39
- else
40
- machine.find_or_create_states_by_name( *args.flatten )
41
- end
42
- unless new_states.is_a?( Array )
43
- new_states = [new_states]
44
- end
45
- existing = instance_variable_get( ivar_name )
46
- # return existing if new_states.empty?
47
- new_value = ((existing || [] ) + new_states).flatten.compact.uniq.extend( StateArray )
48
- instance_variable_set( ivar_name, new_value )
71
+
72
+ def cycle?
73
+ origin && origin == target
49
74
  end
50
75
 
51
76
  # *adds to* the origin states given a list of symbols / States
@@ -58,17 +83,6 @@ module StateFu
58
83
  update_state_collection( '@targets', *args )
59
84
  end
60
85
 
61
-
62
- # used internally
63
- #
64
- # <tt>complete?(:origins) # do we have origins?<tt>
65
- # <tt>complete? # do we have origins and targets?<tt>
66
- def complete?( field = nil )
67
- ( field && [field] || [:origins, :targets] ).
68
- map{ |s| send(s) }.
69
- all?{ |f| !(f.nil? || f.empty?) }
70
- end
71
-
72
86
  # if there is a single state in #origins, returns it
73
87
  def origin
74
88
  origins && origins.length == 1 && origins[0] || nil
@@ -83,7 +97,22 @@ module StateFu
83
97
  # origins. It's simple because it can be triggered without
84
98
  # supplying a target name - ie, <tt>go!<tt> vs <tt>go!(:home)<tt>
85
99
  def simple?
86
- !! ( origins && target )
100
+ !! ( origins && target || sequence? )
101
+ end
102
+
103
+ def fireable?( transition )
104
+ transition.valid?(true)
105
+ end
106
+
107
+
108
+ #
109
+ # Lathe methods
110
+ #
111
+
112
+ # adds an event requirement.
113
+ # DOCME // TODO - can this be removed?
114
+ def requires( *args, &block )
115
+ lathe.requires( *args, &block )
87
116
  end
88
117
 
89
118
  # generally called from a Lathe. Sets the origin(s) and optionally
@@ -93,17 +122,17 @@ module StateFu
93
122
  def from *args
94
123
  options = args.extract_options!.symbolize_keys!
95
124
  args.flatten!
96
- to = options.delete(:to)
125
+ to = options.delete(:to) || options.delete(:transitions_to)
97
126
  if args.empty? && !to
98
127
  if options.length == 1
99
- self.origins= options.keys[0]
100
- self.targets= options.values[0]
128
+ self.origins = options.keys[0]
129
+ self.targets = options.values[0]
101
130
  else
102
131
  raise options.inspect
103
132
  end
104
133
  else
105
- self.origins= *args
106
- self.targets= to unless to.nil?
134
+ self.origins = *args
135
+ self.targets = to unless to.nil?
107
136
  end
108
137
  end
109
138
 
@@ -115,18 +144,53 @@ module StateFu
115
144
  self.targets= *args
116
145
  end
117
146
 
118
- # is the event legal for the given binding, with the given
119
- # (optional) arguments?
120
- def fireable_by?( binding, *args )
121
- requirements.reject do |r|
122
- binding.evaluate_requirement_with_args( r, *args )
123
- end.empty?
147
+ alias_method :transitions_to, :to
148
+ alias_method :transitions_from, :from
149
+
150
+ #
151
+ # misc
152
+ #
153
+
154
+ # display nice and short
155
+ def inspect
156
+ s = self.to_s
157
+ s = s[0,s.length-1]
158
+ display_hooks = hooks.dup
159
+ display_hooks.each do |k,v|
160
+ display_hooks.delete(k) if v.empty?
161
+ end
162
+ unless display_hooks.empty?
163
+ s << " hooks=#{display_hooks.inspect}"
164
+ end
165
+ unless requirements.empty?
166
+ s << " requirements=#{requirements.inspect}"
167
+ end
168
+ s << " targets=#{targets.map(&:to_sym).inspect}" if targets
169
+ s << " origins=#{origins.map(&:to_sym).inspect}" if origins
170
+ s << ">"
171
+ s
124
172
  end
125
173
 
126
- # adds an event requirement.
127
- # TODO MOREDOC
128
- def requires( *args, &block )
129
- lathe.requires( *args, &block )
174
+ private
175
+
176
+ # internal method which accumulates states into an instance
177
+ # variable with successive invocations.
178
+ # ensures that calling #from multiple times adds to, rather than
179
+ # clobbering, the list of origins / targets.
180
+ def update_state_collection( ivar_name, *args)
181
+ raise ArgumentError if sequence?
182
+ new_states = if [args].flatten == [:ALL]
183
+ machine.states
184
+ else
185
+ machine.find_or_create_states_by_name( *args.flatten )
186
+ end
187
+ unless new_states.is_a?( Array )
188
+ new_states = [new_states]
189
+ end
190
+ existing = instance_variable_get( ivar_name )
191
+ # return existing if new_states.empty?
192
+ new_value = ((existing || [] ) + new_states).flatten.compact.uniq.extend( StateArray )
193
+ instance_variable_set( ivar_name, new_value )
130
194
  end
131
195
 
132
196
  end
@@ -1,59 +1,105 @@
1
1
  module StateFu
2
2
 
3
- class Exception < ::Exception
3
+ class MagicMethodError < NoMethodError
4
+ end
5
+
6
+ class Error < ::StandardError
4
7
  attr_reader :binding, :options
8
+
9
+ def initialize binding, message=nil, options={}
10
+ @binding = binding
11
+ @options = options
12
+ super message
13
+ end
14
+
5
15
  end
6
16
 
7
- class RequirementError < Exception
8
- attr_reader :transition
9
- DEFAULT_MESSAGE = "The transition was halted"
17
+ class TransitionNotFound < Error
18
+ attr_reader :valid_transitions
19
+ attr_reader :valid_destinations
20
+ DEFAULT_MESSAGE = "Transition could not be determined"
21
+
22
+ def initialize(binding, valid_transitions, message=DEFAULT_MESSAGE, options={})
23
+ @valid_transitions = valid_transitions
24
+ @valid_destinations = valid_transitions.map(&:destination)
25
+ super(binding, message, options)
26
+ end
10
27
 
11
- # SPECME
12
- def unmet_requirements
13
- transition.unmet_requirements
28
+ def inspect
29
+ "<#{self.class.to_s} #{message} available=[#{valid_destinations.inspect}]>"
14
30
  end
31
+
32
+ end
33
+
34
+ class TransitionError < Error
35
+ # TODO default message
36
+ attr_reader :transition
15
37
 
16
- def initialize( transition, message=DEFAULT_MESSAGE, options={})
17
- @transition = transition
18
- @options = options
19
- super( message )
38
+ def initialize transition, message=nil, options={}
39
+ raise caller.inspect unless transition.is_a?(Transition)
40
+ @transition = transition
41
+ super transition.binding, message, options
20
42
  end
21
43
 
44
+ delegate :origin, :to => :transition
45
+ delegate :target, :to => :transition
46
+ delegate :event, :to => :transition
47
+ delegate :args, :to => :transition
48
+
49
+ # TODO capture these on initialization
50
+ delegate :unmet_requirements, :to => :transition
51
+ delegate :unmet_requirement_messages, :to => :transition
52
+ delegate :requirement_errors, :to => :transition
53
+
22
54
  def inspect
23
- "<StateFu::RequirementError #{message} #{@transition.origin.name}=[#{@transition.event.name}]=>#{transition.target.name}"
55
+ origin_name = origin && origin.name
56
+ target_name = target && target.name
57
+ event_name = event && event.name
58
+ "<#{self.class.to_s} #{message} #{origin_name.inspect}=[#{event_name.inspect}]=>#{target_name.inspect}>"
24
59
  end
25
60
  end
26
61
 
27
- class TransitionHalted < Exception
28
- attr_reader :transition
62
+ class UnknownTarget < TransitionError
63
+ end
29
64
 
30
- DEFAULT_MESSAGE = "The transition was halted"
65
+ class TransitionAlreadyFired < TransitionError
66
+ end
67
+
68
+ class RequirementError < TransitionError
69
+ include Enumerable
31
70
 
32
- def initialize( transition, message=DEFAULT_MESSAGE, options={})
33
- @transition = transition
34
- @options = options
35
- super( message )
71
+ delegate :each, :to => :to_h
72
+ delegate :length, :to => :to_h
73
+ delegate :empty?, :to => :to_h
74
+
75
+ def to_a
76
+ unmet_requirement_messages
77
+ end
78
+
79
+ def to_h
80
+ requirement_errors
81
+ end
82
+
83
+ def to_s
84
+ inspect
85
+ end
86
+
87
+ def inspect
88
+ "<#{self.class.to_s}::#{__id__} :#{transition.origin.to_sym}-[#{transition.event.to_sym}]->:#{transition.target.to_sym} unmet_requirements=#{to_a.inspect}>"
36
89
  end
37
90
  end
38
91
 
39
- class InvalidTransition < Exception
40
- attr_reader :binding, :origin, :target, :event, :args
92
+ class TransitionHalted < TransitionError
93
+ end
41
94
 
42
- DEFAULT_MESSAGE = "An invalid transition was attempted"
95
+ class InvalidTransition < TransitionError
96
+ attr_reader :valid_transitions
43
97
 
44
- def initialize( binding,
45
- event,
46
- origin,
47
- target,
48
- message=DEFAULT_MESSAGE,
49
- options={})
50
- @binding = binding
51
- @event = event
52
- @origin = origin
53
- @target = target
54
- @options = options
55
- super( message )
98
+ def initialize transition, message=nil, valid_transitions=nil, options={}
99
+ @valid_transitions = valid_transitions
100
+ super transition, message, options
56
101
  end
102
+
57
103
  end
58
104
 
59
105
  end
@@ -0,0 +1,149 @@
1
+ module StateFu
2
+ #
3
+ # delegator class for evaluation methods / procs in the context of
4
+ # your object.
5
+ #
6
+
7
+ # There's a bug in ruby 1.8.x where lambda {}.arity == -1 instead of 0
8
+ # To get around this, turn it into a proc if conditions are dangerous.
9
+ def self.get_effective_arity
10
+ if RUBY_VERSION[0,3] == "1.8" && proc.arity == -1
11
+ proc.to_proc.arity
12
+ else
13
+ proc.arity
14
+ end
15
+ end
16
+
17
+ class Executioner
18
+
19
+ # give us a blank slate
20
+ # instance_methods.each { |m| undef_method m unless m =~ /(^__|^self|^nil\?$|^send$|proxy_|^object_id|^respond_to\?|^instance_exec|^instance_eval|^method$)/ }
21
+
22
+ def initialize transition, &block
23
+ @transition = transition
24
+ @__target__ = transition.object
25
+ @__self___ = self
26
+ yield self if block_given?
27
+ # forces method_missing to snap back to its pre-state-fu condition:
28
+ # @__target__.initialize_state_fu!
29
+ self
30
+ end
31
+
32
+ # delegate :self, :to => :__target__
33
+
34
+ delegate :origin, :to => :transition, :prefix => true # transition_origin
35
+ delegate :target, :to => :transition, :prefix => true # transition_target
36
+ delegate :event, :to => :transition, :prefix => true # transition_event
37
+
38
+ delegate :halt!, :to => :transition
39
+ delegate :args, :to => :transition
40
+ delegate :options, :to => :transition
41
+
42
+ def binding
43
+ transition.binding
44
+ end
45
+
46
+ attr_reader :transition, :__target__, :__self__
47
+
48
+ alias_method :t, :transition
49
+ alias_method :current_transition, :transition
50
+ alias_method :context, :transition
51
+ alias_method :ctx, :transition
52
+
53
+ alias_method :arguments, :args
54
+ alias_method :transition_arguments, :args
55
+
56
+ # delegate :machine, :to => :binding
57
+ #delegate :states, :to => :machine
58
+
59
+
60
+ def machine
61
+ binding.machine
62
+ end
63
+
64
+ def states
65
+ # puts binding
66
+ # puts binding.instance_eval() { @machine }
67
+ # puts binding.machine
68
+ # puts binding.machine.states.names
69
+ # puts machine
70
+ # puts machine.states
71
+ # machine.states
72
+ end
73
+ # delegate :machine, :to => :transition
74
+
75
+ def evaluate_with_arguments method_name_or_proc, *arguments
76
+ if method_name_or_proc.is_a?(Proc) && meth = method_name_or_proc
77
+ elsif meth = transition.machine.named_procs[method_name_or_proc]
78
+ elsif respond_to?( method_name_or_proc) && meth = method(method_name_or_proc)
79
+ elsif method_name_or_proc.to_s =~ /^not?_(.*)$/
80
+ # special case: prefix a method with no_ or not_ and get the
81
+ # boolean opposite of its evaluation result
82
+ return !( evaluate_with_arguments $1, *args )
83
+ else
84
+ raise NoMethodError.new( "undefined method_name `#{method_name_or_proc.to_s}' for \"#{__target__}\":#{__target__.class.to_s}" )
85
+ end
86
+
87
+ if arguments.length < meth.arity.abs && meth.arity != -1
88
+ # ensure we don't have too few arguments
89
+ raise ArgumentError.new([meth.arity, arguments.length].inspect)
90
+ else
91
+ # ensure we don't pass too many arguments
92
+ arguments = arguments[0, meth.arity.abs]
93
+ end
94
+
95
+ # execute it!
96
+ __target__.with_methods_on(self) do
97
+ self.instance_exec *arguments, &meth
98
+ end
99
+ end
100
+
101
+ def evaluate method_name_or_proc
102
+ arguments = [transition, args, __target__]
103
+ evaluate_with_arguments(method_name_or_proc, *arguments)
104
+ end
105
+
106
+ alias_method :executioner_respond_to?, :respond_to?
107
+
108
+ def respond_to? method_name, include_private = false
109
+ executioner_respond_to?(method_name, include_private) ||
110
+ __target__.__send__( :respond_to?, method_name, include_private )
111
+ end
112
+
113
+ alias_method :executioner_method, :method
114
+ def method method_name
115
+ begin
116
+ executioner_method(method_name)
117
+ rescue NameError
118
+ __target__.__send__ :method, method_name
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ # Forwards any missing method call to the \target.
125
+ # TODO / FIXME / NOTE: we don't (can't ?) handle block arguments ...
126
+ def method_missing(method_name, *args)
127
+ if __target__.respond_to?(method_name, true)
128
+ begin
129
+ meth = __target__.__send__ :method, method_name
130
+ rescue NameError
131
+ super
132
+ end
133
+ __target__.instance_exec( *args, &meth)
134
+ else # let's hope it's a named proc
135
+ evaluate_with_arguments(method_name, *args)
136
+ end
137
+
138
+ end
139
+
140
+ # # Forwards any missing method call to the \target.
141
+ # def self.const_missing(const_name)
142
+ # unless __target__.class.const_defined?(const_name, true)
143
+ # super(const_name)
144
+ # end
145
+ # __target__.class.const_get(const_name)
146
+ # end
147
+ #
148
+ end
149
+ end
@@ -0,0 +1,16 @@
1
+ module HasOptions
2
+
3
+ def self.included(base)
4
+ base.class_eval do
5
+ attr_accessor :options
6
+ end
7
+ end
8
+
9
+ def []v
10
+ options[v]
11
+ end
12
+
13
+ def []=v,k
14
+ options[v]=k
15
+ end
16
+ end
@@ -2,29 +2,34 @@ module StateFu
2
2
 
3
3
  # TODO document structure / sequence of hooks elsewhere
4
4
 
5
- module Hooks # :nodoc:
5
+ module Hooks #:nodoc
6
6
 
7
- ALL_HOOKS = [[:event, :before], # good place to start a transaction, etc
8
- [:origin, :exit], # say goodbye!
9
- [:event, :execute], # do stuff here, as a rule of thumb
10
- [:target, :entry], # last chance to halt!
11
- [:event, :after], # clean up all the mess
12
- [:target, :accepted]] # state changed. Quicksave!
7
+ ALL_HOOKS = [[:machine, :before_all], # global before. prepare for any transition
8
+ [:event, :before], # prepare for the event
9
+ [:origin, :exit], # say goodbye!
10
+ [:event, :execute], # do stuff here for the event
11
+ [:target, :entry], # entry point. last chance to halt!
12
+ [:event, :after], # clean up after transition
13
+ [:target, :accepted], # state is changed. Do something about it.
14
+ [:machine, :after_all]] # global after. close up shop.
13
15
 
14
- EVENT_HOOKS = ALL_HOOKS.select { |type, name| type == :event }
15
- STATE_HOOKS = ALL_HOOKS - EVENT_HOOKS
16
- HOOK_NAMES = ALL_HOOKS.map {|a| a[1] }
16
+ EVENT_HOOKS = ALL_HOOKS.select { |type, name| type == :event }
17
+ STATE_HOOKS = ALL_HOOKS.select { |type, name| [:origin, :target].include?(type) }
18
+ MACHINE_HOOKS = ALL_HOOKS.select { |type, name| type == :machine }
19
+ HOOK_NAMES = ALL_HOOKS.map(&:last)
17
20
 
18
21
  # just turn the above into what each class needs
19
22
  # and make it into a nice hash: { :name =>[ hook, ... ], ... }
20
23
  def self.for( me )
21
- x = if me.is_a?( StateFu::State ); STATE_HOOKS
22
- elsif me.is_a?( StateFu::Event ); EVENT_HOOKS
23
- else {}
24
+ x = if me.is_a?(State); STATE_HOOKS
25
+ elsif me.is_a?(Event); EVENT_HOOKS
26
+ elsif me.is_a?(Machine); MACHINE_HOOKS
27
+ elsif me.is_a?(Sprocket); [] # let specs pass
28
+ else raise me
24
29
  end.
25
- map { |_,name| [name, [].extend( StateFu::OrderedHash )] }
26
- hash = x.inject({}) {|h, a| h[a[0]] = a[1] ; h}
27
- hash.extend( StateFu::OrderedHash ).freeze
30
+ map { |type, name| [name, [].extend( OrderedHash )] }
31
+ hash = x.inject({}) {|h, a| h[a.first] = a.last; h }
32
+ hash.extend( OrderedHash ).freeze
28
33
  end
29
34
 
30
35
  end