state_machine 0.8.0 → 0.8.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.
@@ -6,6 +6,7 @@ begin
6
6
 
7
7
  gem 'dm-core', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
8
8
  require 'dm-core'
9
+ require 'dm-core/version' unless defined?(::DataMapper::VERSION)
9
10
 
10
11
  # Establish database connection
11
12
  DataMapper.setup(:default, 'sqlite3::memory:')
@@ -18,17 +19,19 @@ begin
18
19
 
19
20
  protected
20
21
  # Creates a new DataMapper resource (and the associated table)
21
- def new_resource(auto_migrate = true, &block)
22
+ def new_resource(create_table = :foo, &block)
23
+ table_name = create_table || :foo
24
+
22
25
  resource = Class.new do
23
26
  include DataMapper::Resource
24
27
 
25
- storage_names[:default] = 'foo'
26
- def self.name; 'DataMapperTest::Foo'; end
28
+ storage_names[:default] = table_name.to_s
29
+ def self.name; "DataMapperTest::#{storage_names[:default].capitalize}"; end
27
30
 
28
31
  property :id, DataMapper::Types::Serial
29
32
  property :state, String
30
33
 
31
- auto_migrate! if auto_migrate
34
+ auto_migrate! if create_table
32
35
  end
33
36
  resource.class_eval(&block) if block_given?
34
37
  resource
@@ -53,6 +56,35 @@ begin
53
56
  def test_should_not_match_if_class_does_not_inherit_from_active_record
54
57
  assert !StateMachine::Integrations::DataMapper.matches?(Class.new)
55
58
  end
59
+
60
+ def test_should_have_defaults
61
+ assert_equal e = {:action => :save, :use_transactions => false}, StateMachine::Integrations::DataMapper.defaults
62
+ end
63
+ end
64
+
65
+ class MachineWithoutDatabaseTest < BaseTestCase
66
+ def setup
67
+ @resource = new_resource(false) do
68
+ # Simulate the database not being available entirely
69
+ def self.repository
70
+ raise DataObjects::SyntaxError
71
+ end
72
+ end
73
+ end
74
+
75
+ def test_should_allow_machine_creation
76
+ assert_nothing_raised { StateMachine::Machine.new(@resource) }
77
+ end
78
+ end
79
+
80
+ class MachineUnmigratedTest < BaseTestCase
81
+ def setup
82
+ @resource = new_resource(false)
83
+ end
84
+
85
+ def test_should_allow_machine_creation
86
+ assert_nothing_raised { StateMachine::Machine.new(@resource) }
87
+ end
56
88
  end
57
89
 
58
90
  class MachineByDefaultTest < BaseTestCase
@@ -68,119 +100,198 @@ begin
68
100
  def test_should_not_use_transactions
69
101
  assert_equal false, @machine.use_transactions
70
102
  end
103
+
104
+ def test_should_not_have_any_before_callbacks
105
+ assert_equal 0, @machine.callbacks[:before].size
106
+ end
107
+
108
+ def test_should_not_have_any_after_callbacks
109
+ assert_equal 0, @machine.callbacks[:after].size
110
+ end
71
111
  end
72
112
 
73
- class MachineTest < BaseTestCase
113
+ class MachineWithStaticInitialStateTest < BaseTestCase
74
114
  def setup
75
- @resource = new_resource
76
- @machine = StateMachine::Machine.new(@resource)
77
- @machine.state :parked, :first_gear
78
- @machine.state :idling, :value => lambda {'idling'}
115
+ @resource = new_resource do
116
+ attr_accessor :value
117
+ end
118
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
79
119
  end
80
120
 
81
- def test_should_create_singular_with_scope
82
- assert @resource.respond_to?(:with_state)
121
+ def test_should_set_initial_state_on_created_object
122
+ record = @resource.new
123
+ assert_equal 'parked', record.state
83
124
  end
84
125
 
85
- def test_should_only_include_records_with_state_in_singular_with_scope
86
- parked = @resource.create :state => 'parked'
87
- idling = @resource.create :state => 'idling'
126
+ def test_should_set_initial_state_with_nil_attributes
127
+ @resource.class_eval do
128
+ def attributes=(attributes)
129
+ super(attributes || {})
130
+ end
131
+ end
88
132
 
89
- assert_equal [parked], @resource.with_state(:parked)
133
+ record = @resource.new(nil)
134
+ assert_equal 'parked', record.state
90
135
  end
91
136
 
92
- def test_should_create_plural_with_scope
93
- assert @resource.respond_to?(:with_states)
137
+ def test_should_still_set_attributes
138
+ record = @resource.new(:value => 1)
139
+ assert_equal 1, record.value
94
140
  end
95
141
 
96
- def test_should_only_include_records_with_states_in_plural_with_scope
97
- parked = @resource.create :state => 'parked'
98
- idling = @resource.create :state => 'idling'
142
+ def test_should_not_allow_initialize_blocks
143
+ block_args = nil
144
+ record = @resource.new do |*args|
145
+ block_args = args
146
+ end
99
147
 
100
- assert_equal [parked, idling], @resource.with_states(:parked, :idling)
148
+ assert_nil block_args
101
149
  end
102
150
 
103
- def test_should_create_singular_without_scope
104
- assert @resource.respond_to?(:without_state)
151
+ def test_should_set_initial_state_before_setting_attributes
152
+ @resource.class_eval do
153
+ attr_accessor :state_during_setter
154
+
155
+ define_method(:value=) do |value|
156
+ self.state_during_setter = state
157
+ end
158
+ end
159
+
160
+ record = @resource.new(:value => 1)
161
+ assert_equal 'parked', record.state_during_setter
105
162
  end
106
163
 
107
- def test_should_only_include_records_without_state_in_singular_without_scope
108
- parked = @resource.create :state => 'parked'
109
- idling = @resource.create :state => 'idling'
164
+ def test_should_not_set_initial_state_after_already_initialized
165
+ record = @resource.new(:value => 1)
166
+ assert_equal 'parked', record.state
110
167
 
111
- assert_equal [parked], @resource.without_state(:idling)
168
+ record.state = 'idling'
169
+ record.attributes = {}
170
+ assert_equal 'idling', record.state
112
171
  end
113
-
114
- def test_should_create_plural_without_scope
115
- assert @resource.respond_to?(:without_states)
172
+ end
173
+
174
+ class MachineWithDynamicInitialStateTest < BaseTestCase
175
+ def setup
176
+ @resource = new_resource do
177
+ attr_accessor :value
178
+ end
179
+ @machine = StateMachine::Machine.new(@resource, :initial => lambda {|object| :parked})
180
+ @machine.state :parked
116
181
  end
