state_machine 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/LICENSE +1 -1
  3. data/README.rdoc +162 -23
  4. data/Rakefile +3 -18
  5. data/lib/state_machine.rb +3 -4
  6. data/lib/state_machine/callback.rb +65 -13
  7. data/lib/state_machine/eval_helpers.rb +20 -4
  8. data/lib/state_machine/initializers.rb +4 -0
  9. data/lib/state_machine/initializers/merb.rb +1 -0
  10. data/lib/state_machine/initializers/rails.rb +7 -0
  11. data/lib/state_machine/integrations.rb +21 -6
  12. data/lib/state_machine/integrations/active_model.rb +414 -0
  13. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  14. data/lib/state_machine/integrations/{active_record → active_model}/observer.rb +7 -7
  15. data/lib/state_machine/integrations/active_record.rb +65 -129
  16. data/lib/state_machine/integrations/active_record/locale.rb +4 -11
  17. data/lib/state_machine/integrations/data_mapper.rb +24 -6
  18. data/lib/state_machine/integrations/data_mapper/observer.rb +36 -0
  19. data/lib/state_machine/integrations/mongo_mapper.rb +295 -0
  20. data/lib/state_machine/integrations/sequel.rb +33 -7
  21. data/lib/state_machine/machine.rb +121 -23
  22. data/lib/state_machine/machine_collection.rb +12 -103
  23. data/lib/state_machine/transition.rb +125 -164
  24. data/lib/state_machine/transition_collection.rb +244 -0
  25. data/lib/tasks/state_machine.rb +12 -15
  26. data/test/functional/state_machine_test.rb +11 -1
  27. data/test/unit/callback_test.rb +305 -32
  28. data/test/unit/eval_helpers_test.rb +103 -1
  29. data/test/unit/event_test.rb +2 -1
  30. data/test/unit/guard_test.rb +2 -1
  31. data/test/unit/integrations/active_model_test.rb +909 -0
  32. data/test/unit/integrations/active_record_test.rb +1542 -1292
  33. data/test/unit/integrations/data_mapper_test.rb +1369 -1041
  34. data/test/unit/integrations/mongo_mapper_test.rb +1349 -0
  35. data/test/unit/integrations/sequel_test.rb +1214 -985
  36. data/test/unit/integrations_test.rb +8 -0
  37. data/test/unit/machine_collection_test.rb +140 -513
  38. data/test/unit/machine_test.rb +212 -10
  39. data/test/unit/state_test.rb +2 -1
  40. data/test/unit/transition_collection_test.rb +2098 -0
  41. data/test/unit/transition_test.rb +704 -552
  42. metadata +16 -3
@@ -1,1239 +1,1468 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
2
 
3
- begin
4
- # Load library
5
- require 'rubygems'
3
+ # Load library
4
+ require 'rubygems'
5
+
6
+ gem 'sequel', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=2.8.0'
7
+ require 'sequel'
8
+ require 'logger'
9
+
10
+ # Establish database connection
11
+ DB = Sequel.connect('sqlite:///', :loggers => [Logger.new("#{File.dirname(__FILE__)}/../../sequel.log")])
12
+
13
+ module SequelTest
14
+ class BaseTestCase < Test::Unit::TestCase
15
+ def default_test
16
+ end
17
+
18
+ protected
19
+ # Creates a new Sequel model (and the associated table)
20
+ def new_model(create_table = :foo, &block)
21
+ table_name = create_table || :foo
22
+
23
+ DB.create_table!(table_name) do
24
+ primary_key :id
25
+ column :state, :string
26
+ end if create_table
27
+ model = Class.new(Sequel::Model(table_name)) do
28
+ self.raise_on_save_failure = false
29
+ def self.name; "SequelTest::#{table_name.to_s.capitalize}"; end
30
+ end
31
+ model.plugin(:validation_class_methods) if model.respond_to?(:plugin)
32
+ model.plugin(:hook_class_methods) if model.respond_to?(:plugin)
33
+ model.class_eval(&block) if block_given?
34
+ model
35
+ end
36
+ end
6
37
 
7
- gem 'sequel', ENV['SEQUEL_VERSION'] ? "=#{ENV['SEQUEL_VERSION']}" : '>=2.8.0'
8
- require 'sequel'
9
- require 'logger'
38
+ class IntegrationTest < BaseTestCase
39
+ def test_should_match_if_class_inherits_from_sequel
40
+ assert StateMachine::Integrations::Sequel.matches?(new_model)
41
+ end
42
+
43
+ def test_should_not_match_if_class_does_not_inherit_from_sequel
44
+ assert !StateMachine::Integrations::Sequel.matches?(Class.new)
45
+ end
46
+
47
+ def test_should_have_defaults
48
+ assert_equal e = {:action => :save}, StateMachine::Integrations::Sequel.defaults
49
+ end
50
+ end
10
51
 
11
- # Establish database connection
12
- DB = Sequel.connect('sqlite:///', :loggers => [Logger.new("#{File.dirname(__FILE__)}/../../sequel.log")])
52
+ class MachineWithoutDatabaseTest < BaseTestCase
53
+ def setup
54
+ @model = new_model(false)
55
+ end
56
+
57
+ def test_should_allow_machine_creation
58
+ assert_nothing_raised { StateMachine::Machine.new(@model) }
59
+ end
60
+ end
13
61
 
14
- module SequelTest
15
- class BaseTestCase < Test::Unit::TestCase
16
- def default_test
17
- end
18
-
19
- protected
20
- # Creates a new Sequel model (and the associated table)
21
- def new_model(create_table = :foo, &block)
22
- table_name = create_table || :foo
23
-
24
- DB.create_table!(table_name) do
25
- primary_key :id
26
- column :state, :string
27
- end if create_table
28
- model = Class.new(Sequel::Model(table_name)) do
29
- self.raise_on_save_failure = false
30
- def self.name; "SequelTest::#{table_name.to_s.capitalize}"; end
31
- end
32
- model.plugin(:validation_class_methods) if model.respond_to?(:plugin)
33
- model.plugin(:hook_class_methods) if model.respond_to?(:plugin)
34
- model.class_eval(&block) if block_given?
35
- model
36
- end
62
+ class MachineUnmigratedTest < BaseTestCase
63
+ def setup
64
+ @model = new_model(false)
37
65
  end
38
66
 
39
- class IntegrationTest < BaseTestCase
40
- def test_should_match_if_class_inherits_from_sequel
41
- assert StateMachine::Integrations::Sequel.matches?(new_model)
42
- end
43
-
44
- def test_should_not_match_if_class_does_not_inherit_from_sequel
45
- assert !StateMachine::Integrations::Sequel.matches?(Class.new)
46
- end
47
-
48
- def test_should_have_defaults
49
- assert_equal e = {:action => :save}, StateMachine::Integrations::Sequel.defaults
50
- end
67
+ def test_should_allow_machine_creation
68
+ assert_nothing_raised { StateMachine::Machine.new(@model) }
69
+ end
70
+ end
71
+
72
+ class MachineByDefaultTest < BaseTestCase
73
+ def setup
74
+ @model = new_model
75
+ @machine = StateMachine::Machine.new(@model)
51
76
  end
52
77
 
53
- class MachineWithoutDatabaseTest < BaseTestCase
54
- def setup
55
- @model = new_model(false)
56
- end
57
-
58
- def test_should_allow_machine_creation
59
- assert_nothing_raised { StateMachine::Machine.new(@model) }
60
- end
78
+ def test_should_use_save_as_action
79
+ assert_equal :save, @machine.action
61
80
  end
62
81
 
63
- class MachineUnmigratedTest < BaseTestCase
64
- def setup
65
- @model = new_model(false)
66
- end
67
-
68
- def test_should_allow_machine_creation
69
- assert_nothing_raised { StateMachine::Machine.new(@model) }
70
- end
82
+ def test_should_use_transactions
83
+ assert_equal true, @machine.use_transactions
71
84
  end
72
85
 
73
- class MachineByDefaultTest < BaseTestCase
74
- def setup
75
- @model = new_model
76
- @machine = StateMachine::Machine.new(@model)
77
- end
78
-
79
- def test_should_use_save_as_action
80
- assert_equal :save, @machine.action
86
+ def test_should_not_have_any_before_callbacks
87
+ assert_equal 0, @machine.callbacks[:before].size
88
+ end
89
+
90
+ def test_should_not_have_any_after_callbacks
91
+ assert_equal 0, @machine.callbacks[:after].size
92
+ end
93
+ end
94
+
95
+ class MachineWithStaticInitialStateTest < BaseTestCase
96
+ def setup
97
+ @model = new_model do
98
+ attr_accessor :value
81
99
  end
82
-
83
- def test_should_use_transactions
84
- assert_equal true, @machine.use_transactions
100
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
101
+ end
102
+
103
+ def test_should_set_initial_state_on_created_object
104
+ record = @model.new
105
+ assert_equal 'parked', record.state
106
+ end
107
+
108
+ def test_should_set_initial_state_with_nil_attributes
109
+ @model.class_eval do
110
+ def set(hash)
111
+ super(hash || {})
112
+ end
85
113
  end
86
114
 
87
- def test_should_not_have_any_before_callbacks
88
- assert_equal 0, @machine.callbacks[:before].size
115
+ record = @model.new(nil)
116
+ assert_equal 'parked', record.state
117
+ end
118
+
119
+ def test_should_still_set_attributes
120
+ record = @model.new(:value => 1)
121
+ assert_equal 1, record.value
122
+ end
123
+
124
+ def test_should_still_allow_initialize_blocks
125
+ block_args = nil
126
+ record = @model.new do |*args|
127
+ block_args = args
89
128
  end
90
129
 
91
- def test_should_not_have_any_after_callbacks
92
- assert_equal 0, @machine.callbacks[:after].size
130
+ assert_equal [record], block_args
131
+ end
132
+
133
+ def test_should_set_attributes_prior_to_after_initialize_hook
134
+ state = nil
135
+ @model.class_eval do
136
+ define_method(:after_initialize) do
137
+ state = self.state
138
+ end
93
139
  end
140
+ @model.new
141
+ assert_equal 'parked', state
94
142
  end
95
143
 
96
- class MachineWithStaticInitialStateTest < BaseTestCase
97
- def setup
98
- @model = new_model do
99
- attr_accessor :value
144
+ def test_should_set_initial_state_before_setting_attributes
145
+ @model.class_eval do
146
+ attr_accessor :state_during_setter
147
+
148
+ define_method(:value=) do |value|
149
+ self.state_during_setter = state
100
150
  end
