state-fu 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/LICENSE +40 -0
  2. data/README.textile +293 -0
  3. data/Rakefile +114 -0
  4. data/lib/binding.rb +292 -0
  5. data/lib/event.rb +192 -0
  6. data/lib/executioner.rb +120 -0
  7. data/lib/hooks.rb +39 -0
  8. data/lib/interface.rb +132 -0
  9. data/lib/lathe.rb +538 -0
  10. data/lib/machine.rb +184 -0
  11. data/lib/method_factory.rb +243 -0
  12. data/lib/persistence.rb +116 -0
  13. data/lib/persistence/active_record.rb +34 -0
  14. data/lib/persistence/attribute.rb +47 -0
  15. data/lib/persistence/base.rb +100 -0
  16. data/lib/persistence/relaxdb.rb +23 -0
  17. data/lib/persistence/session.rb +7 -0
  18. data/lib/sprocket.rb +58 -0
  19. data/lib/state-fu.rb +56 -0
  20. data/lib/state.rb +48 -0
  21. data/lib/support/active_support_lite/array.rb +9 -0
  22. data/lib/support/active_support_lite/array/access.rb +60 -0
  23. data/lib/support/active_support_lite/array/conversions.rb +202 -0
  24. data/lib/support/active_support_lite/array/extract_options.rb +21 -0
  25. data/lib/support/active_support_lite/array/grouping.rb +109 -0
  26. data/lib/support/active_support_lite/array/random_access.rb +13 -0
  27. data/lib/support/active_support_lite/array/wrapper.rb +25 -0
  28. data/lib/support/active_support_lite/blank.rb +67 -0
  29. data/lib/support/active_support_lite/cattr_reader.rb +57 -0
  30. data/lib/support/active_support_lite/keys.rb +57 -0
  31. data/lib/support/active_support_lite/misc.rb +59 -0
  32. data/lib/support/active_support_lite/module.rb +1 -0
  33. data/lib/support/active_support_lite/module/delegation.rb +130 -0
  34. data/lib/support/active_support_lite/object.rb +9 -0
  35. data/lib/support/active_support_lite/string.rb +38 -0
  36. data/lib/support/active_support_lite/symbol.rb +16 -0
  37. data/lib/support/applicable.rb +41 -0
  38. data/lib/support/arrays.rb +197 -0
  39. data/lib/support/core_ext.rb +90 -0
  40. data/lib/support/exceptions.rb +106 -0
  41. data/lib/support/has_options.rb +16 -0
  42. data/lib/support/logger.rb +165 -0
  43. data/lib/support/methodical.rb +17 -0
  44. data/lib/support/no_stdout.rb +55 -0
  45. data/lib/support/plotter.rb +62 -0
  46. data/lib/support/vizier.rb +300 -0
  47. data/lib/tasks/spec_last.rake +55 -0
  48. data/lib/tasks/state_fu.rake +57 -0
  49. data/lib/transition.rb +338 -0
  50. data/lib/transition_query.rb +224 -0
  51. data/spec/custom_formatter.rb +49 -0
  52. data/spec/features/binding_and_transition_helper_mixin_spec.rb +111 -0
  53. data/spec/features/method_missing_only_once_spec.rb +28 -0
  54. data/spec/features/not_requirements_spec.rb +118 -0
  55. data/spec/features/plotter_spec.rb +97 -0
  56. data/spec/features/shared_log_spec.rb +7 -0
  57. data/spec/features/singleton_machine_spec.rb +39 -0
  58. data/spec/features/state_and_array_options_accessor_spec.rb +47 -0
  59. data/spec/features/transition_boolean_comparison_spec.rb +101 -0
  60. data/spec/helper.rb +13 -0
  61. data/spec/integration/active_record_persistence_spec.rb +202 -0
  62. data/spec/integration/binding_extension_spec.rb +41 -0
  63. data/spec/integration/class_accessor_spec.rb +117 -0
  64. data/spec/integration/event_definition_spec.rb +74 -0
  65. data/spec/integration/example_01_document_spec.rb +133 -0
  66. data/spec/integration/example_02_string_spec.rb +88 -0
  67. data/spec/integration/instance_accessor_spec.rb +97 -0
  68. data/spec/integration/lathe_extension_spec.rb +67 -0
  69. data/spec/integration/machine_duplication_spec.rb +101 -0
  70. data/spec/integration/relaxdb_persistence_spec.rb +97 -0
  71. data/spec/integration/requirement_reflection_spec.rb +270 -0
  72. data/spec/integration/state_definition_spec.rb +163 -0
  73. data/spec/integration/transition_spec.rb +1033 -0
  74. data/spec/spec.opts +9 -0
  75. data/spec/spec_helper.rb +132 -0
  76. data/spec/state_fu_spec.rb +948 -0
  77. data/spec/units/binding_spec.rb +192 -0
  78. data/spec/units/event_spec.rb +214 -0
  79. data/spec/units/exceptions_spec.rb +82 -0
  80. data/spec/units/lathe_spec.rb +570 -0
  81. data/spec/units/machine_spec.rb +229 -0
  82. data/spec/units/method_factory_spec.rb +366 -0
  83. data/spec/units/sprocket_spec.rb +69 -0
  84. data/spec/units/state_spec.rb +59 -0
  85. metadata +171 -0