117
182
 
118
- def test_should_only_include_records_without_states_in_plural_without_scope
119
- parked = @resource.create :state => 'parked'
120
- idling = @resource.create :state => 'idling'
121
- first_gear = @resource.create :state => 'first_gear'
122
-
123
- assert_equal [parked, idling], @resource.without_states(:first_gear)
183
+ def test_should_set_initial_state_on_created_object
184
+ record = @resource.new
185
+ assert_equal 'parked', record.state
124
186
  end
125
187
 
126
- def test_should_allow_chaining_scopes
127
- parked = @resource.create :state => 'parked'
128
- idling = @resource.create :state => 'idling'
129
-
130
- assert_equal [idling], @resource.without_state(:parked).with_state(:idling)
188
+ def test_should_still_set_attributes
189
+ record = @resource.new(:value => 1)
190
+ assert_equal 1, record.value
131
191
  end
132
192
 
133
- def test_should_not_rollback_transaction_if_false
134
- @machine.within_transaction(@resource.new) do
135
- @resource.create
136
- false
193
+ def test_should_not_allow_initialize_blocks
194
+ block_args = nil
195
+ record = @resource.new do |*args|
196
+ block_args = args
137
197
  end
138
198
 
139
- assert_equal 1, @resource.all.size
199
+ assert_nil block_args
140
200
  end
141
201
 
142
- def test_should_not_rollback_transaction_if_true
143
- @machine.within_transaction(@resource.new) do
144
- @resource.create
145
- true
202
+ def test_should_set_initial_state_after_setting_attributes
203
+ @resource.class_eval do
204
+ attr_accessor :state_during_setter
205
+
206
+ define_method(:value=) do |value|
207
+ self.state_during_setter = state || 'nil'
208
+ end
146
209
  end
147
210
 
148
- assert_equal 1, @resource.all.size
211
+ record = @resource.new(:value => 1)
212
+ assert_equal 'nil', record.state_during_setter
149
213
  end
150
214
 
151
- def test_should_not_override_the_column_reader
152
- record = @resource.new
153
- record.attribute_set(:state, 'parked')
215
+ def test_should_not_set_initial_state_after_already_initialized
216
+ record = @resource.new(:value => 1)
154
217
  assert_equal 'parked', record.state
218
+
219
+ record.state = 'idling'
220
+ record.attributes = {}
221
+ assert_equal 'idling', record.state
222
+ end
223
+ end
224
+
225
+ class MachineWithColumnDefaultTest < BaseTestCase
226
+ def setup
227
+ @resource = new_resource do
228
+ property :status, String, :default => 'idling'
229
+ auto_migrate!
230
+ end
231
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
232
+ @record = @resource.new
155
233
  end
156
234
 
157
- def test_should_not_override_the_column_writer
158
- record = @resource.new
159
- record.state = 'parked'
160
- assert_equal 'parked', record.attribute_get(:state)
235
+ def test_should_use_machine_default
236
+ assert_equal 'parked', @record.status
161
237
  end
162
238
  end
163
239
 
164
- class MachineUnmigratedTest < BaseTestCase
240
+ class MachineWithConflictingPredicateTest < BaseTestCase
165
241
  def setup
166
- @resource = new_resource(false)
242
+ @resource = new_resource do
243
+ def state?(*args)
244
+ true
245
+ end
246
+ end
247
+
248
+ @machine = StateMachine::Machine.new(@resource)
249
+ @record = @resource.new
167
250
  end
168
251
 
169
- def test_should_allow_machine_creation
170
- assert_nothing_raised { StateMachine::Machine.new(@resource) }
252
+ def test_should_not_define_attribute_predicate
253
+ assert @record.state?
171
254
  end
172
255
  end
173
256
 
174
- class MachineWithInitialStateTest < BaseTestCase
257
+ class MachineWithColumnStateAttributeTest < BaseTestCase
175
258
  def setup
176
259
  @resource = new_resource
177
- @machine = StateMachine::Machine.new(@resource, :initial => 'parked')
260
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
261
+ @machine.other_states(:idling)
262
+
178
263
  @record = @resource.new
179
264
  end
180
265
 
181
- def test_should_set_initial_state_on_created_object
266
+ def test_should_not_override_the_column_reader
267
+ @record.attribute_set(:state, 'parked')
182
268
  assert_equal 'parked', @record.state
183
269
  end
270
+
271
+ def test_should_not_override_the_column_writer
272
+ @record.state = 'parked'
273
+ assert_equal 'parked', @record.attribute_get(:state)
274
+ end
275
+
276
+ def test_should_have_an_attribute_predicate
277
+ assert @record.respond_to?(:state?)
278
+ end
279
+
280
+ def test_should_raise_exception_for_predicate_without_parameters
281
+ assert_raise(IndexError) { @record.state? }
282
+ end
283
+
284
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
285
+ assert !@record.state?(:idling)
286
+ end
287
+
288
+ def test_should_return_true_for_predicate_if_matches_current_value
289
+ assert @record.state?(:parked)
290
+ end
291
+
292
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
293
+ assert_raise(IndexError) { @record.state?(:invalid) }
294
+ end
184
295
  end
185
296
 
186
297
  class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
@@ -188,6 +299,8 @@ begin
188
299
  @resource = new_resource do
189
300
  def initialize
190
301
  # Skip attribute initialization
302
+ @initialized_state_machines = true
303
+ super
191
304
  end
192
305
  end
193
306
 
@@ -197,23 +310,18 @@ begin
197
310
 
198
311
  def test_should_define_a_new_property_for_the_attribute
199
312
  assert_not_nil @resource.properties[:status]
200
- assert @record.respond_to?(:status)
201
- assert @record.respond_to?(:status=)
202
313
  end
203
- end
204
-
205
- class MachineWithComplexPluralizationTest < BaseTestCase
206
- def setup
207
- @resource = new_resource
208
- @machine = StateMachine::Machine.new(@resource, :status)
314
+
315
+ def test_should_define_a_reader_attribute_for_the_attribute
316
+ assert @record.respond_to?(:status)
209
317
  end
210
318
 
211
- def test_should_create_singular_with_scope
212
- assert @resource.respond_to?(:with_status)
319
+ def test_should_define_a_writer_attribute_for_the_attribute
320
+ assert @record.respond_to?(:status=)
213
321
  end
214
322
 