101
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
102
151
  end
103
152
 
104
- def test_should_set_initial_state_on_created_object
105
- record = @model.new
106
- assert_equal 'parked', record.state
107
- end
153
+ record = @model.new(:value => 1)
154
+ assert_equal 'parked', record.state_during_setter
155
+ end
156
+
157
+ def test_should_not_set_initial_state_after_already_initialized
158
+ record = @model.new(:value => 1)
159
+ assert_equal 'parked', record.state
108
160
 
109
- def test_should_set_initial_state_with_nil_attributes
110
- @model.class_eval do
111
- def set(hash)
112
- super(hash || {})
113
- end
114
- end
115
-
116
- record = @model.new(nil)
117
- assert_equal 'parked', record.state
118
- end
161
+ record.state = 'idling'
162
+ record.set({})
163
+ assert_equal 'idling', record.state
164
+ end
165
+
166
+ def test_should_use_stored_values_when_loading_from_database
167
+ @machine.state :idling
119
168
 
120
- def test_should_still_set_attributes
121
- record = @model.new(:value => 1)
122
- assert_equal 1, record.value
123
- end
169
+ record = @model[@model.create(:state => 'idling').id]
170
+ assert_equal 'idling', record.state
171
+ end
172
+
173
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
174
+ @machine.state nil
124
175
 
125
- def test_should_still_allow_initialize_blocks
126
- block_args = nil
127
- record = @model.new do |*args|
128
- block_args = args
129
- end
130
-
131
- assert_equal [record], block_args
176
+ record = @model[@model.create(:state => nil).id]
177
+ assert_nil record.state
178
+ end
179
+ end
180
+
181
+ class MachineWithDynamicInitialStateTest < BaseTestCase
182
+ def setup
183
+ @model = new_model do
184
+ attr_accessor :value
132
185
  end
133
-
134
- def test_should_set_attributes_prior_to_after_initialize_hook
135
- state = nil
136
- @model.class_eval do
137
- define_method(:after_initialize) do
138
- state = self.state
139
- end
140
- end
141
- @model.new
142
- assert_equal 'parked', state
186
+ @machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked})
187
+ @machine.state :parked
188
+ end
189
+
190
+ def test_should_set_initial_state_on_created_object
191
+ record = @model.new
192
+ assert_equal 'parked', record.state
193
+ end
194
+
195
+ def test_should_still_set_attributes
196
+ record = @model.new(:value => 1)
197
+ assert_equal 1, record.value
198
+ end
199
+
200
+ def test_should_still_allow_initialize_blocks
201
+ block_args = nil
202
+ record = @model.new do |*args|
203
+ block_args = args
143
204
  end
144
205
 
145
- def test_should_set_initial_state_before_setting_attributes
146
- @model.class_eval do
147
- attr_accessor :state_during_setter
148
-
149
- define_method(:value=) do |value|
150
- self.state_during_setter = state
151
- end
206
+ assert_equal [record], block_args
207
+ end
208
+
209
+ def test_should_not_have_any_changed_columns
210
+ record = @model.new
211
+ assert record.changed_columns.empty?
212
+ end
213
+
214
+ def test_should_set_attributes_prior_to_after_initialize_hook
215
+ state = nil
216
+ @model.class_eval do
217
+ define_method(:after_initialize) do
218
+ state = self.state
152
219
  end
153
-
154
- record = @model.new(:value => 1)
155
- assert_equal 'parked', record.state_during_setter
156
- end
157
-
158
- def test_should_not_set_initial_state_after_already_initialized
159
- record = @model.new(:value => 1)
160
- assert_equal 'parked', record.state
161
-
162
- record.state = 'idling'
163
- record.set({})
164
- assert_equal 'idling', record.state
165
220
  end
221
+ @model.new
222
+ assert_equal 'parked', state
166
223
  end
167
224
 
168
- class MachineWithDynamicInitialStateTest < BaseTestCase
169
- def setup
170
- @model = new_model do
171
- attr_accessor :value
225
+ def test_should_set_initial_state_after_setting_attributes
226
+ @model.class_eval do
227
+ attr_accessor :state_during_setter
228
+
229
+ define_method(:value=) do |value|
230
+ self.state_during_setter = state || 'nil'
172
231
  end
173
- @machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked})
174
- @machine.state :parked
175
232
  end
176
233
 
177
- def test_should_set_initial_state_on_created_object
178
- record = @model.new
179
- assert_equal 'parked', record.state
180
- end
234
+ record = @model.new(:value => 1)
235
+ assert_equal 'nil', record.state_during_setter
236
+ end
237
+
238
+ def test_should_not_set_initial_state_after_already_initialized
239
+ record = @model.new(:value => 1)
240
+ assert_equal 'parked', record.state
181
241
 
182
- def test_should_still_set_attributes
183
- record = @model.new(:value => 1)
184
- assert_equal 1, record.value
185
- end
242
+ record.state = 'idling'
243
+ record.set({})
244
+ assert_equal 'idling', record.state
245
+ end
246
+
247
+ def test_should_use_stored_values_when_loading_from_database
248
+ @machine.state :idling
186
249
 
187
- def test_should_still_allow_initialize_blocks
188
- block_args = nil
189
- record = @model.new do |*args|
190
- block_args = args
191
- end
192
-
193
- assert_equal [record], block_args
194
- end
250
+ record = @model[@model.create(:state => 'idling').id]
251
+ assert_equal 'idling', record.state
252
+ end
253
+
254
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
255
+ @machine.state nil
195
256
 
196
- def test_should_not_have_any_changed_columns
197
- record = @model.new
198
- assert record.changed_columns.empty?
257
+ record = @model[@model.create(:state => nil).id]
258
+ assert_nil record.state
259
+ end
260
+ end
261
+
262
+ class MachineWithColumnDefaultTest < BaseTestCase
263
+ def setup
264
+ @model = new_model
265
+ DB.alter_table :foo do
266
+ add_column :status, :string, :default => 'idling'
199
267
  end
268
+ @model.class_eval { get_db_schema(true) }
200
269
 
201
- def test_should_set_attributes_prior_to_after_initialize_hook
202
- state = nil
203
- @model.class_eval do
204
- define_method(:after_initialize) do
205
- state = self.state
206
- end
270
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
271
+ @record = @model.new
272
+ end
273
+
274
+ def test_should_use_machine_default
275
+ assert_equal 'parked', @record.status
276
+ end
277
+ end
278
+
279
+ class MachineWithConflictingPredicateTest < BaseTestCase
280
+ def setup
281
+ @model = new_model do
282
+ def state?(*args)
283
+ true
207
284
  end
208
- @model.new
209
- assert_equal 'parked', state
210
285
  end
211
286
 
212
- def test_should_set_initial_state_after_setting_attributes
213
- @model.class_eval do
214
- attr_accessor :state_during_setter
215
-
216
- define_method(:value=) do |value|
217
- self.state_during_setter = state || 'nil'
218
- end
219
- end
220
-
221
- record = @model.new(:value => 1)
222
- assert_equal 'nil', record.state_during_setter
223
- end
287
+ @machine = StateMachine::Machine.new(@model)
288
+ @record = @model.new
289
+ end
290
+
291
+ def test_should_not_define_attribute_predicate
292
+ assert @record.state?
293
+ end
294
+ end
295
+
296
+ class MachineWithColumnStateAttributeTest < BaseTestCase
297
+ def setup
298
+ @model = new_model
299
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
300
+ @machine.other_states(:idling)
224
301
 
225
- def test_should_not_set_initial_state_after_already_initialized
226
- record = @model.new(:value => 1)
227
- assert_equal 'parked', record.state
228
-
229
- record.state = 'idling'
230
- record.set({})
231
- assert_equal 'idling', record.state
232
- end
302
+ @record = @model.new
303
+ end
304
+
305
+ def test_should_not_override_the_column_reader
306
+ record = @model.new
307
+ record[:state] = 'parked'
308
+ assert_equal 'parked', record.state
309
+ end
310
+
311
+ def test_should_not_override_the_column_writer
312
+ record = @model.new
313
+ record.state = 'parked'
314
+ assert_equal 'parked', record[:state]
315
+ end
316
+
317
+ def test_should_have_an_attribute_predicate
318
+ assert @record.respond_to?(:state?)
233
319
  end
234
320
 
235
- class MachineWithColumnDefaultTest < BaseTestCase
236
- def setup
237
- @model = new_model
238
- DB.alter_table :foo do
239
- add_column :status, :string, :default => 'idling'
321
+ def test_should_raise_exception_for_predicate_without_parameters
322
+ assert_raise(IndexError) { @record.state? }
323
+ end
324
+
325
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
326
+ assert !@record.state?(:idling)
327
+ end
328
+
329
+ def test_should_return_true_for_predicate_if_matches_current_value
330
+ assert @record.state?(:parked)
331
+ end
332
+
333
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
334
+ assert_raise(IndexError) { @record.state?(:invalid) }
335
+ end
336
+ end
337
+
338
+ class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
339
+ def setup
340
+ @model = new_model do
341
+ def initialize
342
+ # Skip attribute initialization
343
+ @initialized_state_machines = true
344
+ super
240
345
  end
241
- @model.class_eval { get_db_schema(true) }
242
-
243
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
244
- @record = @model.new
245
346
  end
246
347
 
247
- def test_should_use_machine_default
248
- assert_equal 'parked', @record.status
249
- end
348
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
349
+ @machine.other_states(:idling)
350
+ @record = @model.new
250
351
  end
251
352
 
252
- class MachineWithConflictingPredicateTest < BaseTestCase
253
- def setup
254
- @model = new_model do
255
- def state?(*args)
256
- true
257
- end
258
- end
259
-
260
- @machine = StateMachine::Machine.new(@model)
261
- @record = @model.new
353
+ def test_should_not_define_a_reader_attribute_for_the_attribute
354
+ assert !@record.respond_to?(:status)
355
+ end
356
+
357
+ def test_should_not_define_a_writer_attribute_for_the_attribute
358
+ assert !@record.respond_to?(:status=)
359
+ end
360
+
361
+ def test_should_define_an_attribute_predicate
362
+ assert @record.respond_to?(:status?)
363
+ end
364
+ end
365
+
366
+ class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
367
+ def setup
368
+ @model = new_model do
369
+ attr_accessor :status
262
370
  end
263
371
 
264
- def test_should_not_define_attribute_predicate
265
- assert @record.state?
266
- end
372
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
373
+ @machine.other_states(:idling)
374
+ @record = @model.new
267
375
  end
268
376
 
