davidlee-state-fu 0.0.1

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 (49) hide show
  1. data/LICENSE +40 -0
  2. data/README.textile +174 -0
  3. data/Rakefile +87 -0
  4. data/lib/no_stdout.rb +32 -0
  5. data/lib/state-fu.rb +93 -0
  6. data/lib/state_fu/binding.rb +262 -0
  7. data/lib/state_fu/core_ext.rb +23 -0
  8. data/lib/state_fu/event.rb +98 -0
  9. data/lib/state_fu/exceptions.rb +42 -0
  10. data/lib/state_fu/fu_space.rb +50 -0
  11. data/lib/state_fu/helper.rb +189 -0
  12. data/lib/state_fu/hooks.rb +28 -0
  13. data/lib/state_fu/interface.rb +139 -0
  14. data/lib/state_fu/lathe.rb +247 -0
  15. data/lib/state_fu/logger.rb +10 -0
  16. data/lib/state_fu/machine.rb +159 -0
  17. data/lib/state_fu/method_factory.rb +95 -0
  18. data/lib/state_fu/persistence/active_record.rb +27 -0
  19. data/lib/state_fu/persistence/attribute.rb +46 -0
  20. data/lib/state_fu/persistence/base.rb +98 -0
  21. data/lib/state_fu/persistence/session.rb +7 -0
  22. data/lib/state_fu/persistence.rb +50 -0
  23. data/lib/state_fu/sprocket.rb +27 -0
  24. data/lib/state_fu/state.rb +45 -0
  25. data/lib/state_fu/transition.rb +213 -0
  26. data/spec/helper.rb +86 -0
  27. data/spec/integration/active_record_persistence_spec.rb +189 -0
  28. data/spec/integration/class_accessor_spec.rb +127 -0
  29. data/spec/integration/event_definition_spec.rb +74 -0
  30. data/spec/integration/ex_machine_for_accounts_spec.rb +79 -0
  31. data/spec/integration/example_01_document_spec.rb +127 -0
  32. data/spec/integration/example_02_string_spec.rb +87 -0
  33. data/spec/integration/instance_accessor_spec.rb +100 -0
  34. data/spec/integration/machine_duplication_spec.rb +95 -0
  35. data/spec/integration/requirement_reflection_spec.rb +201 -0
  36. data/spec/integration/sanity_spec.rb +31 -0
  37. data/spec/integration/state_definition_spec.rb +177 -0
  38. data/spec/integration/transition_spec.rb +1060 -0
  39. data/spec/spec.opts +7 -0
  40. data/spec/units/binding_spec.rb +145 -0
  41. data/spec/units/event_spec.rb +232 -0
  42. data/spec/units/exceptions_spec.rb +75 -0
  43. data/spec/units/fu_space_spec.rb +95 -0
  44. data/spec/units/lathe_spec.rb +567 -0
  45. data/spec/units/machine_spec.rb +237 -0
  46. data/spec/units/method_factory_spec.rb +359 -0
  47. data/spec/units/sprocket_spec.rb +71 -0
  48. data/spec/units/state_spec.rb +50 -0
  49. metadata +122 -0