215
- def test_should_create_plural_with_scope
216
- assert @resource.respond_to?(:with_statuses)
323
+ def test_should_define_an_attribute_predicate
324
+ assert @record.respond_to?(:status?)
217
325
  end
218
326
  end
219
327
 
@@ -223,104 +331,315 @@ begin
223
331
  attr_accessor :status
224
332
  end
225
333
 
226
- @machine = StateMachine::Machine.new(@resource, :status, :initial => 'parked')
334
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
335
+ @machine.other_states(:idling)
227
336
  @record = @resource.new
228
337
  end
229
338
 
339
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
340
+ assert !@record.status?(:idling)
341
+ end
342
+
343
+ def test_should_return_true_for_predicate_if_matches_current_value
344
+ assert @record.status?(:parked)
345
+ end
346
+
347
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
348
+ assert_raise(IndexError) { @record.status?(:invalid) }
349
+ end
350
+
230
351
  def test_should_set_initial_state_on_created_object
231
352
  assert_equal 'parked', @record.status
232
353
  end
233
354
  end
234
355
 
235
- class MachineWithOwnerSubclassTest < BaseTestCase
356
+ class MachineWithInitializedStateTest < BaseTestCase
236
357
  def setup
237
358
  @resource = new_resource
238
- @machine = StateMachine::Machine.new(@resource, :state)
239
-
240
- @subclass = Class.new(@resource)
241
- @subclass_machine = @subclass.state_machine(:state) {}
242
- @subclass_machine.state :parked, :idling, :first_gear
359
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
360
+ @machine.state nil, :idling
243
361
  end
244
362
 
245
- def test_should_only_include_records_with_subclass_states_in_with_scope
246
- parked = @subclass.create :state => 'parked'
247
- idling = @subclass.create :state => 'idling'
248
-
249
- assert_equal [parked, idling], @subclass.with_states(:parked, :idling)
363
+ def test_should_allow_nil_initial_state_when_static
364
+ record = @resource.new(:state => nil)
365
+ assert_nil record.state
250
366
  end
251
367
 
252
- def test_should_only_include_records_without_subclass_states_in_without_scope
253
- parked = @subclass.create :state => 'parked'
254
- idling = @subclass.create :state => 'idling'
255
- first_gear = @subclass.create :state => 'first_gear'
256
-
257
- assert_equal [parked, idling], @subclass.without_states(:first_gear)
368
+ def test_should_allow_nil_initial_state_when_dynamic
369
+ @machine.initial_state = lambda {:parked}
370
+ record = @resource.new(:state => nil)
371
+ assert_nil record.state
372
+ end
373
+
374
+ def test_should_allow_different_initial_state_when_static
375
+ record = @resource.new(:state => 'idling')
376
+ assert_equal 'idling', record.state
377
+ end
378
+
379
+ def test_should_allow_different_initial_state_when_dynamic
380
+ @machine.initial_state = lambda {:parked}
381
+ record = @resource.new(:state => 'idling')
382
+ assert_equal 'idling', record.state
383
+ end
384
+
385
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.9.8')
386
+ def test_should_raise_exception_if_protected
387
+ @resource.class_eval do
388
+ protected :state=
389
+ end
390
+
391
+ assert_raise(ArgumentError) { @resource.new(:state => 'idling') }
392
+ end
258
393
  end
259
394
  end
260
395
 
261
- class MachineWithTransactionsTest < BaseTestCase
396
+ class MachineWithLoopbackTest < BaseTestCase
262
397
  def setup
263
- @resource = new_resource
264
- @machine = StateMachine::Machine.new(@resource, :use_transactions => true)
265
- end
266
-
267
- def test_should_rollback_transaction_if_false
268
- @machine.within_transaction(@resource.new) do
269
- @resource.create
270
- false
398
+ @resource = new_resource do
399
+ property :updated_at, DateTime
400
+ auto_migrate!
401
+
402
+ # Simulate dm-timestamps
403
+ before :update do
404
+ return unless dirty?
405
+ self.updated_at = DateTime.now
406
+ end
271
407
  end
272
408
 
273
- assert_equal 0, @resource.all.size
409
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
410
+ @machine.event :park
411
+
412
+ @record = @resource.create(:updated_at => Time.now - 1)
413
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
414
+
415
+ @timestamp = @record.updated_at
416
+ @transition.perform
274
417
  end
275
418
 
276
- def test_should_not_rollback_transaction_if_true
277
- @machine.within_transaction(@resource.new) do
278
- @resource.create
279
- true
280
- end
281
-
282
- assert_equal 1, @resource.all.size
419
+ def test_should_update_record
420
+ assert_not_equal @timestamp, @record.updated_at
283
421
  end
284
422
  end
285
423
 
286
- class MachineWithCallbacksTest < BaseTestCase
424
+ class MachineWithDirtyAttributesTest < BaseTestCase
287
425
  def setup
288
426
  @resource = new_resource
289
- @machine = StateMachine::Machine.new(@resource)
290
- @machine.state :parked, :idling
427
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
291
428
  @machine.event :ignite
292
- @record = @resource.new(:state => 'parked')
429
+ @machine.state :idling
430
+
431
+ @record = @resource.create
432
+
293
433
  @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
434
+ @transition.perform(false)
294
435
  end
295
436
 
296
- def test_should_run_before_callbacks
297
- called = false
298
- @machine.before_transition(lambda {called = true})
299
-
300
- @transition.perform
301
- assert called
437
+ def test_should_include_state_in_changed_attributes
438
+ assert_equal e = {@resource.properties[:state] => 'idling'}, @record.dirty_attributes
302
439
  end
303
440
 
304
- def test_should_pass_transition_to_before_callbacks_with_one_argument
305
- transition = nil
306
- @machine.before_transition(lambda {|arg| transition = arg})
307
-
308
- @transition.perform
309
- assert_equal @transition, transition
441
+ def test_should_track_attribute_change
442
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
443
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.original_attributes
444
+ else
445
+ assert_equal e = {:state => 'parked'}, @record.original_values
446
+ end
310
447
  end
311
448
 
312
- def test_should_pass_transition_to_before_callbacks_with_multiple_arguments
313
- callback_args = nil
314
- @machine.before_transition(lambda {|*args| callback_args = args})
315
-
316
- @transition.perform
317
- assert_equal [@transition], callback_args
449
+ def test_should_not_reset_changes_on_multiple_transitions
450
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
451
+ transition.perform(false)
452
+
453
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
454
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.original_attributes
455
+ else
456
+ assert_equal e = {:state => 'parked'}, @record.original_values
457
+ end
318
458
  end