269
- class MachineWithColumnStateAttributeTest < BaseTestCase
270
- def setup
271
- @model = new_model
272
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
273
- @machine.other_states(:idling)
274
-
275
- @record = @model.new
377
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
378
+ assert !@record.status?(:idling)
379
+ end
380
+
381
+ def test_should_return_true_for_predicate_if_matches_current_value
382
+ assert @record.status?(:parked)
383
+ end
384
+
385
+ def test_should_set_initial_state_on_created_object
386
+ assert_equal 'parked', @record.status
387
+ end
388
+ end
389
+
390
+ class MachineWithInitializedStateTest < BaseTestCase
391
+ def setup
392
+ @model = new_model
393
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
394
+ @machine.state nil, :idling
395
+ end
396
+
397
+ def test_should_allow_nil_initial_state_when_static
398
+ record = @model.new(:state => nil)
399
+ assert_nil record.state
400
+ end
401
+
402
+ def test_should_allow_nil_initial_state_when_dynamic
403
+ @machine.initial_state = lambda {:parked}
404
+ record = @model.new(:state => nil)
405
+ assert_nil record.state
406
+ end
407
+
408
+ def test_should_allow_different_initial_state_when_static
409
+ record = @model.new(:state => 'idling')
410
+ assert_equal 'idling', record.state
411
+ end
412
+
413
+ def test_should_allow_different_initial_state_when_dynamic
414
+ @machine.initial_state = lambda {:parked}
415
+ record = @model.new(:state => 'idling')
416
+ assert_equal 'idling', record.state
417
+ end
418
+
419
+ def test_should_use_default_state_if_protected
420
+ @model.class_eval do
421
+ self.strict_param_setting = false
422
+ set_restricted_columns :state
276
423
  end
277
424
 
278
- def test_should_not_override_the_column_reader
279
- record = @model.new
280
- record[:state] = 'parked'
281
- assert_equal 'parked', record.state
425
+ record = @model.new(:state => 'idling')
426
+ assert_equal 'parked', record.state
427
+ end
428
+ end
429
+
430
+ class MachineWithAliasedAttributeTest < BaseTestCase
431
+ def setup
432
+ @model = new_model do
433
+ alias_method :vehicle_status, :state
434
+ alias_method :vehicle_status=, :state=
282
435
  end
283
436
 
284
- def test_should_not_override_the_column_writer
285
- record = @model.new
286
- record.state = 'parked'
287
- assert_equal 'parked', record[:state]
288
- end
437
+ @machine = StateMachine::Machine.new(@model, :status, :attribute => :vehicle_status)
438
+ @machine.state :parked
289
439
 
290
- def test_should_have_an_attribute_predicate
291
- assert @record.respond_to?(:state?)
292
- end
440
+ @record = @model.new
441
+ end
442
+
443
+ def test_should_add_validation_errors_to_custom_attribute
444
+ @record.vehicle_status = 'invalid'
293
445
 
294
- def test_should_raise_exception_for_predicate_without_parameters
295
- assert_raise(IndexError) { @record.state? }
296
- end
446
+ assert !@record.valid?
447
+ assert_equal ['is invalid'], @record.errors.on(:vehicle_status)
297
448
 
298
- def test_should_return_false_for_predicate_if_does_not_match_current_value
299
- assert !@record.state?(:idling)
449
+ @record.vehicle_status = 'parked'
450
+ assert @record.valid?
451
+ end
452
+ end
453
+
454
+ class MachineWithLoopbackTest < BaseTestCase
455
+ def setup
456
+ @model = new_model do
457
+ # Simulate timestamps plugin
458
+ define_method(:before_update) do
459
+ changed_columns = self.changed_columns.dup
460
+
461
+ super()
462
+ self.updated_at = Time.now if changed_columns.any?
463
+ end
300
464
  end
301
465
 
302
- def test_should_return_true_for_predicate_if_matches_current_value
303
- assert @record.state?(:parked)
466
+ DB.alter_table :foo do
467
+ add_column :updated_at, :datetime
304
468
  end
469
+ @model.class_eval { get_db_schema(true) }
305
470
 
306
- def test_should_raise_exception_for_predicate_if_invalid_state_specified
307
- assert_raise(IndexError) { @record.state?(:invalid) }
308
- end
471
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
472
+ @machine.event :park
473
+
474
+ @record = @model.create(:updated_at => Time.now - 1)
475
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
476
+
477
+ @timestamp = @record.updated_at
478
+ @transition.perform
309
479
  end
310
480
 
311
- class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
312
- def setup
313
- @model = new_model do
314
- def initialize
315
- # Skip attribute initialization
316
- @initialized_state_machines = true
317
- super
318
- end
319
- end
320
-
321
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
322
- @machine.other_states(:idling)
323
- @record = @model.new
324
- end
481
+ def test_should_update_record
482
+ assert_not_equal @timestamp, @record.updated_at
483
+ end
484
+ end
485
+
486
+ class MachineWithDirtyAttributesTest < BaseTestCase
487
+ def setup
488
+ @model = new_model
489
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
490
+ @machine.event :ignite
491
+ @machine.state :idling
325
492
 
326
- def test_should_not_define_a_reader_attribute_for_the_attribute
327
- assert !@record.respond_to?(:status)
328
- end
493
+ @record = @model.create
329
494
 
330
- def test_should_not_define_a_writer_attribute_for_the_attribute
331
- assert !@record.respond_to?(:status=)
332
- end
495
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
496
+ @transition.perform(false)
497
+ end
498
+
499
+ def test_should_include_state_in_changed_attributes
500
+ assert_equal [:state], @record.changed_columns
501
+ end
502
+
503
+ def test_should_not_have_changes_when_loaded_from_database
504
+ record = @model[@record.id]
505
+ assert record.changed_columns.empty?
506
+ end
507
+ end
508
+
509
+ class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
510
+ def setup
511
+ @model = new_model
512
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
513
+ @machine.event :park
333
514
 
334
- def test_should_define_an_attribute_predicate
335
- assert @record.respond_to?(:status?)
336
- end
515
+ @record = @model.create
516
+
517
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
518
+ @transition.perform(false)
337
519
  end
338
520
 
339
- class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
340
- def setup
341
- @model = new_model do
342
- attr_accessor :status
343
- end
344
-
345
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
346
- @machine.other_states(:idling)
347
- @record = @model.new
521
+ def test_should_include_state_in_changed_attributes
522
+ assert_equal [:state], @record.changed_columns
523
+ end
524
+ end
525
+
526
+ class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
527
+ def setup
528
+ @model = new_model
529
+ DB.alter_table :foo do
530
+ add_column :status, :string, :default => 'idling'
348
531
  end
532
+ @model.class_eval { get_db_schema(true) }
349
533
 
350
- def test_should_return_false_for_predicate_if_does_not_match_current_value
351
- assert !@record.status?(:idling)
352
- end
534
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
535
+ @machine.event :ignite
536
+ @machine.state :idling
353
537
 
354
- def test_should_return_true_for_predicate_if_matches_current_value
355
- assert @record.status?(:parked)
356
- end
538
+ @record = @model.create
357
539
 
358
- def test_should_set_initial_state_on_created_object
359
- assert_equal 'parked', @record.status
360
- end
540
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
541
+ @transition.perform(false)
361
542
  end
362
543
 
363
- class MachineWithInitializedStateTest < BaseTestCase
364
- def setup
365
- @model = new_model
366
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
367
- @machine.state nil, :idling
544
+ def test_should_include_state_in_changed_attributes
545
+ assert_equal [:status], @record.changed_columns
546
+ end
547
+ end
548
+
549
+ class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
550
+ def setup
551
+ @model = new_model
552
+ DB.alter_table :foo do
553
+ add_column :status, :string, :default => 'idling'
368
554
  end
555
+ @model.class_eval { get_db_schema(true) }
369
556
 
370
- def test_should_allow_nil_initial_state_when_static
371
- record = @model.new(:state => nil)
372
- assert_nil record.state
373
- end
557
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
558
+ @machine.event :park
374
559
 
375
- def test_should_allow_nil_initial_state_when_dynamic
376
- @machine.initial_state = lambda {:parked}
377
- record = @model.new(:state => nil)
378
- assert_nil record.state
379
- end
560
+ @record = @model.create
380
561
 
381
- def test_should_allow_different_initial_state_when_static
382
- record = @model.new(:state => 'idling')
383
- assert_equal 'idling', record.state
562
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
563
+ @transition.perform(false)
564
+ end
565
+
566
+ def test_should_include_state_in_changed_attributes
567
+ assert_equal [:status], @record.changed_columns
568
+ end
569
+ end
570
+
571
+ class MachineWithoutTransactionsTest < BaseTestCase
572
+ def setup
573
+ @model = new_model
574
+ @machine = StateMachine::Machine.new(@model, :use_transactions => false)
575
+ end
576
+
577
+ def test_should_not_rollback_transaction_if_false
578
+ @machine.within_transaction(@model.new) do
579
+ @model.create
580
+ false
384
581
  end
385
582
 
386
- def test_should_allow_different_initial_state_when_dynamic
387
- @machine.initial_state = lambda {:parked}
388
- record = @model.new(:state => 'idling')
389
- assert_equal 'idling', record.state
583
+ assert_equal 1, @model.count
584
+ end
585
+
586
+ def test_should_not_rollback_transaction_if_true
587
+ @machine.within_transaction(@model.new) do
588
+ @model.create
589
+ true
390
590
  end
391
591
 
392
- def test_should_use_default_state_if_protected
393
- @model.class_eval do
394
- self.strict_param_setting = false
395
- set_restricted_columns :state
396
- end
397
-
398
- record = @model.new(:state => 'idling')
399
- assert_equal 'parked', record.state
400
- end
592
+ assert_equal 1, @model.count
593
+ end
594
+ end
595
+
596
+ class MachineWithTransactionsTest < BaseTestCase
597
+ def setup
598
+ @model = new_model
599
+ @machine = StateMachine::Machine.new(@model, :use_transactions => true)
401
600
  end
402
601
 
403
- class MachineWithAliasedAttributeTest < BaseTestCase
404
- def setup
405
- @model = new_model do
406
- alias_method :vehicle_status, :state
407
- alias_method :vehicle_status=, :state=
408
- end
409
-
410
- @machine = StateMachine::Machine.new(@model, :status, :attribute => :vehicle_status)
411
- @machine.state :parked
412
-
413
- @record = @model.new
602
+ def test_should_rollback_transaction_if_false
603
+ @machine.within_transaction(@model.new) do
604
+ @model.create
605
+ false
414
606
  end