@@ -0,0 +1,237 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ ## See state_and_event_common_spec.rb for behaviour shared between
4
+ ## StateFu::State and StateFu::Event
5
+ ##
6
+
7
+ describe StateFu::Machine do
8
+ include MySpecHelper
9
+
10
+ before do
11
+ end
12
+
13
+ describe "class methods" do
14
+ before do
15
+
16
+ end
17
+
18
+ describe "Machine.for_class" do
19
+ describe "when there's no matching machine in FuSpace" do
20
+ before do
21
+ reset!
22
+ make_pristine_class 'Klass'
23
+ mock( StateFu::FuSpace ).class_machines() { { Klass => {} } }
24
+ end
25
+
26
+ it "should create a new machine and bind! it" do
27
+ @machine = Object.new
28
+ mock( @machine ).bind!( Klass, :moose, nil )
29
+ mock( StateFu::Machine ).new( :moose, {} ) { @machine }
30
+ StateFu::Machine.for_class( Klass, :moose )
31
+ end
32
+
33
+ it "should apply the block (via lathe) if one is given" do
34
+ @m = StateFu::Machine.for_class( Klass, :snoo ) do
35
+ state :porpoise
36
+ end
37
+ @m.states.map(&:name).should == [:porpoise]
38
+ end
39
+ end
40
+
41
+ describe "when there's a matching machine in FuSpace" do
42
+ it "should retrieve the previously created machine" do
43
+ @machine = Object.new
44
+ mock( StateFu::FuSpace ).class_machines() { { Klass => { :moose => @machine } } }
45
+ StateFu::Machine.for_class( Klass, :moose ).should == @machine
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+ describe "attributes" do
53
+ end
54
+
55
+ describe "instance methods" do
56
+ before do
57
+ reset!
58
+ make_pristine_class 'Klass'
59
+ @mchn = StateFu::Machine.new( :spec_machine, options={} )
60
+ end
61
+
62
+ describe "helper" do
63
+ it "should add its arguments to the @@helpers array" do
64
+ module Foo; FOO = :foo; end
65
+ module Bar; BAR = :bar; end
66
+ @mchn.helper Foo, Bar
67
+ @mchn.helpers.should == [Foo, Bar]
68
+ end
69
+
70
+ end
71
+
72
+ describe ".initialize" do
73
+ it "should require a name" do
74
+ lambda do
75
+ StateFu::Machine.new()
76
+ end.should raise_error( ArgumentError )
77
+ end
78
+ end
79
+
80
+ describe ".apply!" do
81
+
82
+ end
83
+
84
+ describe ".bind!" do
85
+ it "should call StateFu::FuSpace.insert! with itself and its arguments" do
86
+ field_name = :my_field_name
87
+ mock( StateFu::FuSpace ).insert!( Klass, @mchn, :newname, field_name ) {}
88
+ @mchn.bind!( Klass, :newname, field_name )
89
+ end
90
+
91
+ it "should generate a field name if none is given" do
92
+ klass = Klass
93
+ name = :StinkJuice
94
+ field_name = 'stink_juice_field'
95
+ mock( StateFu::FuSpace ).insert!( Klass, @mchn, name, field_name.to_sym ) {}
96
+ @mchn.bind!( Klass, name )
97
+ end
98
+ end
99
+
100
+ describe ".initial_state=" do
101
+
102
+ it "should set @initial_state given a String, Symbol or State for an existing state" do
103
+ state = StateFu::State.new( @mchn, :wizzle )
104
+ @mchn.states << state
105
+ @mchn.initial_state = state
106
+ @mchn.initial_state.should == state
107
+ end
108
+
109
+ it "should create the state if it doesnt exist" do
110
+ @mchn.initial_state = :snoo
111
+ @mchn.initial_state.should be_kind_of( StateFu::State )
112
+ @mchn.initial_state.name.should == :snoo
113
+ @mchn.states.should include( @mchn.initial_state )
114
+ end
115
+
116
+ it "should raise an ArgumentError given a number or an Array" do
117
+ lambda do @mchn.initial_state = 6
118
+ end.should raise_error( ArgumentError )
119
+
120
+ lambda do @mchn.initial_state = [:ping]
121
+ end.should raise_error( ArgumentError )
122
+ end
123
+
124
+ end
125
+
126
+ describe ".initial_state" do
127
+ it "should return nil if there are no states and initial_state= has not been called" do
128
+ @mchn.states.should == []
129
+ @mchn.initial_state.should == nil
130
+ end
131
+
132
+ it "should return the first state if one exists" do
133
+ stub( @mchn ).states() { [:a, :b, :c] }
134
+ @mchn.initial_state.should == :a
135
+ end
136
+
137
+ end
138
+
139
+ describe ".states" do
140
+ it "should return an array extended with StateFu::StateArray" do
141
+ @mchn.states.should be_kind_of( Array )
142
+ @mchn.states.extended_by.should include( StateFu::StateArray )
143
+ end
144
+ end
145
+
146
+ describe ".state_names" do
147
+ it "should return a list of symbols of state names" do
148
+ @mchn.states << StateFu::State.new( @mchn, :a )
149
+ @mchn.states << StateFu::State.new( @mchn, :b )
150
+ @mchn.state_names.should == [:a, :b ]
151
+ end
152
+ end
153
+
154
+ describe ".events" do
155
+ it "should return an array extended with StateFu::EventArray" do
156
+ @mchn.events.should be_kind_of( Array )
157
+ @mchn.events.extended_by.should include( StateFu::EventArray )
158
+ end
159
+ end
160
+
161
+ describe ".event_names" do
162
+ it "should return a list of symbols of event names" do
163
+ @mchn.events << StateFu::Event.new( @mchn, :a )
164
+ @mchn.events << StateFu::Event.new( @mchn, :b )
165
+ @mchn.event_names.should == [:a, :b ]
166
+ end
167
+ end
168
+
169
+ describe ".find_or_create_states_by_name" do
170
+ describe "given an array of symbols" do
171
+ it "should return the states named by the symbols if they exist" do
172
+ a = StateFu::State.new( @mchn, :a )
173
+ b = StateFu::State.new( @mchn, :b )
174
+ @mchn.states << a
175
+ @mchn.states << b
176
+ @mchn.find_or_create_states_by_name( :a, :b ).should == [a, b]
177
+ @mchn.find_or_create_states_by_name( [:a, :b] ).should == [a, b]
178
+ end
179
+
180
+ it "should return the states named by the symbols and create them if they don't exist" do
181
+ @mchn.states.should == []
182
+ res = @mchn.find_or_create_states_by_name( :a, :b )
183
+ res.should be_kind_of( Array )
184
+ res.length.should == 2
185
+ res.all? { |e| e.class == StateFu::State }.should be_true
186
+ res.map(&:name).should == [ :a, :b ]
187
+ @mchn.find_or_create_states_by_name( :a, :b ).should == res
188
+ end
189
+ end # arr symbols
190
+
191
+ describe "given an array of states" do
192
+ it "should return the states if they're in the machine's states array" do
193
+ a = StateFu::State.new( @mchn, :a )
194
+ b = StateFu::State.new( @mchn, :b )
195
+ @mchn.states << a
196
+ @mchn.states << b
197
+ @mchn.find_or_create_states_by_name( a, b ).should == [a, b]
198
+ @mchn.find_or_create_states_by_name( [a, b] ).should == [a, b]
199
+ @mchn.find_or_create_states_by_name( [[a, b]] ).should == [a, b]
200
+ end
201
+
202
+ it "should add the states to the machine's states array if they're absent" do
203
+ a = StateFu::State.new( @mchn, :a )
204
+ b = StateFu::State.new( @mchn, :b )
205
+ @mchn.find_or_create_states_by_name( a, b ).should == [a, b]
206
+ @mchn.find_or_create_states_by_name( [a, b] ).should == [a, b]
207
+ @mchn.find_or_create_states_by_name( [[a, b]] ).should == [a, b]
208
+ end
209
+ end # arr states
210
+ end # find_or_create_states_by_name
211
+
212
+ describe "requirement_messages" do
213
+ it "should be a hash" do
214
+ @mchn.should respond_to(:requirement_messages)
215
+ @mchn.requirement_messages.should be_kind_of( Hash )
216
+ end
217
+
218
+ it "should be empty by default" do
219
+ @mchn.requirement_messages.should be_empty
220
+ end
221
+
222
+ end # requirement_messages
223
+
224
+ describe "named_procs" do
225
+ it "should be a hash" do
226
+ @mchn.should respond_to(:named_procs)
227
+ @mchn.named_procs.should be_kind_of( Hash )
228
+ end
229
+
230
+ it "should be empty by default" do
231
+ @mchn.named_procs.should be_empty
232
+ end
233
+
234
+ end # named_procs
235
+
236
+ end # instance methods
237
+ end
@@ -0,0 +1,359 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ describe StateFu::MethodFactory do
4
+ include MySpecHelper
5
+
6
+ # TODO - move to eg method_factory integration spec
7
+ describe "event_methods" do
8
+
9
+ before do
10
+ make_pristine_class('Klass')
11
+ end
12
+
13
+ describe "defined on the stateful instance / object before state_fu has been called" do
14
+
15
+ before do
16
+ @machine = Klass.machine do
17
+ event( :simple_event,
18
+ :from => { [:a, :b] => :targ } )
19
+
20
+ state( :a ) { cycle }
21
+ end # machine
22
+ @obj = Klass.new
23
+ end
24
+
25
+ describe "when there is a method_missing already defined for the class" do
26
+ it "should still call the pre-defined method_missing"
27
+ end
28
+
29
+ describe "event creation methods" do
30
+ it "should call method_missing" do
31
+ mock( @obj ).method_missing( :simple_event! )
32
+ @obj.simple_event!
33
+ end
34
+
35
+ it "should call state_fu!" do
36
+ mock.proxy( StateFu::Binding ).new( Klass.machine, @obj, :state_fu )
37
+ @obj.simple_event!
38
+ # @obj.should_have_received( :state_fu! )
39
+ end
40
+
41
+ it "should not raise a NoMethodError" do
42
+ lambda { @obj.simple_event! }.should_not raise_error( NoMethodError )
43
+ end
44
+
45
+ it "should call binding.fire!( :simple_event ... ) with no args" do
46
+ mock.instance_of( StateFu::Binding ).fire!( is_a(StateFu::Event) )
47
+ t = @obj.simple_event!
48
+ end
49
+
50
+ it "should call binding.fire!( :simple_event ... ) with any specified args" do
51
+ mock.instance_of( StateFu::Binding ).fire!( is_a(StateFu::Event), :a, :b, {:c => "d"} )
52
+ t = @obj.simple_event!( :a, :b, :c => "d" )
53
+ end
54
+
55
+ it "should fire the transition" do
56
+ @obj.send(:state_fu_field).should == nil
57
+ t = @obj.simple_event!
58
+ t.should be_accepted
59
+ @obj.send(:state_fu_field).should == 'targ'
60
+ end
61
+
62
+ it "should take a block and use it as per usual"
63
+ end
64
+ end
65
+
66
+
67
+ describe "defined on the binding" do
68
+ describe "when the event is simple (has only one possible target)" do
69
+ before do
70
+ @machine = Klass.machine do
71
+ event( :simple_event,
72
+ :from => { [:a, :b] => :targ } )
73
+ end # machine
74
+ @obj = Klass.new
75
+ @binding = @obj.state_fu
76
+ end # before
77
+
78
+ it "should be simple?" do
79
+ e = @machine.events[:simple_event]
80
+ e.origins.length.should == 2
81
+ e.targets.length.should == 1
82
+ e.should be_simple
83
+ end
84
+
85
+ describe "method which returns an unfired transition" do
86
+ it "should have the same name as the event" do
87
+ @binding.should respond_to(:simple_event)
88
+ end
89
+
90
+ it "should return a new transition if called without any arguments" do
91
+ t = @binding.simple_event()
92
+ t.should be_kind_of( StateFu::Transition )
93
+ t.target.should == @machine.states[:targ]
94
+ t.event.should == @machine.events[:simple_event]
95
+ t.should_not be_fired
96
+ end
97
+
98
+ it "should add any arguments / options it is called with to the transition" do
99
+ t = @binding.simple_event(:a, :b, :c, {'d' => 'e'})
100
+ t.should be_kind_of( StateFu::Transition )
101
+ t.target.should == @machine.states[:targ]
102
+ t.event.should == @machine.events[:simple_event]
103
+ t.args.should == [:a,:b,:c]
104
+ t.options.should == {:d => 'e'}
105
+ end
106
+ end # transition builder
107
+
108
+ describe "method which tests if the event is fireable?" do
109
+ it "should have the name of the event suffixed with ?" do
110
+ @binding.should respond_to(:simple_event?)
111
+ end
112
+
113
+ it "should be true when the binding says it\'s fireable?" do
114
+ @binding.fireable?( :simple_event ).should == true
115
+ @binding.simple_event?.should == true
116
+ end
117
+
118
+ it "should be false when the binding says it\'s not fireable?" do
119
+ mock( @binding ).fireable?( anything ) { false }
120
+ @binding.simple_event?.should == false
121
+ end
122
+ end # fireable?
123
+
124
+ describe "bang (!) method which creates, fires and returns a transition" do
125
+ it "should have the name of the event suffixed with a bang (!)" do
126
+ @binding.should respond_to(:simple_event!)
127
+ end
128
+
129
+ it "should return a fired transition" do
130
+ t = @binding.simple_event!
131
+ t.should be_kind_of( StateFu::Transition )
132
+ t.should be_fired
133
+ end
134
+
135
+ it "should pass any arguments to the transition as args / options" do
136
+ t = @binding.simple_event!( :a, :b, {'c' => :d } )
137
+ t.should be_kind_of( StateFu::Transition )
138
+ t.args.should == [:a, :b ]
139
+ t.options.should == { :c => :d }
140
+ end
141
+ end # bang!
142
+ end # simple
143
+
144
+ describe "when the event is complex (has more than one possible target)" do
145
+ before do
146
+ @machine = Klass.machine do
147
+ state :orphan
148
+ event( :complex_event,
149
+ :from => :home,
150
+ :to => [ :x, :y, :z ] )
151
+ initial_state :home
152
+ end # machine
153
+ @obj = Klass.new
154
+ @binding = @obj.state_fu
155
+ end # before
156
+
157
+ it "should not be simple?" do
158
+ e = @machine.events[:complex_event]
159
+ e.origins.length.should == 1
160
+ e.targets.length.should == 3
161
+ e.should_not be_simple
162
+ end
163
+
164
+ describe "method which returns an unfired transition" do
165
+ it "should have the same name as the event" do
166
+ @binding.should respond_to(:complex_event)
167
+ end
168
+
169
+ it "should raise an error if called without any arguments" do
170
+ lambda { @binding.complex_event() }.should raise_error( ArgumentError )
171
+ end
172
+
173
+ it "should raise an ArgumentError if called with a nonexistent target state" do
174
+ lambda { @binding.complex_event(:nonexistent) }.should raise_error( ArgumentError )
175
+ end
176
+
177
+ it "should raise an InvalidTransition if called with an invalid target state" do
178
+ lambda { @binding.complex_event(:orphan) }.should raise_error( StateFu::InvalidTransition )
179
+ end
180
+
181
+ it "should return a transition to the specified state if supplied a valid state" do
182
+ t = @binding.complex_event( :x )
183
+ t.should be_kind_of( StateFu::Transition )
184
+ t.target.name.should == :x
185
+ end
186
+
187
+ it "should add any arguments / options it is called with to the transition" do
188
+ t = @binding.complex_event(:x,
189
+ :a, :b, :c, {'d' => 'e'})
190
+ t.should be_kind_of( StateFu::Transition )
191
+ t.args.should == [:a,:b,:c]
192
+ t.options.should == {:d => 'e'}
193
+ end
194
+ end # transition builder
195
+
196
+ describe "method which tests if the event is fireable?" do
197
+ it "should have the name of the event suffixed with ?" do
198
+ @binding.should respond_to(:complex_event?)
199
+ end
200
+
201
+ it "should require a valid state name" do
202
+ lambda { @binding.complex_event?(:nonexistent) }.should raise_error( ArgumentError )
203
+ lambda { @binding.complex_event?(:orphan) }.should_not raise_error()
204
+ @binding.complex_event?(:orphan).should == nil
205
+ lambda { @binding.complex_event?(:x) }.should_not raise_error
206
+ end
207
+
208
+ it "should be true when the binding says the event is fireable? " do
209
+ @binding.fireable?( [:complex_event, :x] ).should == true
210
+ @binding.complex_event?(:x).should == true
211
+ end
212
+
213
+ it "should be false when the binding says the event is not fireable?" do
214
+ mock( @binding ).fireable?( anything ) { false }
215
+ @binding.complex_event?(:x).should == false
216
+ end
217
+ end # fireable?
218
+
219
+ describe "bang (!) method which creates, fires and returns a transition" do
220
+ it "should have the name of the event suffixed with a bang (!)" do
221
+ @binding.should respond_to(:complex_event!)
222
+ end
223
+
224
+ it "should require a valid state name" do
225
+ lambda { @binding.complex_event!(:nonexistent) }.should raise_error( ArgumentError )
226
+ lambda { @binding.complex_event!(:orphan) }.should raise_error( StateFu::InvalidTransition )
227
+ lambda { @binding.complex_event!(:x) }.should_not raise_error
228
+ end
229
+
230
+ it "should return a fired transition given a valid state name" do
231
+ t = @binding.complex_event!( :x )
232
+ t.should be_kind_of( StateFu::Transition )
233
+ t.target.should == @machine.states[:x]
234
+ t.should be_fired
235
+ end
236
+
237
+ it "should pass any arguments to the transition as args / options" do
238
+ t = @binding.complex_event!( :x,
239
+ :a, :b, {'c' => :d } )
240
+ t.should be_kind_of( StateFu::Transition )
241
+ t.target.should == @machine.states[:x]
242
+ t.args.should == [:a, :b ]
243
+ t.options.should == { :c => :d }
244
+ end
245
+ end # bang!
246
+ end # complex_event
247
+
248
+ # TODO move these to binding spec
249
+ describe "cycle and next_state methods" do
250
+ describe "when there is a valid transition available for cycle and next_state" do
251
+ before do
252
+ @machine = Klass.machine do
253
+ initial_state :groundhog_day
254
+
255
+ state(:groundhog_day) do
256
+ cycle
257
+ end
258
+
259
+ event(:end_movie, :from => :groundhog_day, :to => :happy_ending)
260
+ end # machine
261
+ @obj = Klass.new
262
+ @binding = @obj.state_fu
263
+ end # before
264
+
265
+ describe "cycle methods:" do
266
+ describe "cycle" do
267
+ it "should return a transition for the cyclical event" do
268
+ t = @binding.cycle
269
+ t.should be_kind_of( StateFu::Transition )
270
+ t.origin.name.should == :groundhog_day
271
+ t.target.name.should == :groundhog_day
272
+ t.should_not be_fired
273
+ end
274
+ end
275
+
276
+ describe "cycle?" do
277
+ end
278
+
279
+ describe "cycle!" do
280
+ end
281
+ end # cycle
282
+
283
+ describe "next_state methods:" do
284
+ describe "next_state" do
285
+ end
286
+
287
+ describe "next_state?" do
288
+ end
289
+
290
+ describe "next_state!" do
291
+ end
292
+ end # next_state
293
+ end # with valid transitions
294
+
295
+ describe "when the machine is empty" do
296
+ before do
297
+ @machine = Klass.machine() {}
298
+ @obj = Klass.new
299
+ @binding = @obj.state_fu
300
+ end
301
+
302
+ describe "current_state" do
303
+ it "should be nil" do
304
+ @binding.current_state.should == nil
305
+ end
306
+ end
307
+ describe "cycle methods:" do
308
+ describe "cycle" do
309
+ it "should return nil" do
310
+ @binding.cycle.should == nil
311
+ end
312
+ end
313
+
314
+ describe "cycle?" do
315
+ it "should return nil" do
316
+ @binding.cycle?.should == nil
317
+ end
318
+ end
319
+
320
+ describe "cycle!" do
321
+ it "should raise_error( InvalidTransition )" do
322
+ lambda { @binding.cycle!.should == nil }.should raise_error( StateFu::InvalidTransition )
323
+ end
324
+ end
325
+ end # cycle
326
+
327
+ describe "next_state methods:" do
328
+ describe "next_state" do
329
+ it "should return nil" do
330
+ @binding.next_state.should == nil
331
+ end
332
+ end
333
+
334
+ describe "next_state?" do
335
+ it "should return nil" do
336
+ @binding.next_state?.should == nil
337
+ end
338
+ end
339
+
340
+ describe "next_state!" do
341
+ it "should raise_error( InvalidTransition )" do
342
+ lambda { @binding.next_state! }.should raise_error( StateFu::InvalidTransition )
343
+ end
344
+ end
345
+ end # next_state
346
+
347
+ end # empty machine
348
+
349
+ describe "when there is more than one candidate event / state" do
350
+ end # too many candidates
351
+
352
+ end # cycle & next_state
353
+ end # defined on binding
354
+
355
+ describe "methods defined on the object" do
356
+ end
357
+
358
+ end # event methods
359
+ end
@@ -0,0 +1,71 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ ##
4
+ ##
5
+ ##
6
+
7
+ describe "Common features / functionality for StateFu::State & StateFu::Event" do
8
+
9
+ include MySpecHelper
10
+ Sprocket = StateFu::Sprocket
11
+ before do
12
+ @machine = Object.new
13
+ end
14
+
15
+ describe "calling Sprocket.new" do
16
+ it "should create a new Sprocket given valid args" do
17
+ sprocket = Sprocket.new(@machine, :flux, { :meta => :doodle })
18
+ sprocket.should be_kind_of( Sprocket )
19
+ sprocket.name.should == :flux
20
+ sprocket.options[:meta].should == :doodle
21
+ sprocket.machine.should == @machine
22
+ end
23
+ end
24
+
25
+ describe "instance methods" do
26
+ before do
27
+ @sprocket = Sprocket.new(@machine, :flux, {:meta => "wibble"})
28
+ end
29
+
30
+ describe ".apply!" do
31
+
32
+ it "should yield itself if the block's arity is 1" do
33
+ yielded = false
34
+ @sprocket.apply!{ |s| yielded = s }
35
+ yielded.should == @sprocket
36
+ end
37
+
38
+ it "should instance_eval the block if its arity is 0/-1" do
39
+ yielded = false
40
+ @sprocket.apply!{ yielded = self }
41
+ yielded.should == @sprocket
42
+ end
43
+ end
44
+
45
+ describe "update!" do
46
+ it "should merge any options passed into .options" do
47
+ opts = @sprocket.options
48
+ newopts = { :size => "huge", :colour => "orange" }
49
+ @sprocket.update!( newopts )
50
+ @sprocket.options.should == opts.merge(newopts)
51
+ end
52
+
53
+ it "should instance_eval the block if one is passed" do
54
+ ref = nil
55
+ @sprocket.update!(){ ref = self }
56
+ ref.should == @sprocket
57
+ end
58
+
59
+ it "should return itself" do
60
+ @sprocket.update!.should == @sprocket
61
+ end
62
+ end
63
+
64
+ describe "to_sym" do
65
+ it "should return its name" do
66
+ @sprocket.to_sym.should == @sprocket.name
67
+ end
68
+ end
69
+
70
+ end
71
+ end