319
-
320
- def test_should_run_before_callbacks_within_the_context_of_the_record
321
- context = nil
322
- @machine.before_transition(lambda {context = self})
323
-
459
+ end
460
+
461
+ class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
462
+ def setup
463
+ @resource = new_resource
464
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
465
+ @machine.event :park
466
+
467
+ @record = @resource.create
468
+
469
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
470
+ @transition.perform(false)
471
+ end
472
+
473
+ def test_should_include_state_in_changed_attributes
474
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.dirty_attributes
475
+ end
476
+
477
+ def test_should_track_attribute_change
478
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
479
+ assert_equal e = {@resource.properties[:state] => 'parked-ignored'}, @record.original_attributes
480
+ else
481
+ assert_equal e = {:state => 'parked-ignored'}, @record.original_values
482
+ end
483
+ end
484
+ end
485
+
486
+ class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
487
+ def setup
488
+ @resource = new_resource do
489
+ property :status, String, :default => 'idling'
490
+ auto_migrate!
491
+ end
492
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
493
+ @machine.event :ignite
494
+ @machine.state :idling
495
+
496
+ @record = @resource.create
497
+
498
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
499
+ @transition.perform(false)
500
+ end
501
+
502
+ def test_should_include_state_in_changed_attributes
503
+ assert_equal e = {@resource.properties[:status] => 'idling'}, @record.dirty_attributes
504
+ end
505
+
506
+ def test_should_track_attribute_change
507
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
508
+ assert_equal e = {@resource.properties[:status] => 'parked'}, @record.original_attributes
509
+ else
510
+ assert_equal e = {:status => 'parked'}, @record.original_values
511
+ end
512
+ end
513
+
514
+ def test_should_not_reset_changes_on_multiple_transitions
515
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
516
+ transition.perform(false)
517
+
518
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
519
+ assert_equal e = {@resource.properties[:status] => 'parked'}, @record.original_attributes
520
+ else
521
+ assert_equal e = {:status => 'parked'}, @record.original_values
522
+ end
523
+ end
524
+ end
525
+
526
+ class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
527
+ def setup
528
+ @resource = new_resource do
529
+ property :status, String, :default => 'idling'
530
+ auto_migrate!
531
+ end
532
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
533
+ @machine.event :park
534
+
535
+ @record = @resource.create
536
+
537
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
538
+ @transition.perform(false)
539
+ end
540
+
541
+ def test_should_include_state_in_changed_attributes
542
+ assert_equal e = {@resource.properties[:status] => 'parked'}, @record.dirty_attributes
543
+ end
544
+
545
+ def test_should_track_attribute_changes
546
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
547
+ assert_equal e = {@resource.properties[:status] => 'parked-ignored'}, @record.original_attributes
548
+ else
549
+ assert_equal e = {:status => 'parked-ignored'}, @record.original_values
550
+ end
551
+ end
552
+ end
553
+
554
+ class MachineWithoutTransactionsTest < BaseTestCase
555
+ def setup
556
+ @resource = new_resource
557
+ @machine = StateMachine::Machine.new(@resource, :use_transactions => false)
558
+ end
559
+
560
+ def test_should_not_rollback_transaction_if_false
561
+ @machine.within_transaction(@resource.new) do
562
+ @resource.create
563
+ false
564
+ end
565
+
566
+ assert_equal 1, @resource.all.size
567
+ end
568
+
569
+ def test_should_not_rollback_transaction_if_true
570
+ @machine.within_transaction(@resource.new) do
571
+ @resource.create
572
+ true
573
+ end
574
+
575
+ assert_equal 1, @resource.all.size
576
+ end
577
+ end
578
+
579
+ class MachineWithTransactionsTest < BaseTestCase
580
+ def setup
581
+ @resource = new_resource
582
+ @machine = StateMachine::Machine.new(@resource, :use_transactions => true)
583
+ end
584
+
585
+ def test_should_rollback_transaction_if_false
586
+ @machine.within_transaction(@resource.new) do
587
+ @resource.create
588
+ false
589
+ end
590
+
591
+ assert_equal 0, @resource.all.size
592
+ end
593
+
594
+ def test_should_not_rollback_transaction_if_true
595
+ @machine.within_transaction(@resource.new) do
596
+ @resource.create
597
+ true
598
+ end
599
+
600
+ assert_equal 1, @resource.all.size
601
+ end
602
+ end
603
+
604
+ class MachineWithCallbacksTest < BaseTestCase
605
+ def setup
606
+ @resource = new_resource
607
+ @machine = StateMachine::Machine.new(@resource)
608
+ @machine.state :parked, :idling
609
+ @machine.event :ignite
610
+
611
+ @record = @resource.new(:state => 'parked')
612
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
613
+ end
614
+
615
+ def test_should_run_before_callbacks
616
+ called = false
617
+ @machine.before_transition(lambda {called = true})
618
+
619
+ @transition.perform
620
+ assert called
621
+ end
622
+
623
+ def test_should_pass_transition_to_before_callbacks_with_one_argument
624
+ transition = nil
625
+ @machine.before_transition(lambda {|arg| transition = arg})
626
+
627
+ @transition.perform
628
+ assert_equal @transition, transition
629
+ end
630
+
631
+ def test_should_pass_transition_to_before_callbacks_with_multiple_arguments
632
+ callback_args = nil
633
+ @machine.before_transition(lambda {|*args| callback_args = args})
634
+
635
+ @transition.perform
636
+ assert_equal [@transition], callback_args
637
+ end
638
+
639
+ def test_should_run_before_callbacks_within_the_context_of_the_record
640
+ context = nil
641
+ @machine.before_transition(lambda {context = self})
642
+
324
643
  @transition.perform
325
644
  assert_equal @record, context
326
645
  end
@@ -375,274 +694,163 @@ begin
375
694
  end
376
695
  end
377
696
 
378
- class MachineWithLoopbackTest < BaseTestCase
697
+ class MachineWithFailedBeforeCallbacksTest < BaseTestCase
379
698
  def setup
380
- dirty_attributes = nil
699
+ before_count = 0
700
+ after_count = 0
701
+
702
+ @resource = new_resource
703
+ @machine = StateMachine::Machine.new(@resource)
704
+ @machine.state :parked, :idling
705
+ @machine.event :ignite
706
+ @machine.before_transition(lambda {before_count += 1; throw :halt})
707
+ @machine.before_transition(lambda {before_count += 1})
708
+ @machine.after_transition(lambda {after_count += 1})
709
+
710
+ @record = @resource.new(:state => 'parked')
711
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
712
+ @result = @transition.perform
381
713
 