415
607
 
416
- def test_should_add_validation_errors_to_custom_attribute
417
- @record.vehicle_status = 'invalid'
418
-
419
- assert !@record.valid?
420
- assert_equal ['is invalid'], @record.errors.on(:vehicle_status)
421
-
422
- @record.vehicle_status = 'parked'
423
- assert @record.valid?
424
- end
608
+ assert_equal 0, @model.count
425
609
  end
426
610
 
427
- class MachineWithLoopbackTest < BaseTestCase
428
- def setup
429
- @model = new_model do
430
- # Simulate timestamps plugin
431
- define_method(:before_update) do
432
- changed_columns = self.changed_columns.dup
433
-
434
- super()
435
- self.updated_at = Time.now if changed_columns.any?
436
- end
437
- end
438
-
439
- DB.alter_table :foo do
440
- add_column :updated_at, :datetime
441
- end
442
- @model.class_eval { get_db_schema(true) }
443
-
444
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
445
- @machine.event :park
446
-
447
- @record = @model.create(:updated_at => Time.now - 1)
448
- @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
449
-
450
- @timestamp = @record.updated_at
451
- @transition.perform
611
+ def test_should_not_rollback_transaction_if_true
612
+ @machine.within_transaction(@model.new) do
613
+ @model.create
614
+ true
452
615
  end
453
616
 
454
- def test_should_update_record
455
- assert_not_equal @timestamp, @record.updated_at
456
- end
617
+ assert_equal 1, @model.count
618
+ end
619
+ end
620
+
621
+ class MachineWithCallbacksTest < BaseTestCase
622
+ def setup
623
+ @model = new_model
624
+ @machine = StateMachine::Machine.new(@model)
625
+ @machine.state :parked, :idling
626
+ @machine.event :ignite
627
+
628
+ @record = @model.new(:state => 'parked')
629
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
457
630
  end
458
631
 
459
- class MachineWithDirtyAttributesTest < BaseTestCase
460
- def setup
461
- @model = new_model
462
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
463
- @machine.event :ignite
464
- @machine.state :idling
465
-
466
- @record = @model.create
467
-
468
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
469
- @transition.perform(false)
470
- end
632
+ def test_should_run_before_callbacks
633
+ called = false
634
+ @machine.before_transition {called = true}
471
635
 
472
- def test_should_include_state_in_changed_attributes
473
- assert_equal [:state], @record.changed_columns
474
- end
636
+ @transition.perform
637
+ assert called
475
638
  end
476
639
 
477
- class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
478
- def setup
479
- @model = new_model
480
- @machine = StateMachine::Machine.new(@model, :initial => :parked)
481
- @machine.event :park
482
-
483
- @record = @model.create
484
-
485
- @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
486
- @transition.perform(false)
487
- end
640
+ def test_should_pass_transition_to_before_callbacks_with_one_argument
641
+ transition = nil
642
+ @machine.before_transition {|arg| transition = arg}
488
643
 
489
- def test_should_include_state_in_changed_attributes
490
- assert_equal [:state], @record.changed_columns
491
- end
644
+ @transition.perform
645
+ assert_equal @transition, transition
492
646
  end
493
647
 
494
- class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
495
- def setup
496
- @model = new_model
497
- DB.alter_table :foo do
498
- add_column :status, :string, :default => 'idling'
499
- end
500
- @model.class_eval { get_db_schema(true) }
501
-
502
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
503
- @machine.event :ignite
504
- @machine.state :idling
505
-
506
- @record = @model.create
507
-
508
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
509
- @transition.perform(false)
510
- end
648
+ def test_should_pass_transition_to_before_callbacks_with_multiple_arguments
649
+ callback_args = nil
650
+ @machine.before_transition {|*args| callback_args = args}
511
651
 
512
- def test_should_include_state_in_changed_attributes
513
- assert_equal [:status], @record.changed_columns
514
- end
652
+ @transition.perform
653
+ assert_equal [@transition], callback_args
515
654
  end
516
655
 
517
- class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
518
- def setup
519
- @model = new_model
520
- DB.alter_table :foo do
521
- add_column :status, :string, :default => 'idling'
522
- end
523
- @model.class_eval { get_db_schema(true) }
524
-
525
- @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
526
- @machine.event :park
527
-
528
- @record = @model.create
529
-
530
- @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
531
- @transition.perform(false)
532
- end
656
+ def test_should_run_before_callbacks_within_the_context_of_the_record
657
+ context = nil
658
+ @machine.before_transition {context = self}
533
659
 
534
- def test_should_include_state_in_changed_attributes
535
- assert_equal [:status], @record.changed_columns
536
- end
660
+ @transition.perform
661
+ assert_equal @record, context
537
662
  end
538
663
 
539
- class MachineWithoutTransactionsTest < BaseTestCase
540
- def setup
541
- @model = new_model
542
- @machine = StateMachine::Machine.new(@model, :use_transactions => false)
543
- end
664
+ def test_should_run_after_callbacks
665
+ called = false
666
+ @machine.after_transition {called = true}
544
667
 
545
- def test_should_not_rollback_transaction_if_false
546
- @machine.within_transaction(@model.new) do
547
- @model.create
548
- false
549
- end
550
-
551
- assert_equal 1, @model.count
552
- end
668
+ @transition.perform
669
+ assert called
670
+ end
671
+
672
+ def test_should_pass_transition_to_after_callbacks_with_multiple_arguments
673
+ callback_args = nil
674
+ @machine.after_transition {|*args| callback_args = args}
553
675
 
554
- def test_should_not_rollback_transaction_if_true
555
- @machine.within_transaction(@model.new) do
556
- @model.create
557
- true
558
- end
559
-
560
- assert_equal 1, @model.count
561
- end
676
+ @transition.perform
677
+ assert_equal [@transition], callback_args
562
678
  end
563
679
 
564
- class MachineWithTransactionsTest < BaseTestCase
565
- def setup
566
- @model = new_model
567
- @machine = StateMachine::Machine.new(@model, :use_transactions => true)
568
- end
680
+ def test_should_run_after_callbacks_with_the_context_of_the_record
681
+ context = nil
682
+ @machine.after_transition {context = self}
569
683
 
570
- def test_should_rollback_transaction_if_false
571
- @machine.within_transaction(@model.new) do
572
- @model.create
573
- false
574
- end
575
-
576
- assert_equal 0, @model.count
577
- end
684
+ @transition.perform
685
+ assert_equal @record, context
686
+ end
687
+
688
+ def test_should_run_around_callbacks
689
+ before_called = false
690
+ after_called = [false]
691
+ @machine.around_transition {|block| before_called = true; block.call; after_called[0] = true}
692
+
693
+ @transition.perform
694
+ assert before_called
695
+ assert after_called[0]
696
+ end
697
+
698
+ def test_should_run_around_callbacks_with_the_context_of_the_record
699
+ context = nil
700
+ @machine.around_transition {|block| context = self; block.call}
578
701
 
579
- def test_should_not_rollback_transaction_if_true
580
- @machine.within_transaction(@model.new) do
581
- @model.create
582
- true
583
- end
584
-
585
- assert_equal 1, @model.count
586
- end
702
+ @transition.perform
703
+ assert_equal @record, context
587
704
  end
588
705
 
589
- class MachineWithCallbacksTest < BaseTestCase
590
- def setup
591
- @model = new_model
592
- @machine = StateMachine::Machine.new(@model)
593
- @machine.state :parked, :idling
594
- @machine.event :ignite
595
-
596
- @record = @model.new(:state => 'parked')
597
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
598
- end
706
+ def test_should_allow_symbolic_callbacks
707
+ callback_args = nil
599
708
 
600
- def test_should_run_before_callbacks
601
- called = false
602
- @machine.before_transition(lambda {called = true})
603
-
604
- @transition.perform
605
- assert called
709
+ klass = class << @record; self; end
710
+ klass.send(:define_method, :after_ignite) do |*args|
711
+ callback_args = args
606
712
  end
607
713
 
608
- def test_should_pass_transition_to_before_callbacks_with_one_argument
609
- transition = nil
610
- @machine.before_transition(lambda {|arg| transition = arg})
611
-
612
- @transition.perform
613
- assert_equal @transition, transition
614
- end
714
+ @machine.before_transition(:after_ignite)
615
715
 
616
- def test_should_pass_transition_to_before_callbacks_with_multiple_arguments
617
- callback_args = nil
618
- @machine.before_transition(lambda {|*args| callback_args = args})
619
-
620
- @transition.perform
621
- assert_equal [@transition], callback_args
716
+ @transition.perform
717
+ assert_equal [@transition], callback_args
718
+ end
719
+
720
+ def test_should_allow_string_callbacks
721
+ class << @record
722
+ attr_reader :callback_result
622
723
  end
623
724
 
624
- def test_should_run_before_callbacks_within_the_context_of_the_record
625
- context = nil
626
- @machine.before_transition(lambda {context = self})
627
-
628
- @transition.perform
629
- assert_equal @record, context
630
- end
725
+ @machine.before_transition('@callback_result = [1, 2, 3]')
726
+ @transition.perform
631
727
 
632
- def test_should_run_after_callbacks
633
- called = false
634
- @machine.after_transition(lambda {called = true})
635
-
636
- @transition.perform
637
- assert called
728
+ assert_equal [1, 2, 3], @record.callback_result
729
+ end
730
+ end
731
+
732
+ class MachineWithFailedBeforeCallbacksTest < BaseTestCase
733
+ def setup
734
+ callbacks = []
735
+
736
+ @model = new_model
737
+ @machine = StateMachine::Machine.new(@model)
738
+ @machine.state :parked, :idling
739
+ @machine.event :ignite
740
+ @machine.before_transition {callbacks << :before_1; false}
741
+ @machine.before_transition {callbacks << :before_2}
742
+ @machine.after_transition {callbacks << :after}
743
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
744
+
745
+ @record = @model.new(:state => 'parked')
746
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
747
+ @result = @transition.perform
748
+
749
+ @callbacks = callbacks
750
+ end
751
+
752
+ def test_should_not_be_successful
753
+ assert !@result
754
+ end
755
+
756
+ def test_should_not_change_current_state
757
+ assert_equal 'parked', @record.state
758
+ end
759
+
760
+ def test_should_not_run_action
761
+ assert @record.new?
762
+ end
763
+
764
+ def test_should_not_run_further_callbacks
765
+ assert_equal [:before_1], @callbacks
766
+ end
767
+ end
768
+
769
+ class MachineWithFailedActionTest < BaseTestCase
770
+ def setup
771
+ @model = new_model do
772
+ validates_each :state do |object, attribute, value|
773
+ object.errors[attribute] << 'is invalid' unless %w(first_gear).include?(value)
774
+ end
638
775
  end
