state_machine 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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