714
+ @before_count = before_count
715
+ @after_count = after_count
716
+ end
717
+
718
+ def test_should_not_be_successful
719
+ assert !@result
720
+ end
721
+
722
+ def test_should_not_change_current_state
723
+ assert_equal 'parked', @record.state
724
+ end
725
+
726
+ def test_should_not_run_action
727
+ assert @record.respond_to?(:new?) ? @record.new? : @record.new_record?
728
+ end
729
+
730
+ def test_should_not_run_further_before_callbacks
731
+ assert_equal 1, @before_count
732
+ end
733
+
734
+ def test_should_not_run_after_callbacks
735
+ assert_equal 0, @after_count
736
+ end
737
+ end
738
+
739
+ class MachineWithFailedActionTest < BaseTestCase
740
+ def setup
382
741
  @resource = new_resource do
383
- property :updated_at, DateTime
384
- auto_migrate!
385
-
386
- # Simulate dm-timestamps
387
- before :update do
388
- dirty_attributes = self.dirty_attributes.dup
389
-
390
- return unless dirty?
391
- self.updated_at = DateTime.now
392
- end
742
+ before(:create) { throw :halt }
393
743
  end
394
744
 
395
- @machine = StateMachine::Machine.new(@resource, :initial => :parked)
396
- @machine.event :park
745
+ @machine = StateMachine::Machine.new(@resource)
746
+ @machine.state :parked, :idling
747
+ @machine.event :ignite
397
748
 
398
- @record = @resource.create(:updated_at => Time.now - 1)
399
- @timestamp = @record.updated_at
749
+ before_transition_called = false
750
+ after_transition_called = false
751
+ after_transition_with_failures_called = false
752
+ @machine.before_transition(lambda {before_transition_called = true})
753
+ @machine.after_transition(lambda {after_transition_called = true})
754
+ @machine.after_transition(lambda {after_transition_with_failures_called = true}, :include_failures => true)
400
755
 
401
- @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
402
- @transition.perform
756
+ @record = @resource.new(:state => 'parked')
757
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
758
+ @result = @transition.perform
403
759
 
404
- @dirty_attributes = dirty_attributes
760
+ @before_transition_called = before_transition_called
761
+ @after_transition_called = after_transition_called
762
+ @after_transition_with_failures_called = after_transition_with_failures_called
405
763
  end
406
764
 
407
- def test_should_include_state_in_dirty_attributes
408
- expected = {@resource.properties[:state] => 'parked'}
409
- assert_equal expected, @dirty_attributes
765
+ def test_should_not_be_successful
766
+ assert !@result
410
767
  end
411
768
 
412
- def test_should_update_record
413
- assert_not_equal @timestamp, @record.updated_at
769
+ def test_should_not_change_current_state
770
+ assert_equal 'parked', @record.state
771
+ end
772
+
773
+ def test_should_not_save_record
774
+ assert @record.respond_to?(:new?) ? @record.new? : @record.new_record?
775
+ end
776
+
777
+ def test_should_run_before_callback
778
+ assert @before_transition_called
779
+ end
780
+
781
+ def test_should_not_run_after_callback_if_not_including_failures
782
+ assert !@after_transition_called
783
+ end
784
+
785
+ def test_should_run_after_callback_if_including_failures
786
+ assert @after_transition_with_failures_called
787
+ end
788
+ end
789
+
790
+ class MachineWithFailedAfterCallbacksTest < BaseTestCase
791
+ def setup
792
+ after_count = 0
793
+
794
+ @resource = new_resource
795
+ @machine = StateMachine::Machine.new(@resource)
796
+ @machine.state :parked, :idling
797
+ @machine.event :ignite
798
+ @machine.after_transition(lambda {after_count += 1; throw :halt})
799
+ @machine.after_transition(lambda {after_count += 1})
800
+
801
+ @record = @resource.new(:state => 'parked')
802
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
803
+ @result = @transition.perform
804
+
805
+ @after_count = after_count
806
+ end
807
+
808
+ def test_should_be_successful
809
+ assert @result
810
+ end
811
+
812
+ def test_should_change_current_state
813
+ assert_equal 'idling', @record.state
814
+ end
815
+
816
+ def test_should_save_record
817
+ assert !(@record.respond_to?(:new?) ? @record.new? : @record.new_record?)
818
+ end
819
+
820
+ def test_should_not_run_further_after_callbacks
821
+ assert_equal 1, @after_count
414
822
  end
415
823
  end
416
824
 
417
825
  begin
418
- gem 'dm-observer', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
419
- require 'dm-observer'
826
+ gem 'dm-validations', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
827
+ require 'dm-validations'
420
828
 
421
- class MachineWithObserversTest < BaseTestCase
829
+ class MachineWithValidationsTest < BaseTestCase
422
830
  def setup
423
831
  @resource = new_resource
424
832
  @machine = StateMachine::Machine.new(@resource)
425
- @machine.state :parked, :idling
426
- @machine.event :ignite
427
- @record = @resource.new(:state => 'parked')
428
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
833
+ @machine.state :parked
834
+
835
+ @record = @resource.new
429
836
  end
430
837
 
431
- def test_should_provide_matcher_helpers
432
- matchers = []
433
-
434
- new_observer(@resource) do
435
- matchers = [all, any, same]
436
- end
838
+ def test_should_invalidate_using_errors
839
+ @record.state = 'parked'
437
840
 
438
- assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
841
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, :park]])
842
+ assert_equal ['cannot transition via "park"'], @record.errors.on(:state)
439
843
  end
440
844
 
441
- def test_should_call_before_transition_callback_if_requirements_match
442
- called = false
443
-
444
- observer = new_observer(@resource) do
445
- before_transition :from => :parked do
446
- called = true
447
- end
448
- end
845
+ def test_should_auto_prefix_custom_attributes_on_invalidation
846
+ @machine.invalidate(@record, :event, :invalid)
449
847
 
450
- @transition.perform
451
- assert called
848
+ assert_equal ['is invalid'], @record.errors.on(:state_event)
452
849
  end
453
850
 