639
776
 
640
- def test_should_pass_transition_to_after_callbacks_with_multiple_arguments
641
- callback_args = nil
642
- @machine.after_transition(lambda {|*args| callback_args = args})
643
-
644
- @transition.perform
645
- assert_equal [@transition], callback_args
646
- end
777
+ @machine = StateMachine::Machine.new(@model)
778
+ @machine.state :parked, :idling
779
+ @machine.event :ignite
647
780
 
648
- def test_should_run_after_callbacks_with_the_context_of_the_record
649
- context = nil
650
- @machine.after_transition(lambda {context = self})
651
-
652
- @transition.perform
653
- assert_equal @record, context
781
+ callbacks = []
782
+ @machine.before_transition {callbacks << :before}
783
+ @machine.after_transition {callbacks << :after}
784
+ @machine.after_transition(:include_failures => true) {callbacks << :after_failure}
785
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
786
+ @machine.around_transition(:include_failures => true) do |block|
787
+ callbacks << :around_before_failure
788
+ block.call
789
+ callbacks << :around_after_failure
654
790
  end
655
791
 
656
- def test_should_allow_symbolic_callbacks
657
- callback_args = nil
658
-
659
- klass = class << @record; self; end
660
- klass.send(:define_method, :after_ignite) do |*args|
661
- callback_args = args
662
- end
663
-
664
- @machine.before_transition(:after_ignite)
665
-
666
- @transition.perform
667
- assert_equal [@transition], callback_args
668
- end
792
+ @record = @model.new(:state => 'parked')
793
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
794
+ @result = @transition.perform
669
795
 
670
- def test_should_allow_string_callbacks
671
- class << @record
672
- attr_reader :callback_result
673
- end
674
-
675
- @machine.before_transition('@callback_result = [1, 2, 3]')
676
- @transition.perform
677
-
678
- assert_equal [1, 2, 3], @record.callback_result
679
- end
796
+ @callbacks = callbacks
680
797
  end
681
798
 
682
- class MachineWithFailedBeforeCallbacksTest < BaseTestCase
683
- def setup
684
- before_count = 0
685
- after_count = 0
686
-
687
- @model = new_model
688
- @machine = StateMachine::Machine.new(@model)
689
- @machine.state :parked, :idling
690
- @machine.event :ignite
691
- @machine.before_transition(lambda {before_count += 1; false})
692
- @machine.before_transition(lambda {before_count += 1})
693
- @machine.after_transition(lambda {after_count += 1})
694
-
695
- @record = @model.new(:state => 'parked')
696
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
697
- @result = @transition.perform
698
-
699
- @before_count = before_count
700
- @after_count = after_count
701
- end
799
+ def test_should_not_be_successful
800
+ assert !@result
801
+ end
802
+
803
+ def test_should_not_change_current_state
804
+ assert_equal 'parked', @record.state
805
+ end
806
+
807
+ def test_should_not_save_record
808
+ assert @record.new?
809
+ end
810
+
811
+ def test_should_run_before_callbacks_and_after_callbacks_with_failures
812
+ assert_equal [:before, :around_before, :around_before_failure, :around_after_failure, :after_failure], @callbacks
813
+ end
814
+ end
815
+
816
+ class MachineWithFailedAfterCallbacksTest < BaseTestCase
817
+ def setup
818
+ callbacks = []
819
+
820
+ @model = new_model
821
+ @machine = StateMachine::Machine.new(@model)
822
+ @machine.state :parked, :idling
823
+ @machine.event :ignite
824
+ @machine.after_transition {callbacks << :after_1; false}
825
+ @machine.after_transition {callbacks << :after_2}
826
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
827
+
828
+ @record = @model.new(:state => 'parked')
829
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
830
+ @result = @transition.perform
831
+
832
+ @callbacks = callbacks
833
+ end
834
+
835
+ def test_should_be_successful
836
+ assert @result
837
+ end
838
+
839
+ def test_should_change_current_state
840
+ assert_equal 'idling', @record.state
841
+ end
842
+
843
+ def test_should_save_record
844
+ assert !@record.new?
845
+ end
846
+
847
+ def test_should_not_run_further_after_callbacks
848
+ assert_equal [:around_before, :around_after, :after_1], @callbacks
849
+ end
850
+ end
851
+
852
+ class MachineWithValidationsTest < BaseTestCase
853
+ def setup
854
+ @model = new_model
855
+ @machine = StateMachine::Machine.new(@model)
856
+ @machine.state :parked
702
857
 
703
- def test_should_not_be_successful
704
- assert !@result
705
- end
858
+ @record = @model.new
859
+ end
860
+
861
+ def test_should_invalidate_using_errors
862
+ @record.state = 'parked'
706
863
 
707
- def test_should_not_change_current_state
708
- assert_equal 'parked', @record.state
709
- end
864
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, :park]])
865
+ assert_equal ['cannot transition via "park"'], @record.errors.on(:state)
866
+ end
867
+
868
+ def test_should_auto_prefix_custom_attributes_on_invalidation
869
+ @machine.invalidate(@record, :event, :invalid)
710
870
 
711
- def test_should_not_run_action
712
- assert @record.new?
713
- end
871
+ assert_equal ['is invalid'], @record.errors.on(:state_event)
872
+ end
873
+
874
+ def test_should_clear_errors_on_reset
875
+ @record.state = 'parked'
876
+ @record.errors.add(:state, 'is invalid')
714
877
 
715
- def test_should_not_run_further_before_callbacks
716
- assert_equal 1, @before_count
717
- end
878
+ @machine.reset(@record)
879
+ assert_nil @record.errors.on(:id)
880
+ end
881
+
882
+ def test_should_be_valid_if_state_is_known
883
+ @record.state = 'parked'
718
884
 
719
- def test_should_not_run_after_callbacks
720
- assert_equal 0, @after_count
721
- end
885
+ assert @record.valid?
722
886
  end
723
887
 
724
- class MachineWithFailedActionTest < BaseTestCase
725
- def setup
726
- @model = new_model do
727
- validates_each :state do |object, attribute, value|
728
- object.errors[attribute] << 'is invalid' unless %w(first_gear).include?(value)
729
- end
730
- end
731
-
732
- @machine = StateMachine::Machine.new(@model)
733
- @machine.state :parked, :idling
734
- @machine.event :ignite
735
-
736
- before_transition_called = false
737
- after_transition_called = false
738
- after_transition_with_failures_called = false
739
- @machine.before_transition(lambda {before_transition_called = true})
740
- @machine.after_transition(lambda {after_transition_called = true})
741
- @machine.after_transition(lambda {after_transition_with_failures_called = true}, :include_failures => true)
742
-
743
- @record = @model.new(:state => 'parked')
744
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
745
- @result = @transition.perform
746
-
747
- @before_transition_called = before_transition_called
748
- @after_transition_called = after_transition_called
749
- @after_transition_with_failures_called = after_transition_with_failures_called
750
- end
888
+ def test_should_not_be_valid_if_state_is_unknown
889
+ @record.state = 'invalid'
751
890
 
752
- def test_should_not_be_successful
753
- assert !@result
891
+ assert !@record.valid?
892
+ assert_equal ['state is invalid'], @record.errors.full_messages
893
+ end
894
+ end
895
+
896
+ class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
897
+ def setup
898
+ @model = new_model do
899
+ alias_method :status, :state
900
+ alias_method :status=, :state=
754
901
  end
755
902
 
756
- def test_should_not_change_current_state
757
- assert_equal 'parked', @record.state
758
- end
903
+ @machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
904
+ @machine.state :parked
759
905
 
760
- def test_should_not_save_record
761
- assert @record.new?
762
- end
906
+ @record = @model.new
907
+ end
908
+
909
+ def test_should_add_validation_errors_to_custom_attribute
910
+ @record.state = 'invalid'
763
911
 
764
- def test_should_run_before_callback
765
- assert @before_transition_called
766
- end
912
+ assert !@record.valid?
913
+ assert_equal ['state is invalid'], @record.errors.full_messages
767
914
 
768
- def test_should_not_run_after_callback_if_not_including_failures
769
- assert !@after_transition_called
915
+ @record.state = 'parked'
916
+ assert @record.valid?
917
+ end
918
+ end
919
+
920
+ class MachineWithStateDrivenValidationsTest < BaseTestCase
921
+ def setup
922
+ @model = new_model do
923
+ attr_accessor :seatbelt
770
924
  end
771
925
 
772
- def test_should_run_after_callback_if_including_failures
773
- assert @after_transition_with_failures_called
926
+ @machine = StateMachine::Machine.new(@model)
927
+ @machine.state :first_gear do
928
+ validates_presence_of :seatbelt
774
929
  end
930
+ @machine.other_states :parked
775
931
  end
776
932
 
777
- class MachineWithFailedAfterCallbacksTest < BaseTestCase
778
- def setup
779
- after_count = 0
780
-
781
- @model = new_model
782
- @machine = StateMachine::Machine.new(@model)
783
- @machine.state :parked, :idling
784
- @machine.event :ignite
785
- @machine.after_transition(lambda {after_count += 1; false})
786
- @machine.after_transition(lambda {after_count += 1})
787
-
788
- @record = @model.new(:state => 'parked')
789
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
790
- @result = @transition.perform
791
-
792
- @after_count = after_count
933
+ def test_should_be_valid_if_validation_fails_outside_state_scope
934
+ record = @model.new(:state => 'parked', :seatbelt => nil)
935
+ assert record.valid?
936
+ end
937
+
938
+ def test_should_be_invalid_if_validation_fails_within_state_scope
939
+ record = @model.new(:state => 'first_gear', :seatbelt => nil)
940
+ assert !record.valid?
941
+ end
942
+
943
+ def test_should_be_valid_if_validation_succeeds_within_state_scope
944
+ record = @model.new(:state => 'first_gear', :seatbelt => true)
945
+ assert record.valid?
946
+ end
947
+ end
948
+
949
+ class MachineWithEventAttributesOnValidationTest < BaseTestCase
950
+ def setup
951
+ @model = new_model
952
+ @machine = StateMachine::Machine.new(@model)
953
+ @machine.event :ignite do
954
+ transition :parked => :idling
793
955
  end
794
956
 