@@ -0,0 +1,192 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ describe StateFu::Binding do
4
+ include MySpecHelper
5
+
6
+ describe "instance methods" do
7
+ end
8
+
9
+ #
10
+ # class methods
11
+ #
12
+
13
+ describe "class methods" do
14
+ end
15
+
16
+
17
+ #
18
+ #
19
+ #
20
+
21
+
22
+
23
+ #
24
+ #
25
+ #
26
+
27
+ before do
28
+ reset!
29
+ make_pristine_class('Klass')
30
+ Klass.state_fu_machine(){}
31
+ @obj = Klass.new()
32
+ end
33
+
34
+ describe "constructor" do
35
+ before do
36
+ mock(Klass).state_fu_field_names.at_most(1) do
37
+ { :example => :example_field }
38
+ end
39
+ end
40
+
41
+ it "should create a new Binding given valid arguments" do
42
+ b = StateFu::Binding.new( Klass.state_fu_machine, @obj, :example )
43
+ b.should be_kind_of( StateFu::Binding )
44
+ b.object.should == @obj
45
+ b.machine.should == Klass.state_fu_machine
46
+ b.method_name.should == :example
47
+ end
48
+
49
+ it "should add any options supplied to the binding" do
50
+ b = StateFu::Binding.new( Klass.state_fu_machine, @obj, :example,
51
+ :colour => :red,
52
+ :style => [:robust, :fruity] )
53
+ b.options.should == { :colour => :red, :style => [:robust, :fruity] }
54
+ end
55
+
56
+ describe "persister initialization" do
57
+ before do
58
+ @p = Object.new
59
+ class << @p
60
+ attr_accessor :field_name
61
+ end
62
+ @p.field_name
63
+ end
64
+
65
+ describe "when StateFu::Persistence.active_record_column? is true" do
66
+ before do
67
+ mock( StateFu::Persistence ).active_record_column?(Klass, :example_field).times(1) { true }
68
+ mock( Klass ).before_create( :state_fu!) { }
69
+ end
70
+ it "should get an ActiveRecord persister" do
71
+ mock( StateFu::Persistence::ActiveRecord ).new( anything, :example_field ) { @p }
72
+ b = StateFu::Binding.new( Klass.state_fu_machine, @obj, :example )
73
+ b.persister.should == @p
74
+ end
75
+ end
76
+
77
+ describe "when StateFu::Persistence.active_record_column? is false" do
78
+ before do
79
+ mock( StateFu::Persistence ).active_record_column?(Klass, :example_field) { false }
80
+ end
81
+ it "should get an Attribute persister" do
82
+ mock( StateFu::Persistence::Attribute ).new( anything, :example_field ) { @p }
83
+ b = StateFu::Binding.new( Klass.state_fu_machine, @obj, :example )
84
+ b.persister.should == @p
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ describe "initialization via @obj.state_fu()" do
91
+ it "should create a new StateFu::Binding with default method-name & field_name" do
92
+ b = @obj.state_fu()
93
+ b.should be_kind_of( StateFu::Binding )
94
+ b.machine.should == Klass.state_fu_machine
95
+ b.object.should == @obj
96
+ b.method_name.should == StateFu::DEFAULT
97
+ b.field_name.to_sym.should == StateFu::DEFAULT_FIELD
98
+ end
99
+ end
100
+
101
+ describe "a binding for the default machine with two states and an event" do
102
+ before do
103
+ reset!
104
+ make_pristine_class('Klass')
105
+ Klass.state_fu_machine do
106
+ state :new do
107
+ event :age, :to => :old
108
+ end
109
+ state :old
110
+ end
111
+ @machine = Klass.state_fu_machine()
112
+ @object = Klass.new()
113
+ @binding = @object.state_fu()
114
+ end
115
+
116
+ describe "==" do
117
+ it "should be == :new" do
118
+ @binding.should == :new
119
+ end
120
+ end
121
+
122
+ describe ".state and .initial_state" do
123
+ it "should default to machine.initial_state when no initial_state is explicitly defined" do
124
+ @machine.initial_state.name.should == :new
125
+ @binding.current_state.should == @machine.initial_state
126
+ end
127
+
128
+ it "should default to the machine's initial_state if one is set" do
129
+ @machine.initial_state = :fetus
130
+ @machine.initial_state.name.should == :fetus
131
+ obj = Klass.new
132
+ obj.state_fu.current_state.should == @machine.initial_state
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ describe "Instance methods" do
139
+ before do
140
+ end
141
+ describe "can_transition?" do
142
+ before do
143
+ reset!
144
+ make_pristine_class("Klass")
145
+ Klass.class_eval do
146
+ def tissue?(*args); "o_O"; end
147
+ end
148
+ @machine = Klass.state_fu_machine do
149
+ state :snoo do
150
+ event :fire, :to => :wizz do
151
+ requires :tissue?
152
+ end
153
+ end
154
+ state :wizz do
155
+ event :not_fireable, :to => :pong
156
+ end
157
+ end
158
+ @obj = Klass.new
159
+ end
160
+
161
+ describe "when called with arguments which would return a valid transition from .transition()" do
162
+ it "should return true" do
163
+ @obj.state_fu.can_transition?(:fire).should == true
164
+ end
165
+ end
166
+
167
+ describe "when called with arguments which would raise an IllegalTransition from .transition()" do
168
+ it "should return nil" do
169
+ @obj.state_fu.name.should == :snoo
170
+ lambda { @obj.state_fu.can_transition?(:not_fire) }.should_not raise_error( StateFu::IllegalTransition )
171
+ @obj.state_fu.can_transition?(:not_fire).should == nil
172
+ end
173
+ end
174
+
175
+ describe "when called with additional arguments after the destination event/state" do
176
+
177
+ # This would make very little sense to someone trying to understand how to use the library.
178
+ it "should pass the arguments to any requirements to determine transition availability" do
179
+ pending
180
+ t = nil
181
+ mock(@obj).tissue?(anything) do
182
+ # current_transition.should be_kind_of(StateFu::Transition)
183
+ t << current_transition
184
+ end #{|tr| tr.args.should == [:a,:b] }
185
+ @obj.state_fu.can_fire?(:a, :b)
186
+ end
187
+ end
188
+
189
+ end
190
+
191
+ end
192
+ end
@@ -0,0 +1,214 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ ##
4
+ ##
5
+ ##
6
+
7
+ describe StateFu::Event do
8
+ include MySpecHelper
9
+ before do
10
+ @machine = Object.new
11
+ stub(@machine).tools() { [].extend( StateFu::ToolArray ) }
12
+ end
13
+
14
+ describe "Instance methods" do
15
+ before do
16
+ @name = :germinate
17
+ @options = {:speed => :slow}
18
+ @event = StateFu::Event.new( @machine, @name, @options )
19
+ @state_a = StateFu::State.new( @machine,:a )
20
+ @state_b = StateFu::State.new( @machine,:b )
21
+ @initial = Object.new
22
+ @final = Object.new
23
+ @start = Object.new
24
+ @end = Object.new
25
+ end
26
+
27
+
28
+ describe "Instance methods" do
29
+ describe "setting origin / target" do
30
+
31
+ describe "target" do
32
+ it "should be nil if targets is nil" do
33
+ stub( @event ).targets() { nil }
34
+ @event.target.should == nil
35
+ end
36
+
37
+ it "should be nil if targets has more than one state" do
38
+ stub( @event ).targets() { [@state_a, @state_b] }
39
+ @event.target.should == nil
40
+ end
41
+
42
+ it "should be the sole state if targets is set and there is only one" do
43
+ stub( @event ).targets() { [@state_a] }
44
+ @event.target.should == @state_a
45
+ end
46
+ end
47
+
48
+ describe 'origins=' do
49
+ it "should call get_states_list_by_name with its argument" do
50
+ mock( @machine ).find_or_create_states_by_name( :initial ) { }
51
+ @event.origins= :initial
52
+ end
53
+
54
+ it "should set @origin to the result" do
55
+ mock( @machine ).find_or_create_states_by_name( :initial ) { [:result] }
56
+ @event.origins= :initial
57
+ @event.origins.should == [:result]
58
+ end
59
+
60
+ end
61
+
62
+ describe 'targets=' do
63
+ it "should call get_states_list_by_name with its argument" do
64
+ mock( @machine ).find_or_create_states_by_name( :initial ) { [] }
65
+ @event.targets= :initial
66
+ end
67
+
68
+ it "should set @target to the result" do
69
+ mock( @machine ).find_or_create_states_by_name( :initial ) { [:result] }
70
+ @event.targets= :initial
71
+ @event.targets.should == [:result]
72
+ end
73
+ end
74
+
75
+ describe "lathe" do
76
+ before do
77
+ @lathe = @event.lathe()
78
+ end
79
+
80
+ it "should return a StateFu::Lathe" do
81
+ @lathe.should be_kind_of( StateFu::Lathe )
82
+ end
83
+
84
+ it "should have the event's machine" do
85
+ @lathe.machine.should == @event.machine()
86
+ end
87
+
88
+ it "should have the event as the sprocket" do
89
+ @lathe.state_or_event.should == @event
90
+ end
91
+
92
+ end
93
+
94
+ describe '.from()' do
95
+ describe "given @event.from :initial, :to => :final" do
96
+ describe "setting attributes" do
97
+ before do
98
+ stub( @machine ).find_or_create_states_by_name( anything ) { |*a| raise(a.inspect) }
99
+ stub( @machine ).find_or_create_states_by_name( :initial ) { [@initial] }
100
+ stub( @machine ).find_or_create_states_by_name( :final ) { [@final] }
101
+ end
102
+
103
+ it "should call @machine.find_or_create_states_by_name() with :initial and :final" do
104
+ @event.from :initial, :to => :final
105
+ end
106
+
107
+ it "should set @event.origin to the returned array of origin states" do
108
+ @event.from :initial, :to => :final
109
+ @event.origins.should == [@initial]
110
+ end
111
+
112
+ it "should set @event.target to the returned array of target states" do
113
+ @event.from :initial, :to => :final
114
+ @event.targets.should == [@final]
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "given @event.from <Array>, :to => <Array>" do
120
+ it "should call @machine.find_or_create_states_by_name() with both arrays" do
121
+ stub( @machine ).find_or_create_states_by_name(:initial, :start) do
122
+ [@initial, @start]
123
+ end
124
+ stub( @machine ).find_or_create_states_by_name(:final, :end) do
125
+ [@final, @end]
126
+ end
127
+
128
+ @event.from( [:initial, :start], :to => [:final, :end] )
129
+ end
130
+ end
131
+
132
+ describe "given @event.from :ALL, :to => :ALL" do
133
+ it "should set origins and targets to @machine.states" do
134
+ stub( @machine ).states() { [:all, :of, :them ] }
135
+ stub( @machine ).find_or_create_states_by_name(anything) do |x|
136
+ x
137
+ end
138
+ @event.from( :ALL, :to => :ALL )
139
+ @event.origins.should == [:all, :of, :them ]
140
+ @event.targets.should == [:all, :of, :them ]
141
+ end
142
+ end
143
+
144
+ end
145
+
146
+ describe '.to()' do
147
+ describe "given :final" do
148
+ it "should set @event.target to machine.find_or_create_states_by_name( :final )" do
149
+ mock( @machine ).find_or_create_states_by_name(:final) { [@final] }
150
+ @event.to :final
151
+ @event.targets.should == [@final]
152
+ end
153
+ end
154
+ end
155
+
156
+ end
157
+
158
+ describe 'origin_names' do
159
+ it "should return an array of state names in origin when origin is not nil" do
160
+ mock( @machine ).find_or_create_states_by_name(:initial) { [@initial] }
161
+ mock( @machine ).find_or_create_states_by_name(:final) { [@final] }
162
+ @event.from :initial, :to => :final
163
+ @event.origin.should == @initial
164
+ mock( @initial ).to_sym().times(any_times) { :initial }
165
+ @event.origin_names.should == [:initial]
166
+ end
167
+
168
+ it "should return nil when origin is nil" do
169
+ mock( @event ).origins().times(any_times) { nil }
170
+ @event.origin_names.should == nil
171
+ end
172
+
173
+ end
174
+
175
+ describe 'target_names' do
176
+ it "should return an array of state names in target when target is not nil" do
177
+ mock( @event ).targets.times( any_times ) { [@final] }
178
+ mock( @final ).to_sym { :final }
179
+ @event.target_names.should == [:final]
180
+ end
181
+
182
+ it "should return nil when target is nil" do
183
+ mock( @event ).targets().times(any_times) { nil }
184
+ @event.target_names.should == nil
185
+ end
186
+ end
187
+
188
+ describe 'to?' do
189
+ it "should return true given a symbol which is the name of a state in @target" do
190
+ mock( @event ).targets.times(any_times) { [StateFu::State.new(@machine,:a)] }
191
+ @event.to?( :a ).should == true
192
+ end
193
+
194
+ it "should return false given a symbol which is not the name of a state in @target" do
195
+ mock( @event ).targets.times(any_times) { [StateFu::State.new(@machine,:a)] }
196
+ @event.to?( :b ).should == false
197
+ end
198
+ end
199
+
200
+ describe 'from?' do
201
+ it "should return true given a symbol which is the name of a state in @origin" do
202
+ mock( @event ).origins.times(any_times) { [StateFu::State.new(@machine,:a)] }
203
+ @event.from?( :a ).should == true
204
+ end
205
+
206
+ it "should return nil given a symbol which is not the name of a state in @origin" do
207
+ mock( @event ).origins().times(any_times) { [StateFu::State.new(@machine,:a)] }
208
+ @event.from?( :b ).should == nil
209
+ end
210
+ end
211
+
212
+ end # describe instance methods
213
+ end # describe StateFu::Event
214
+ end
@@ -0,0 +1,82 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ describe StateFu::RequirementError do
4
+
5
+ describe "constructor" do
6
+ before do
7
+ @transition = Object.new()
8
+ end
9
+
10
+ end
11
+ end
12
+
13
+ describe StateFu::TransitionHalted do
14
+
15
+ describe "constructor" do
16
+ before do
17
+ @transition = Object.new()
18
+ end
19
+
20
+ it "should create a TransitionHalted given a transition" do
21
+ pending
22
+ e = StateFu::TransitionHalted.new( @transition )
23
+ e.should be_kind_of( StateFu::TransitionHalted )
24
+ end
25
+
26
+ it "should allow a custom message" do
27
+ pending
28
+ msg = 'helo'
29
+ e = StateFu::TransitionHalted.new( @transition, msg )
30
+ e.should be_kind_of( StateFu::TransitionHalted )
31
+ e.message.should == msg
32
+ end
33
+
34
+ it "should allow a message to be omitted" do
35
+ pending
36
+ e = StateFu::TransitionHalted.new( @transition )
37
+ e.should be_kind_of( StateFu::TransitionHalted )
38
+ e.message.should == StateFu::TransitionHalted::DEFAULT_MESSAGE
39
+ end
40
+
41
+ it "should allow access to the transition" do
42
+ pending
43
+ e = StateFu::TransitionHalted.new( @transition )
44
+ e.transition.should == @transition
45
+ end
46
+ end
47
+ end
48
+
49
+ describe StateFu::IllegalTransition do
50
+ before do
51
+ @binding = Object.new
52
+ @origin = Object.new
53
+ @event = Object.new
54
+ @target = Object.new
55
+ end
56
+
57
+ describe "constructor" do
58
+ it "should create an IllegalTransition given a binding, event, origin & target" do
59
+ pending
60
+ e = StateFu::IllegalTransition.new( @binding, @event, @origin, @target )
61
+ e.should be_kind_of( StateFu::IllegalTransition )
62
+ e.message.should == StateFu::IllegalTransition::DEFAULT_MESSAGE
63
+ end
64
+
65
+ it "should allow a custom message" do
66
+ pending
67
+ msg = 'helo'
68
+ e = StateFu::IllegalTransition.new( @binding, @event, @origin, @target, msg )
69
+ e.should be_kind_of( StateFu::IllegalTransition )
70
+ e.message.should == msg
71
+ end
72
+
73
+ it "should allow access to the binding, event, origin, and target" do
74
+ pending
75
+ e = StateFu::IllegalTransition.new( @binding, @event, @origin, @target )
76
+ e.binding.should == @binding
77
+ e.event.should == @event
78
+ e.origin.should == @origin
79
+ e.target.should == @target
80
+ end
81
+ end
82
+ end