454
- def test_should_not_call_before_transition_callback_if_requirements_do_not_match
455
- called = false
456
-
457
- observer = new_observer(@resource) do
458
- before_transition :from => :idling do
459
- called = true
460
- end
461
- end
462
-
463
- @transition.perform
464
- assert !called
465
- end
466
-
467
- def test_should_pass_transition_to_before_callbacks
468
- callback_args = nil
469
-
470
- observer = new_observer(@resource) do
471
- before_transition do |*args|
472
- callback_args = args
473
- end
474
- end
475
-
476
- @transition.perform
477
- assert_equal [@transition], callback_args
478
- end
479
-
480
- def test_should_call_after_transition_callback_if_requirements_match
481
- called = false
482
-
483
- observer = new_observer(@resource) do
484
- after_transition :from => :parked do
485
- called = true
486
- end
487
- end
488
-
489
- @transition.perform
490
- assert called
491
- end
492
-
493
- def test_should_not_call_after_transition_callback_if_requirements_do_not_match
494
- called = false
495
-
496
- observer = new_observer(@resource) do
497
- after_transition :from => :idling do
498
- called = true
499
- end
500
- end
501
-
502
- @transition.perform
503
- assert !called
504
- end
505
-
506
- def test_should_pass_transition_to_after_callbacks
507
- callback_args = nil
508
-
509
- observer = new_observer(@resource) do
510
- after_transition do |*args|
511
- callback_args = args
512
- end
513
- end
514
-
515
- @transition.perform
516
- assert_equal [@transition], callback_args
517
- end
518
-
519
- def test_should_raise_exception_if_targeting_invalid_machine
520
- assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) do
521
- new_observer(@resource) do
522
- before_transition :invalid, :from => :parked do
523
- end
524
- end
525
- end
526
- end
527
-
528
- def test_should_allow_targeting_specific_machine
529
- @second_machine = StateMachine::Machine.new(@resource, :status)
530
- @resource.auto_migrate!
531
-
532
- called_state = false
533
- called_status = false
534
-
535
- observer = new_observer(@resource) do
536
- before_transition :state, :from => :parked do
537
- called_state = true
538
- end
539
-
540
- before_transition :status, :from => :parked do
541
- called_status = true
542
- end
543
- end
544
-
545
- @transition.perform
546
-
547
- assert called_state
548
- assert !called_status
549
- end
550
-
551
- def test_should_allow_targeting_multiple_specific_machines
552
- @second_machine = StateMachine::Machine.new(@resource, :status)
553
- @second_machine.state :parked, :idling
554
- @second_machine.event :ignite
555
- @resource.auto_migrate!
556
-
557
- called_attribute = nil
558
-
559
- attributes = []
560
- observer = new_observer(@resource) do
561
- before_transition :state, :status, :from => :parked do |transition|
562
- called_attribute = transition.attribute
563
- end
564
- end
565
-
566
- @transition.perform
567
- assert_equal :state, called_attribute
568
-
569
- StateMachine::Transition.new(@record, @second_machine, :ignite, :parked, :idling).perform
570
- assert_equal :status, called_attribute
571
- end
572
- end
573
-
574
- class MachineWithMixedCallbacksTest < BaseTestCase
575
- def setup
576
- @resource = new_resource
577
- @machine = StateMachine::Machine.new(@resource)
578
- @machine.state :parked, :idling
579
- @machine.event :ignite
580
- @record = @resource.new(:state => 'parked')
581
- @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
582
-
583
- @notifications = notifications = []
584
-
585
- # Create callbacks
586
- @machine.before_transition(lambda {notifications << :callback_before_transition})
587
- @machine.after_transition(lambda {notifications << :callback_after_transition})
588
-
589
- observer = new_observer(@resource) do
590
- before_transition do
591
- notifications << :observer_before_transition
592
- end
593
-
594
- after_transition do
595
- notifications << :observer_after_transition
596
- end
597
- end
598
-
599
- @transition.perform
600
- end
601
-
602
- def test_should_invoke_callbacks_in_specific_order
603
- expected = [
604
- :callback_before_transition,
605
- :observer_before_transition,
606
- :callback_after_transition,
607
- :observer_after_transition
608
- ]
609
-
610
- assert_equal expected, @notifications
611
- end
612
- end
613
- rescue LoadError
614
- $stderr.puts "Skipping DataMapper Observer tests. `gem install dm-observer#{" -v #{ENV['DM_VERSION']}" if ENV['DM_VERSION']}` and try again."
615
- end
616
-
617
- begin
618
- gem 'dm-validations', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
619
- require 'dm-validations'
620
-
621
- class MachineWithValidationsTest < BaseTestCase
622
- def setup
623
- @resource = new_resource
624
- @machine = StateMachine::Machine.new(@resource)
625
- @machine.state :parked
626
-
627
- @record = @resource.new
628
- end
629
-
630
- def test_should_invalidate_using_errors
631
- @record.state = 'parked'
632
-
633
- @machine.invalidate(@record, :state, :invalid_transition, [[:event, :park]])
634
- assert_equal ['cannot transition via "park"'], @record.errors.on(:state)
635
- end
636
-
637
- def test_should_auto_prefix_custom_attributes_on_invalidation
638
- @machine.invalidate(@record, :event, :invalid)
639
-
640
- assert_equal ['is invalid'], @record.errors.on(:state_event)
641
- end
642
-
643
- def test_should_clear_errors_on_reset
644
- @record.state = 'parked'
645
- @record.errors.add(:state, 'is invalid')
851
+ def test_should_clear_errors_on_reset
852
+ @record.state = 'parked'
853
+ @record.errors.add(:state, 'is invalid')
646
854
 
647
855
  @machine.reset(@record)
648
856
  assert_nil @record.errors.on(:id)
@@ -836,32 +1044,35 @@ begin
836
1044
  assert_equal 'idling', @record.state
837
1045
  end
838
1046
 
839
- def test_should_run_after_callbacks
840
- ran_callback = false
841
- @machine.after_transition { ran_callback = true }
842
-
843
- @record.save
844
- assert ran_callback
845
- end
846
-
847
- def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
848
- @resource.before(:create) { throw :halt }
849
-
850
- ran_callback = false
851
- @machine.after_transition { ran_callback = true }
852
-
853
- @record.save
854
- assert !ran_callback
855
- end
856
-
857
- def test_should_run_after_callbacks_with_failures_enabled_if_fails
858
- @resource.before(:create) { throw :halt }
1047
+ # See README caveats
1048
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.9.7')
1049
+ def test_should_run_after_callbacks
1050
+ ran_callback = false
1051
+ @machine.after_transition { ran_callback = true }
1052
+
1053
+ @record.save
1054
+ assert ran_callback
1055
+ end
859
1056
 
860
- ran_callback = false
861
- @machine.after_transition(:include_failures => true) { ran_callback = true }
1057
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
1058
+ @resource.before(:create) { throw :halt }
1059
+
1060
+ ran_callback = false
1061
+ @machine.after_transition { ran_callback = true }
1062
+
1063
+ @record.save
1064
+ assert !ran_callback
1065
+ end
862
1066
 