795
- def test_should_be_successful
796
- assert @result
797
- end
957
+ @record = @model.new
958
+ @record.state = 'parked'
959
+ @record.state_event = 'ignite'
960
+ end
961
+
962
+ def test_should_fail_if_event_is_invalid
963
+ @record.state_event = 'invalid'
964
+ assert !@record.valid?
965
+ assert_equal ['state_event is invalid'], @record.errors.full_messages
966
+ end
967
+
968
+ def test_should_fail_if_event_has_no_transition
969
+ @record.state = 'idling'
970
+ assert !@record.valid?
971
+ assert_equal ['state_event cannot transition when idling'], @record.errors.full_messages
972
+ end
973
+
974
+ def test_should_be_successful_if_event_has_transition
975
+ assert @record.valid?
976
+ end
977
+
978
+ def test_should_run_before_callbacks
979
+ ran_callback = false
980
+ @machine.before_transition { ran_callback = true }
798
981
 
799
- def test_should_change_current_state
800
- assert_equal 'idling', @record.state
801
- end
982
+ @record.valid?
983
+ assert ran_callback
984
+ end
985
+
986
+ def test_should_run_around_callbacks_before_yield
987
+ ran_callback = false
988
+ @machine.around_transition {|block| ran_callback = true; block.call }
802
989
 
803
- def test_should_save_record
804
- assert !@record.new?
805
- end
990
+ @record.valid?
991
+ assert ran_callback
992
+ end
993
+
994
+ def test_should_persist_new_state
995
+ @record.valid?
996
+ assert_equal 'idling', @record.state
997
+ end
998
+
999
+ def test_should_not_run_after_callbacks
1000
+ ran_callback = false
1001
+ @machine.after_transition { ran_callback = true }
806
1002
 
807
- def test_should_not_run_further_after_callbacks
808
- assert_equal 1, @after_count
809
- end
1003
+ @record.valid?
1004
+ assert !ran_callback
810
1005
  end
811
1006
 
812
- class MachineWithValidationsTest < BaseTestCase
813
- def setup
814
- @model = new_model
815
- @machine = StateMachine::Machine.new(@model)
816
- @machine.state :parked
817
-
818
- @record = @model.new
1007
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
1008
+ @model.class_eval do
1009
+ attr_accessor :seatbelt
1010
+ validates_presence_of :seatbelt
819
1011
  end
820
1012
 
821
- def test_should_invalidate_using_errors
822
- @record.state = 'parked'
823
-
824
- @machine.invalidate(@record, :state, :invalid_transition, [[:event, :park]])
825
- assert_equal ['cannot transition via "park"'], @record.errors.on(:state)
826
- end
1013
+ ran_callback = false
1014
+ @machine.after_transition { ran_callback = true }
827
1015
 
828
- def test_should_auto_prefix_custom_attributes_on_invalidation
829
- @machine.invalidate(@record, :event, :invalid)
830
-
831
- assert_equal ['is invalid'], @record.errors.on(:state_event)
1016
+ @record.valid?
1017
+ assert !ran_callback
1018
+ end
1019
+
1020
+ def test_should_run_after_callbacks_with_failures_enabled_if_validation_fails
1021
+ @model.class_eval do
1022
+ attr_accessor :seatbelt
1023
+ validates_presence_of :seatbelt
832
1024
  end
833
1025
 
834
- def test_should_clear_errors_on_reset
835
- @record.state = 'parked'
836
- @record.errors.add(:state, 'is invalid')
837
-
838
- @machine.reset(@record)
839
- assert_nil @record.errors.on(:id)
840
- end
1026
+ ran_callback = false
1027
+ @machine.after_transition(:include_failures => true) { ran_callback = true }
841
1028
 
842
- def test_should_be_valid_if_state_is_known
843
- @record.state = 'parked'
844
-
845
- assert @record.valid?
846
- end
1029
+ @record.valid?
1030
+ assert ran_callback
1031
+ end
1032
+
1033
+ def test_should_not_run_around_callbacks_after_yield
1034
+ ran_callback = [false]
1035
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
847
1036
 
848
- def test_should_not_be_valid_if_state_is_unknown
849
- @record.state = 'invalid'
850
-
851
- assert !@record.valid?
852
- assert_equal ['state is invalid'], @record.errors.full_messages
853
- end
1037
+ @record.valid?
1038
+ assert !ran_callback[0]
854
1039
  end
855
1040
 
856
- class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
857
- def setup
858
- @model = new_model do
859
- alias_method :status, :state
860
- alias_method :status=, :state=
861
- end
862
-
863
- @machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
864
- @machine.state :parked
865
-
866
- @record = @model.new
1041
+ def test_should_not_run_around_callbacks_after_yield_with_failures_disabled_if_validation_fails
1042
+ @model.class_eval do
1043
+ attr_accessor :seatbelt
1044
+ validates_presence_of :seatbelt
867
1045
  end
868
1046
 
869
- def test_should_add_validation_errors_to_custom_attribute
870
- @record.state = 'invalid'
871
-
872
- assert !@record.valid?
873
- assert_equal ['state is invalid'], @record.errors.full_messages
874
-
875
- @record.state = 'parked'
876
- assert @record.valid?
877
- end
1047
+ ran_callback = [false]
1048
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1049
+
1050
+ @record.valid?
1051
+ assert !ran_callback[0]
878
1052
  end
879
1053
 
880
- class MachineWithStateDrivenValidationsTest < BaseTestCase
881
- def setup
882
- @model = new_model do
883
- attr_accessor :seatbelt
884
- end
885
-
886
- @machine = StateMachine::Machine.new(@model)
887
- @machine.state :first_gear do
888
- validates_presence_of :seatbelt
889
- end
890
- @machine.other_states :parked
1054
+ def test_should_run_around_callbacks_after_yield_with_failures_enabled_if_validation_fails
1055
+ @model.class_eval do
1056
+ attr_accessor :seatbelt
1057
+ validates_presence_of :seatbelt
891
1058
  end
892
1059
 
893
- def test_should_be_valid_if_validation_fails_outside_state_scope
894
- record = @model.new(:state => 'parked', :seatbelt => nil)
895
- assert record.valid?
1060
+ ran_callback = [false]
1061
+ @machine.around_transition(:include_failures => true) {|block| block.call; ran_callback[0] = true }
1062
+
1063
+ @record.valid?
1064
+ assert ran_callback[0]
1065
+ end
1066
+
1067
+ def test_should_not_run_before_transitions_within_transaction
1068
+ @machine.before_transition { self.class.create; raise Sequel::Error::Rollback }
1069
+
1070
+ begin
1071
+ @record.valid?
1072
+ rescue Sequel::Error::Rollback
896
1073
  end
897
1074
 
898
- def test_should_be_invalid_if_validation_fails_within_state_scope
899
- record = @model.new(:state => 'first_gear', :seatbelt => nil)
900
- assert !record.valid?
1075
+ assert_equal 1, @model.count
1076
+ end
1077
+ end
1078
+
1079
+ class MachineWithEventAttributesOnSaveTest < BaseTestCase
1080
+ def setup
1081
+ @model = new_model
1082
+ @machine = StateMachine::Machine.new(@model)
1083
+ @machine.event :ignite do
1084
+ transition :parked => :idling
901
1085
  end
902
1086
 
903
- def test_should_be_valid_if_validation_succeeds_within_state_scope
904
- record = @model.new(:state => 'first_gear', :seatbelt => true)
905
- assert record.valid?
1087
+ @record = @model.new
1088
+ @record.state = 'parked'
1089
+ @record.state_event = 'ignite'
1090
+ end
1091
+
1092
+ def test_should_fail_if_event_is_invalid
1093
+ @record.state_event = 'invalid'
1094
+ assert !@record.save
1095
+ end
1096
+
1097
+ def test_should_raise_exception_when_enabled_if_event_is_invalid
1098
+ @record.state_event = 'invalid'
1099
+ @model.raise_on_save_failure = true
1100
+ if defined?(Sequel::BeforeHookFailed)
1101
+ assert_raise(Sequel::BeforeHookFailed) { @record.save }
1102
+ else
1103
+ assert_raise(Sequel::Error) { @record.save }
906
1104
  end
907
1105
  end
908
1106
 
909
- class MachineWithEventAttributesOnValidationTest < BaseTestCase
910
- def setup
911
- @model = new_model
912
- @machine = StateMachine::Machine.new(@model)
913
- @machine.event :ignite do
914
- transition :parked => :idling
915
- end
916
-
917
- @record = @model.new
918
- @record.state = 'parked'
919
- @record.state_event = 'ignite'
1107
+ def test_should_fail_if_event_has_no_transition
1108
+ @record.state = 'idling'
1109
+ assert !@record.save
1110
+ end
1111
+
1112
+ def test_should_raise_exception_when_enabled_if_event_has_no_transition
1113
+ @record.state = 'idling'
1114
+ @model.raise_on_save_failure = true
1115
+ if defined?(Sequel::BeforeHookFailed)
1116
+ assert_raise(Sequel::BeforeHookFailed) { @record.save }
1117
+ else
1118
+ assert_raise(Sequel::Error) { @record.save }
920
1119
  end
1120
+ end
1121
+
1122
+ def test_should_be_successful_if_event_has_transition
1123
+ assert @record.save
1124
+ end
1125
+
1126
+ def test_should_run_before_callbacks
1127
+ ran_callback = false
1128
+ @machine.before_transition { ran_callback = true }
921
1129
 
922
- def test_should_fail_if_event_is_invalid
923
- @record.state_event = 'invalid'
924
- assert !@record.valid?
925
- assert_equal ['state_event is invalid'], @record.errors.full_messages
926
- end
1130
+ @record.save
1131
+ assert ran_callback
1132
+ end
1133
+
1134
+ def test_should_run_before_callbacks_once
1135
+ before_count = 0
1136
+ @machine.before_transition { before_count += 1 }
927
1137
 
928
- def test_should_fail_if_event_has_no_transition
929
- @record.state = 'idling'
930
- assert !@record.valid?
931
- assert_equal ['state_event cannot transition when idling'], @record.errors.full_messages
932
- end
1138
+ @record.save
1139
+ assert_equal 1, before_count
1140
+ end
1141
+
1142
+ def test_should_run_around_callbacks_before_yield
1143
+ ran_callback = false
1144
+ @machine.around_transition {|block| ran_callback = true; block.call }
933
1145
 
934
- def test_should_be_successful_if_event_has_transition
935
- assert @record.valid?
936
- end
1146
+ @record.save
1147
+ assert ran_callback
1148
+ end
1149
+
1150
+ def test_should_run_around_callbacks_before_yield_once
1151
+ around_before_count = 0
1152
+ @machine.around_transition {|block| around_before_count += 1; block.call }
937
1153
 
938
- def test_should_run_before_callbacks
939
- ran_callback = false
940
- @machine.before_transition { ran_callback = true }
941
-
942
- @record.valid?
943
- assert ran_callback
944
- end
1154
+ @record.save
1155
+ assert_equal 1, around_before_count
1156
+ end
1157
+
1158
+ def test_should_persist_new_state
1159
+ @record.save
1160
+ assert_equal 'idling', @record.state
1161
+ end
1162
+
1163
+ def test_should_run_after_callbacks
1164
+ ran_callback = false
1165
+ @machine.after_transition { ran_callback = true }
945
1166
 
946
- def test_should_persist_new_state
947
- @record.valid?
948
- assert_equal 'idling', @record.state
949
- end
1167
+ @record.save
1168
+ assert ran_callback
1169
+ end
1170
+
1171
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
1172
+ @model.before_create {|record| false}
950
1173
 
951
- def test_should_not_run_after_callbacks
952
- ran_callback = false
953
- @machine.after_transition { ran_callback = true }
954
-
955
- @record.valid?
956
- assert !ran_callback
957
- end
1174
+ ran_callback = false
1175
+ @machine.after_transition { ran_callback = true }
958
1176
 
959
- def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
960
- @model.class_eval do
961
- attr_accessor :seatbelt
962
- validates_presence_of :seatbelt
963
- end
1177
+ @record.save
1178
+ assert !ran_callback
1179
+ end
1180
+
1181
+ if defined?(Sequel::MAJOR) && Sequel::MAJOR >= 3 && Sequel::MINOR >= 7
1182
+ def test_should_not_run_after_callbacks_with_failures_enabled_if_fails
1183
+ @model.before_create {|record| false}
964
1184
 
965
1185
  ran_callback = false
966
- @machine.after_transition { ran_callback = true }
1186
+ @machine.after_transition(:include_failures => true) { ran_callback = true }
967
1187
 
968
- @record.valid?
1188
+ @record.save
969
1189
  assert !ran_callback
970
1190
  end
971
-
972
- def test_should_run_after_callbacks_with_failures_enabled_if_validation_fails
973
- @model.class_eval do
974
- attr_accessor :seatbelt
975
- validates_presence_of :seatbelt
976
- end
1191
+ else
1192
+ def test_should_run_after_callbacks_with_failures_enabled_if_fails
1193
+ @model.before_create {|record| false}
977
1194
 
978
1195
  ran_callback = false
979
1196
  @machine.after_transition(:include_failures => true) { ran_callback = true }
980
1197
 
981
- @record.valid?
1198
+ @record.save
982
1199
  assert ran_callback
983
1200
  end
984
1201
  end
985
1202
 
986
- class MachineWithEventAttributesOnSaveTest < BaseTestCase
987
- def setup
988
- @model = new_model
989
- @machine = StateMachine::Machine.new(@model)
990
- @machine.event :ignite do
991
- transition :parked => :idling
992
- end
993
-
994
- @record = @model.new
995
- @record.state = 'parked'
996
- @record.state_event = 'ignite'
997
- end
998
-
999
- def test_should_fail_if_event_is_invalid
1000
- @record.state_event = 'invalid'
1001
- assert !@record.save
1002
- end
1003
-
1004
- def test_should_fail_if_event_has_no_transition
1005
- @record.state = 'idling'
1006
- assert !@record.save
1007
- end
1203
+ def test_should_not_run_before_transitions_within_transaction
1204
+ @machine.before_transition { self.class.create; raise Sequel::Error::Rollback }
1008
1205
 
1009
- def test_should_be_successful_if_event_has_transition
1010
- assert @record.save
1011
- end
1012
-
1013
- def test_should_run_before_callbacks
1014
- ran_callback = false
1015
- @machine.before_transition { ran_callback = true }
1016
-
1206
+ begin
1017
1207
  @record.save
1018
- assert ran_callback
1208
+ rescue Sequel::Error::Rollback
1019
1209
  end
1020
1210
 
1021
- def test_should_run_before_callbacks_once
1022
- before_count = 0
1023
- @machine.before_transition { before_count += 1 }
1024
-
1025
- @record.save
1026
- assert_equal 1, before_count
1027
- end
1211
+ assert_equal 1, @model.count
1212
+ end
1213
+
1214
+ def test_should_not_run_around_callbacks_with_failures_disabled_if_fails
1215
+ @model.before_create {|record| false}
1028
1216
 
1029
- def test_should_persist_new_state
1030
- @record.save
1031
- assert_equal 'idling', @record.state
1032
- end
1217
+ ran_callback = [false]
1218
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1033
1219
 
1034
- def test_should_run_after_callbacks
1035
- ran_callback = false
1036
- @machine.after_transition { ran_callback = true }
1037
-
1038
- @record.save
1039
- assert ran_callback
1040
- end
1220
+ @record.save
1221
+ assert !ran_callback[0]
1222
+ end
1223
+
1224
+ def test_should_run_around_callbacks_after_yield
1225
+ ran_callback = [false]
1226
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1041
1227
 
1042
- def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
1228
+ @record.save
1229
+ assert ran_callback[0]
1230
+ end
1231
+
1232
+ if defined?(Sequel::MAJOR) && Sequel::MAJOR >= 3 && Sequel::MINOR >= 7
1233
+ def test_should_not_run_around_callbacks_after_yield_with_failures_enabled_if_fails
1043
1234
  @model.before_create {|record| false}
1044
1235
 
1045
- ran_callback = false
1046
- @machine.after_transition { ran_callback = true }
1236
+ ran_callback = [false]
1237
+ @machine.around_transition(:include_failures => true) {|block| block.call; ran_callback[0] = true }
1047
1238
 
1048
1239
  @record.save
1049
- assert !ran_callback
1240
+ assert !ran_callback[0]
1050
1241
  end
1051
-
1052
- def test_should_run_after_callbacks_with_failures_enabled_if_fails
1242
+ else
1243
+ def test_should_run_around_callbacks_after_yield_with_failures_enabled_if_fails
1053
1244
  @model.before_create {|record| false}
1054
1245
 
1055
- ran_callback = false
1056
- @machine.after_transition(:include_failures => true) { ran_callback = true }
1246
+ ran_callback = [false]
1247
+ @machine.around_transition(:include_failures => true) {|block| block.call; ran_callback[0] = true }
1057
1248
 
1058
1249
  @record.save
1059
- assert ran_callback
1250
+ assert ran_callback[0]
1060
1251
  end
1061
1252
  end
1062
1253
 
1063
- class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
1064
- def setup
1065
- @superclass = new_model do
1066
- def persist
1067
- save
1068
- end
1069
- end
1070
- @model = Class.new(@superclass)
1071
- @machine = StateMachine::Machine.new(@model, :action => :persist)
1072
- @machine.event :ignite do
1073
- transition :parked => :idling
1074
- end
1254
+ if defined?(Sequel::MAJOR) && (Sequel::MAJOR >= 3 || Sequel::MAJOR == 2 && Sequel::MINOR == 12)
1255
+ def test_should_run_after_transitions_within_transaction
1256
+ @machine.after_transition { self.class.create; raise Sequel::Error::Rollback }
1075
1257
 
1076
- @record = @model.new
1077
- @record.state = 'parked'
1078
- @record.state_event = 'ignite'
1079
- end
1080
-
1081
- def test_should_not_transition_on_valid?
1082
- @record.valid?
1083
- assert_equal 'parked', @record.state
1084
- end
1085
-
1086
- def test_should_not_transition_on_save
1087
1258
  @record.save
1088
- assert_equal 'parked', @record.state
1089
- end
1090
-
1091
- def test_should_transition_on_custom_action
1092
- @record.persist
1093
- assert_equal 'idling', @record.state
1094
- end
1095
- end
1096
-
1097
- class MachineWithScopesTest < BaseTestCase
1098
- def setup
1099
- @model = new_model
1100
- @machine = StateMachine::Machine.new(@model)
1101
- @machine.state :parked, :first_gear
1102
- @machine.state :idling, :value => lambda {'idling'}
1103
- end
1104
-
1105
- def test_should_create_singular_with_scope
1106
- assert @model.respond_to?(:with_state)
1259
+
1260
+ assert_equal 0, @model.count
1107
1261
  end
1108
1262
 
1109
- def test_should_only_include_records_with_state_in_singular_with_scope
1110
- parked = @model.create :state => 'parked'
1111
- idling = @model.create :state => 'idling'
1263
+ def test_should_run_around_transition_within_transaction
1264
+ @machine.around_transition {|block| block.call; self.class.create; raise Sequel::Error::Rollback }
1112
1265
 
1113
- assert_equal [parked], @model.with_state(:parked).all
1266
+ @record.save
1267
+
1268
+ assert_equal 0, @model.count
1114
1269
  end
1115
-
1116
- def test_should_create_plural_with_scope
1117
- assert @model.respond_to?(:with_states)
1270
+ else
1271
+ def test_should_not_run_after_transitions_within_transaction
1272
+ @machine.after_transition { self.class.create; raise Sequel::Error::Rollback }
1273
+
1274
+ begin
1275
+ @record.save
1276
+ rescue Sequel::Error::Rollback
1277
+ end
1278
+
1279
+ assert_equal 2, @model.count
1118
1280
  end
1119
1281
 
1120
- def test_should_only_include_records_with_states_in_plural_with_scope
1121
- parked = @model.create :state => 'parked'
1122
- idling = @model.create :state => 'idling'
1282
+ def test_should_not_run_around_transition_within_transaction
1283
+ @machine.around_transition {|block| block.call; self.class.create; raise Sequel::Error::Rollback }
1284
+
1285
+ begin
1286
+ @record.save
1287
+ rescue Sequel::Error::Rollback
1288
+ end
1123
1289
 
1124
- assert_equal [parked, idling], @model.with_states(:parked, :idling).all
1290
+ assert_equal 2, @model.count
1125
1291
  end
1126
-
1127
- def test_should_create_singular_without_scope
1128
- assert @model.respond_to?(:without_state)
1292
+ end
1293
+ end
1294
+
1295
+ class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
1296
+ def setup
1297
+ @superclass = new_model do
1298
+ def persist
1299
+ save
1300
+ end
1129
1301
  end
1130
-
1131
- def test_should_only_include_records_without_state_in_singular_without_scope
1132
- parked = @model.create :state => 'parked'
1133
- idling = @model.create :state => 'idling'
1134
-
1135
- assert_equal [parked], @model.without_state(:idling).all
1302
+ @model = Class.new(@superclass)
1303
+ @machine = StateMachine::Machine.new(@model, :action => :persist)
1304
+ @machine.event :ignite do
1305
+ transition :parked => :idling
1136
1306
  end