863
- @record.save
864
- assert ran_callback
1067
+ def test_should_run_after_callbacks_with_failures_enabled_if_fails
1068
+ @resource.before(:create) { throw :halt }
1069
+
1070
+ ran_callback = false
1071
+ @machine.after_transition(:include_failures => true) { ran_callback = true }
1072
+
1073
+ @record.save
1074
+ assert ran_callback
1075
+ end
865
1076
  end
866
1077
  end
867
1078
 
@@ -901,7 +1112,346 @@ begin
901
1112
  rescue LoadError
902
1113
  $stderr.puts "Skipping DataMapper Validation tests. `gem install dm-validations#{" -v #{ENV['DM_VERSION']}" if ENV['DM_VERSION']}` and try again."
903
1114
  end
1115
+
1116
+ begin
1117
+ gem 'dm-observer', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
1118
+ require 'dm-observer'
1119
+
1120
+ class MachineWithObserversTest < BaseTestCase
1121
+ def setup
1122
+ @resource = new_resource
1123
+ @machine = StateMachine::Machine.new(@resource)
1124
+ @machine.state :parked, :idling
1125
+ @machine.event :ignite
1126
+ @record = @resource.new(:state => 'parked')
1127
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1128
+ end
1129
+
1130
+ def test_should_provide_matcher_helpers
1131
+ matchers = []
1132
+
1133
+ new_observer(@resource) do
1134
+ matchers = [all, any, same]
1135
+ end
1136
+
1137
+ assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
1138
+ end
1139
+
1140
+ def test_should_call_before_transition_callback_if_requirements_match
1141
+ called = false
1142
+
1143
+ observer = new_observer(@resource) do
1144
+ before_transition :from => :parked do
1145
+ called = true
1146
+ end
1147
+ end
1148
+
1149
+ @transition.perform
1150
+ assert called
1151
+ end
1152
+
1153
+ def test_should_not_call_before_transition_callback_if_requirements_do_not_match
1154
+ called = false
1155
+
1156
+ observer = new_observer(@resource) do
1157
+ before_transition :from => :idling do
1158
+ called = true
1159
+ end
1160
+ end
1161
+
1162
+ @transition.perform
1163
+ assert !called
1164
+ end
1165
+
1166
+ def test_should_pass_transition_to_before_callbacks
1167
+ callback_args = nil
1168
+
1169
+ observer = new_observer(@resource) do
1170
+ before_transition do |*args|
1171
+ callback_args = args
1172
+ end
1173
+ end
1174
+
1175
+ @transition.perform
1176
+ assert_equal [@transition], callback_args
1177
+ end
1178
+
1179
+ def test_should_call_after_transition_callback_if_requirements_match
1180
+ called = false
1181
+
1182
+ observer = new_observer(@resource) do
1183
+ after_transition :from => :parked do
1184
+ called = true
1185
+ end
1186
+ end
1187
+
1188
+ @transition.perform
1189
+ assert called
1190
+ end
1191
+
1192
+ def test_should_not_call_after_transition_callback_if_requirements_do_not_match
1193
+ called = false
1194
+
1195
+ observer = new_observer(@resource) do
1196
+ after_transition :from => :idling do
1197
+ called = true
1198
+ end
1199
+ end
1200
+
1201
+ @transition.perform
1202
+ assert !called
1203
+ end
1204
+
1205
+ def test_should_pass_transition_to_after_callbacks
1206
+ callback_args = nil
1207
+
1208
+ observer = new_observer(@resource) do
1209
+ after_transition do |*args|
1210
+ callback_args = args
1211
+ end
1212
+ end
1213
+
1214
+ @transition.perform
1215
+ assert_equal [@transition], callback_args
1216
+ end
1217
+
1218
+ def test_should_raise_exception_if_targeting_invalid_machine
1219
+ assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) do
1220
+ new_observer(@resource) do
1221
+ before_transition :invalid, :from => :parked do
1222
+ end
1223
+ end
1224
+ end
1225
+ end
1226
+
1227
+ def test_should_allow_targeting_specific_machine
1228
+ @second_machine = StateMachine::Machine.new(@resource, :status)
1229
+ @resource.auto_migrate!
1230
+
1231
+ called_state = false
1232
+ called_status = false
1233
+
1234
+ observer = new_observer(@resource) do
1235
+ before_transition :state, :from => :parked do
1236
+ called_state = true
1237
+ end
1238
+
1239
+ before_transition :status, :from => :parked do
1240
+ called_status = true
1241
+ end
1242
+ end
1243
+
1244
+ @transition.perform
1245
+
1246
+ assert called_state
1247
+ assert !called_status
1248
+ end
1249
+
1250
+ def test_should_allow_targeting_multiple_specific_machines
1251
+ @second_machine = StateMachine::Machine.new(@resource, :status)
1252
+ @second_machine.state :parked, :idling
1253
+ @second_machine.event :ignite
1254
+ @resource.auto_migrate!
1255
+
1256
+ called_attribute = nil
1257
+
1258
+ attributes = []
1259
+ observer = new_observer(@resource) do
1260
+ before_transition :state, :status, :from => :parked do |transition|
1261
+ called_attribute = transition.attribute
1262
+ end
1263
+ end
1264
+
1265
+ @transition.perform
1266
+ assert_equal :state, called_attribute
1267
+
1268
+ StateMachine::Transition.new(@record, @second_machine, :ignite, :parked, :idling).perform
1269
+ assert_equal :status, called_attribute
1270
+ end
1271
+ end
1272
+
1273
+ class MachineWithMixedCallbacksTest < BaseTestCase
1274
+ def setup
1275
+ @resource = new_resource
1276
+ @machine = StateMachine::Machine.new(@resource)
1277
+ @machine.state :parked, :idling
1278
+ @machine.event :ignite
1279
+ @record = @resource.new(:state => 'parked')
1280
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1281
+
1282
+ @notifications = notifications = []
1283
+
1284
+ # Create callbacks
1285
+ @machine.before_transition(lambda {notifications << :callback_before_transition})
1286
+ @machine.after_transition(lambda {notifications << :callback_after_transition})
1287
+
1288
+ observer = new_observer(@resource) do
1289
+ before_transition do
1290
+ notifications << :observer_before_transition
1291
+ end
1292
+
1293
+ after_transition do
1294
+ notifications << :observer_after_transition
1295
+ end
1296
+ end
1297
+
1298
+ @transition.perform
1299
+ end
1300
+
1301
+ def test_should_invoke_callbacks_in_specific_order
1302
+ expected = [
1303
+ :callback_before_transition,
1304
+ :observer_before_transition,
1305
+ :callback_after_transition,
1306
+ :observer_after_transition
1307
+ ]
1308
+
1309
+ assert_equal expected, @notifications
1310
+ end
1311
+ end
1312
+ rescue LoadError
1313
+ $stderr.puts "Skipping DataMapper Observer tests. `gem install dm-observer#{" -v #{ENV['DM_VERSION']}" if ENV['DM_VERSION']}` and try again."
1314
+ end
1315
+
1316
+ class MachineWithScopesTest < BaseTestCase
1317
+ def setup
1318
+ @resource = new_resource
1319
+ @machine = StateMachine::Machine.new(@resource)
1320
+ @machine.state :parked, :first_gear
1321
+ @machine.state :idling, :value => lambda {'idling'}
1322
+ end
1323
+
1324
+ def test_should_create_singular_with_scope
1325
+ assert @resource.respond_to?(:with_state)
1326
+ end
1327
+
1328
+ def test_should_only_include_records_with_state_in_singular_with_scope
1329
+ parked = @resource.create :state => 'parked'
1330
+ idling = @resource.create :state => 'idling'
1331
+
1332
+ assert_equal [parked], @resource.with_state(:parked)
1333
+ end
1334
+
1335
+ def test_should_create_plural_with_scope
1336
+ assert @resource.respond_to?(:with_states)
1337
+ end
1338
+
1339
+ def test_should_only_include_records_with_states_in_plural_with_scope
1340
+ parked = @resource.create :state => 'parked'
1341
+ idling = @resource.create :state => 'idling'
1342
+
1343
+ assert_equal [parked, idling], @resource.with_states(:parked, :idling)
1344
+ end
1345
+
1346
+ def test_should_create_singular_without_scope
1347
+ assert @resource.respond_to?(:without_state)
1348
+ end
1349
+
1350
+ def test_should_only_include_records_without_state_in_singular_without_scope
1351
+ parked = @resource.create :state => 'parked'
1352
+ idling = @resource.create :state => 'idling'
1353
+
1354
+ assert_equal [parked], @resource.without_state(:idling)
1355
+ end
1356
+
1357
+ def test_should_create_plural_without_scope
1358
+ assert @resource.respond_to?(:without_states)
1359
+ end
1360
+
1361
+ def test_should_only_include_records_without_states_in_plural_without_scope
1362
+ parked = @resource.create :state => 'parked'
1363
+ idling = @resource.create :state => 'idling'
1364
+ first_gear = @resource.create :state => 'first_gear'
1365
+
1366
+ assert_equal [parked, idling], @resource.without_states(:first_gear)
1367
+ end
1368
+
1369
+ def test_should_allow_chaining_scopes
1370
+ parked = @resource.create :state => 'parked'
1371
+ idling = @resource.create :state => 'idling'
1372
+
1373
+ assert_equal [idling], @resource.without_state(:parked).with_state(:idling)
1374
+ end
1375
+ end
1376
+
1377
+ class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
1378
+ def setup
1379
+ @resource = new_resource
1380
+ @machine = StateMachine::Machine.new(@resource, :state)
1381
+
1382
+ @subclass = Class.new(@resource)
1383
+ @subclass_machine = @subclass.state_machine(:state) {}
1384
+ @subclass_machine.state :parked, :idling, :first_gear
1385
+ end
1386
+
1387
+ def test_should_only_include_records_with_subclass_states_in_with_scope
1388
+ parked = @subclass.create :state => 'parked'
1389
+ idling = @subclass.create :state => 'idling'
1390
+
1391
+ assert_equal [parked, idling], @subclass.with_states(:parked, :idling)
1392
+ end
1393
+
1394
+ def test_should_only_include_records_without_subclass_states_in_without_scope
1395
+ parked = @subclass.create :state => 'parked'
1396
+ idling = @subclass.create :state => 'idling'
1397
+ first_gear = @subclass.create :state => 'first_gear'
1398
+
1399
+ assert_equal [parked, idling], @subclass.without_states(:first_gear)
1400
+ end
1401
+ end
1402
+
1403
+ class MachineWithComplexPluralizationScopesTest < BaseTestCase
1404
+ def setup
1405
+ @resource = new_resource
1406
+ @machine = StateMachine::Machine.new(@resource, :status)
1407
+ end
1408
+
1409
+ def test_should_create_singular_with_scope
1410
+ assert @resource.respond_to?(:with_status)
1411
+ end
1412
+
1413
+ def test_should_create_plural_with_scope
1414
+ assert @resource.respond_to?(:with_statuses)
1415
+ end
1416
+ end
1417
+
1418
+ class MachineWithScopesAndJoinsTest < BaseTestCase
1419
+ def setup
1420
+ @company = new_resource(:company)
1421
+ DataMapperTest.const_set('Company', @company)
1422
+
1423
+ @vehicle = new_resource(:vehicle) do
1424
+ property :company_id, Integer
1425
+ auto_migrate!
1426
+
1427
+ belongs_to :company
1428
+ end
1429
+ DataMapperTest.const_set('Vehicle', @vehicle)
1430
+
1431
+ @company_machine = StateMachine::Machine.new(@company, :initial => :active)
1432
+ @vehicle_machine = StateMachine::Machine.new(@vehicle, :initial => :parked)
1433
+ @vehicle_machine.state :idling
1434
+
1435
+ @ford = @company.create
1436
+ @mustang = @vehicle.create(:company => @ford)
1437
+ end
1438
+
1439
+ def test_should_find_records_in_with_scope
1440
+ assert_equal [@mustang], @vehicle.with_states(:parked).all(Vehicle.company.state => 'active')
1441
+ end
1442
+
1443
+ def test_should_find_records_in_without_scope
1444
+ assert_equal [@mustang], @vehicle.without_states(:idling).all(Vehicle.company.state => 'active')
1445
+ end
1446
+
1447
+ def teardown
1448
+ DataMapperTest.class_eval do
1449
+ remove_const('Vehicle')
1450
+ remove_const('Company')
1451
+ end
1452
+ end
1453
+ end
904
1454
  end
905
- rescue LoadError
1455
+ rescue LoadError => ex
906
1456
  $stderr.puts "Skipping DataMapper tests. `gem install dm-core#{" -v #{ENV['DM_VERSION']}" if ENV['DM_VERSION']}`, `gem install cucumber rspec hoe launchy do_sqlite3` and try again."
907
1457
  end