1137
1307
 
1138
- def test_should_create_plural_without_scope
1139
- assert @model.respond_to?(:without_states)
1140
- end
1308
+ @record = @model.new
1309
+ @record.state = 'parked'
1310
+ @record.state_event = 'ignite'
1311
+ end
1312
+
1313
+ def test_should_not_transition_on_valid?
1314
+ @record.valid?
1315
+ assert_equal 'parked', @record.state
1316
+ end
1317
+
1318
+ def test_should_not_transition_on_save
1319
+ @record.save
1320
+ assert_equal 'parked', @record.state
1321
+ end
1322
+
1323
+ def test_should_transition_on_custom_action
1324
+ @record.persist
1325
+ assert_equal 'idling', @record.state
1326
+ end
1327
+ end
1328
+
1329
+ class MachineWithScopesTest < BaseTestCase
1330
+ def setup
1331
+ @model = new_model
1332
+ @machine = StateMachine::Machine.new(@model)
1333
+ @machine.state :parked, :first_gear
1334
+ @machine.state :idling, :value => lambda {'idling'}
1335
+ end
1336
+
1337
+ def test_should_create_singular_with_scope
1338
+ assert @model.respond_to?(:with_state)
1339
+ end
1340
+
1341
+ def test_should_only_include_records_with_state_in_singular_with_scope
1342
+ parked = @model.create :state => 'parked'
1343
+ idling = @model.create :state => 'idling'
1141
1344
 
1142
- def test_should_only_include_records_without_states_in_plural_without_scope
1143
- parked = @model.create :state => 'parked'
1144
- idling = @model.create :state => 'idling'
1145
- first_gear = @model.create :state => 'first_gear'
1146
-
1147
- assert_equal [parked, idling], @model.without_states(:first_gear).all
1148
- end
1345
+ assert_equal [parked], @model.with_state(:parked).all
1346
+ end
1347
+
1348
+ def test_should_create_plural_with_scope
1349
+ assert @model.respond_to?(:with_states)
1350
+ end
1351
+
1352
+ def test_should_only_include_records_with_states_in_plural_with_scope
1353
+ parked = @model.create :state => 'parked'
1354
+ idling = @model.create :state => 'idling'
1149
1355
 
1150
- def test_should_allow_chaining_scopes_and_filters
1151
- parked = @model.create :state => 'parked'
1152
- idling = @model.create :state => 'idling'
1153
-
1154
- assert_equal [idling], @model.without_state(:parked).filter(:state => 'idling').all
1155
- end
1356
+ assert_equal [parked, idling], @model.with_states(:parked, :idling).all
1156
1357
  end
1157
1358
 
1158
- class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
1159
- def setup
1160
- @model = new_model
1161
- @machine = StateMachine::Machine.new(@model, :state)
1162
-
1163
- @subclass = Class.new(@model)
1164
- @subclass_machine = @subclass.state_machine(:state) {}
1165
- @subclass_machine.state :parked, :idling, :first_gear
1166
- end
1359
+ def test_should_create_singular_without_scope
1360
+ assert @model.respond_to?(:without_state)
1361
+ end
1362
+
1363
+ def test_should_only_include_records_without_state_in_singular_without_scope
1364
+ parked = @model.create :state => 'parked'
1365
+ idling = @model.create :state => 'idling'
1167
1366
 
1168
- def test_should_only_include_records_with_subclass_states_in_with_scope
1169
- parked = @subclass.create :state => 'parked'
1170
- idling = @subclass.create :state => 'idling'
1171
-
1172
- assert_equal [parked, idling], @subclass.with_states(:parked, :idling).all
1173
- end
1367
+ assert_equal [parked], @model.without_state(:idling).all
1368
+ end
1369
+
1370
+ def test_should_create_plural_without_scope
1371
+ assert @model.respond_to?(:without_states)
1372
+ end
1373
+
1374
+ def test_should_only_include_records_without_states_in_plural_without_scope
1375
+ parked = @model.create :state => 'parked'
1376
+ idling = @model.create :state => 'idling'
1377
+ first_gear = @model.create :state => 'first_gear'
1174
1378
 
1175
- def test_should_only_include_records_without_subclass_states_in_without_scope
1176
- parked = @subclass.create :state => 'parked'
1177
- idling = @subclass.create :state => 'idling'
1178
- first_gear = @subclass.create :state => 'first_gear'
1179
-
1180
- assert_equal [parked, idling], @subclass.without_states(:first_gear).all
1181
- end
1379
+ assert_equal [parked, idling], @model.without_states(:first_gear).all
1182
1380
  end
1183
1381
 
1184
- class MachineWithComplexPluralizationScopesTest < BaseTestCase
1185
- def setup
1186
- @model = new_model
1187
- @machine = StateMachine::Machine.new(@model, :status)
1188
- end
1382
+ def test_should_allow_chaining_scopes_and_filters
1383
+ parked = @model.create :state => 'parked'
1384
+ idling = @model.create :state => 'idling'
1189
1385
 
1190
- def test_should_create_singular_with_scope
1191
- assert @model.respond_to?(:with_status)
1192
- end
1386
+ assert_equal [idling], @model.without_state(:parked).filter(:state => 'idling').all
1387
+ end
1388
+ end
1389
+
1390
+ class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
1391
+ def setup
1392
+ @model = new_model
1393
+ @machine = StateMachine::Machine.new(@model, :state)
1394
+
1395
+ @subclass = Class.new(@model)
1396
+ @subclass_machine = @subclass.state_machine(:state) {}
1397
+ @subclass_machine.state :parked, :idling, :first_gear
1398
+ end
1399
+
1400
+ def test_should_only_include_records_with_subclass_states_in_with_scope
1401
+ parked = @subclass.create :state => 'parked'
1402
+ idling = @subclass.create :state => 'idling'
1193
1403
 
1194
- def test_should_create_plural_with_scope
1195
- assert @model.respond_to?(:with_statuses)
1196
- end
1404
+ assert_equal [parked, idling], @subclass.with_states(:parked, :idling).all
1197
1405
  end
1198
1406
 
1199
- class MachineWithScopesAndJoinsTest < BaseTestCase
1200
- def setup
1201
- @company = new_model(:company)
1202
- SequelTest.const_set('Company', @company)
1203
-
1204
- @vehicle = new_model(:vehicle) do
1205
- many_to_one :company, :class => SequelTest::Company
1206
- end
1207
- DB.alter_table :vehicle do
1208
- add_column :company_id, :integer
1209
- end
1210
- @vehicle.class_eval { get_db_schema(true) }
1211
- SequelTest.const_set('Vehicle', @vehicle)
1212
-
1213
- @company_machine = StateMachine::Machine.new(@company, :initial => :active)
1214
- @vehicle_machine = StateMachine::Machine.new(@vehicle, :initial => :parked)
1215
- @vehicle_machine.state :idling
1216
-
1217
- @ford = @company.create
1218
- @mustang = @vehicle.create(:company => @ford)
1219
- end
1407
+ def test_should_only_include_records_without_subclass_states_in_without_scope
1408
+ parked = @subclass.create :state => 'parked'
1409
+ idling = @subclass.create :state => 'idling'
1410
+ first_gear = @subclass.create :state => 'first_gear'
1220
1411
 
1221
- def test_should_find_records_in_with_scope
1222
- assert_equal [@mustang], @vehicle.with_states(:parked).join(:company, :id => :company_id).filter(:company__state => 'active').select(:vehicle.*).all
1223
- end
1412
+ assert_equal [parked, idling], @subclass.without_states(:first_gear).all
1413
+ end
1414
+ end
1415
+
1416
+ class MachineWithComplexPluralizationScopesTest < BaseTestCase
1417
+ def setup
1418
+ @model = new_model
1419
+ @machine = StateMachine::Machine.new(@model, :status)
1420
+ end
1421
+
1422
+ def test_should_create_singular_with_scope
1423
+ assert @model.respond_to?(:with_status)
1424
+ end
1425
+
1426
+ def test_should_create_plural_with_scope
1427
+ assert @model.respond_to?(:with_statuses)
1428
+ end
1429
+ end
1430
+
1431
+ class MachineWithScopesAndJoinsTest < BaseTestCase
1432
+ def setup
1433
+ @company = new_model(:company)
1434
+ SequelTest.const_set('Company', @company)
1224
1435
 
1225
- def test_should_find_records_in_without_scope
1226
- assert_equal [@mustang], @vehicle.without_states(:idling).join(:company, :id => :company_id).filter(:company__state => 'active').select(:vehicle.*).all
1436
+ @vehicle = new_model(:vehicle) do
1437
+ many_to_one :company, :class => SequelTest::Company
1438
+ end
1439
+ DB.alter_table :vehicle do
1440
+ add_column :company_id, :integer
1227
1441
  end
1442
+ @vehicle.class_eval { get_db_schema(true) }
1443
+ SequelTest.const_set('Vehicle', @vehicle)
1228
1444
 
1229
- def teardown
1230
- SequelTest.class_eval do
1231
- remove_const('Vehicle')
1232
- remove_const('Company')
1233
- end
1445
+ @company_machine = StateMachine::Machine.new(@company, :initial => :active)
1446
+ @vehicle_machine = StateMachine::Machine.new(@vehicle, :initial => :parked)
1447
+ @vehicle_machine.state :idling
1448
+
1449
+ @ford = @company.create
1450
+ @mustang = @vehicle.create(:company => @ford)
1451
+ end
1452
+
1453
+ def test_should_find_records_in_with_scope
1454
+ assert_equal [@mustang], @vehicle.with_states(:parked).join(:company, :id => :company_id).filter(:company__state => 'active').select(:vehicle.*).all
1455
+ end
1456
+
1457
+ def test_should_find_records_in_without_scope
1458
+ assert_equal [@mustang], @vehicle.without_states(:idling).join(:company, :id => :company_id).filter(:company__state => 'active').select(:vehicle.*).all
1459
+ end
1460
+
1461
+ def teardown
1462
+ SequelTest.class_eval do
1463
+ remove_const('Vehicle')
1464
+ remove_const('Company')
1234
1465
  end
1235
1466
  end
1236
1467
  end
1237
- rescue LoadError
1238
- $stderr.puts "Skipping Sequel tests. `gem install sequel#{" -v #{ENV['SEQUEL_VERSION']}" if ENV['SEQUEL_VERSION']}` and try again."
1239
1468
  end