hsume2-state_machine 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/CHANGELOG.rdoc +413 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +717 -0
  4. data/Rakefile +77 -0
  5. data/examples/AutoShop_state.png +0 -0
  6. data/examples/Car_state.png +0 -0
  7. data/examples/TrafficLight_state.png +0 -0
  8. data/examples/Vehicle_state.png +0 -0
  9. data/examples/auto_shop.rb +11 -0
  10. data/examples/car.rb +19 -0
  11. data/examples/merb-rest/controller.rb +51 -0
  12. data/examples/merb-rest/model.rb +28 -0
  13. data/examples/merb-rest/view_edit.html.erb +24 -0
  14. data/examples/merb-rest/view_index.html.erb +23 -0
  15. data/examples/merb-rest/view_new.html.erb +13 -0
  16. data/examples/merb-rest/view_show.html.erb +17 -0
  17. data/examples/rails-rest/controller.rb +43 -0
  18. data/examples/rails-rest/migration.rb +11 -0
  19. data/examples/rails-rest/model.rb +23 -0
  20. data/examples/rails-rest/view_edit.html.erb +25 -0
  21. data/examples/rails-rest/view_index.html.erb +23 -0
  22. data/examples/rails-rest/view_new.html.erb +14 -0
  23. data/examples/rails-rest/view_show.html.erb +17 -0
  24. data/examples/traffic_light.rb +7 -0
  25. data/examples/vehicle.rb +31 -0
  26. data/init.rb +1 -0
  27. data/lib/state_machine.rb +448 -0
  28. data/lib/state_machine/alternate_machine.rb +79 -0
  29. data/lib/state_machine/assertions.rb +36 -0
  30. data/lib/state_machine/branch.rb +224 -0
  31. data/lib/state_machine/callback.rb +236 -0
  32. data/lib/state_machine/condition_proxy.rb +94 -0
  33. data/lib/state_machine/error.rb +13 -0
  34. data/lib/state_machine/eval_helpers.rb +86 -0
  35. data/lib/state_machine/event.rb +304 -0
  36. data/lib/state_machine/event_collection.rb +139 -0
  37. data/lib/state_machine/extensions.rb +149 -0
  38. data/lib/state_machine/initializers.rb +4 -0
  39. data/lib/state_machine/initializers/merb.rb +1 -0
  40. data/lib/state_machine/initializers/rails.rb +25 -0
  41. data/lib/state_machine/integrations.rb +110 -0
  42. data/lib/state_machine/integrations/active_model.rb +502 -0
  43. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  44. data/lib/state_machine/integrations/active_model/observer.rb +45 -0
  45. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  46. data/lib/state_machine/integrations/active_record.rb +424 -0
  47. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  48. data/lib/state_machine/integrations/active_record/versions.rb +143 -0
  49. data/lib/state_machine/integrations/base.rb +91 -0
  50. data/lib/state_machine/integrations/data_mapper.rb +392 -0
  51. data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
  52. data/lib/state_machine/integrations/data_mapper/versions.rb +62 -0
  53. data/lib/state_machine/integrations/mongo_mapper.rb +272 -0
  54. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  55. data/lib/state_machine/integrations/mongo_mapper/versions.rb +110 -0
  56. data/lib/state_machine/integrations/mongoid.rb +357 -0
  57. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  58. data/lib/state_machine/integrations/mongoid/versions.rb +18 -0
  59. data/lib/state_machine/integrations/sequel.rb +428 -0
  60. data/lib/state_machine/integrations/sequel/versions.rb +36 -0
  61. data/lib/state_machine/machine.rb +1873 -0
  62. data/lib/state_machine/machine_collection.rb +87 -0
  63. data/lib/state_machine/matcher.rb +123 -0
  64. data/lib/state_machine/matcher_helpers.rb +54 -0
  65. data/lib/state_machine/node_collection.rb +157 -0
  66. data/lib/state_machine/path.rb +120 -0
  67. data/lib/state_machine/path_collection.rb +90 -0
  68. data/lib/state_machine/state.rb +271 -0
  69. data/lib/state_machine/state_collection.rb +112 -0
  70. data/lib/state_machine/transition.rb +458 -0
  71. data/lib/state_machine/transition_collection.rb +244 -0
  72. data/lib/tasks/state_machine.rake +1 -0
  73. data/lib/tasks/state_machine.rb +27 -0
  74. data/test/files/en.yml +17 -0
  75. data/test/files/switch.rb +11 -0
  76. data/test/functional/alternate_state_machine_test.rb +122 -0
  77. data/test/functional/state_machine_test.rb +993 -0
  78. data/test/test_helper.rb +4 -0
  79. data/test/unit/assertions_test.rb +40 -0
  80. data/test/unit/branch_test.rb +890 -0
  81. data/test/unit/callback_test.rb +701 -0
  82. data/test/unit/condition_proxy_test.rb +328 -0
  83. data/test/unit/error_test.rb +43 -0
  84. data/test/unit/eval_helpers_test.rb +222 -0
  85. data/test/unit/event_collection_test.rb +358 -0
  86. data/test/unit/event_test.rb +985 -0
  87. data/test/unit/integrations/active_model_test.rb +1097 -0
  88. data/test/unit/integrations/active_record_test.rb +2021 -0
  89. data/test/unit/integrations/base_test.rb +99 -0
  90. data/test/unit/integrations/data_mapper_test.rb +1909 -0
  91. data/test/unit/integrations/mongo_mapper_test.rb +1611 -0
  92. data/test/unit/integrations/mongoid_test.rb +1591 -0
  93. data/test/unit/integrations/sequel_test.rb +1523 -0
  94. data/test/unit/integrations_test.rb +61 -0
  95. data/test/unit/invalid_event_test.rb +20 -0
  96. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  97. data/test/unit/invalid_transition_test.rb +77 -0
  98. data/test/unit/machine_collection_test.rb +599 -0
  99. data/test/unit/machine_test.rb +3043 -0
  100. data/test/unit/matcher_helpers_test.rb +37 -0
  101. data/test/unit/matcher_test.rb +155 -0
  102. data/test/unit/node_collection_test.rb +217 -0
  103. data/test/unit/path_collection_test.rb +266 -0
  104. data/test/unit/path_test.rb +485 -0
  105. data/test/unit/state_collection_test.rb +310 -0
  106. data/test/unit/state_machine_test.rb +31 -0
  107. data/test/unit/state_test.rb +924 -0
  108. data/test/unit/transition_collection_test.rb +2102 -0
  109. data/test/unit/transition_test.rb +1541 -0
  110. metadata +207 -0
@@ -0,0 +1,99 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ # Load library
4
+ require 'rubygems'
5
+
6
+ module BaseTest
7
+ class IntegrationTest < Test::Unit::TestCase
8
+ def test_should_have_an_integration_name
9
+ assert_equal :base, StateMachine::Integrations::Base.integration_name
10
+ end
11
+
12
+ def test_should_not_be_available
13
+ assert !StateMachine::Integrations::Base.available?
14
+ end
15
+
16
+ def test_should_not_match_any_classes
17
+ assert !StateMachine::Integrations::Base.matches?(Class.new)
18
+ end
19
+
20
+ def test_should_not_have_a_locale_path
21
+ assert_nil StateMachine::Integrations::Base.locale_path
22
+ end
23
+ end
24
+
25
+ class IncludedTest < Test::Unit::TestCase
26
+ def setup
27
+ @integration = Module.new
28
+ StateMachine::Integrations.const_set('Custom', @integration)
29
+
30
+ @integration.class_eval do
31
+ include StateMachine::Integrations::Base
32
+ end
33
+ end
34
+
35
+ def test_should_not_have_any_defaults
36
+ assert_nil @integration.defaults
37
+ end
38
+
39
+ def test_should_not_have_any_versions
40
+ assert_equal [], @integration.versions
41
+ end
42
+
43
+ def test_should_track_version
44
+ version1 = @integration.version '1.0' do
45
+ def self.active?
46
+ true
47
+ end
48
+ end
49
+
50
+ version2 = @integration.version '2.0' do
51
+ def self.active?
52
+ false
53
+ end
54
+ end
55
+
56
+ assert_equal [version1, version2], @integration.versions
57
+ end
58
+
59
+ def test_should_allow_active_versions_to_override_default_behavior
60
+ @integration.class_eval do
61
+ def version1_included?
62
+ false
63
+ end
64
+
65
+ def version2_included?
66
+ false
67
+ end
68
+ end
69
+
70
+ version1 = @integration.version '1.0' do
71
+ def self.active?
72
+ true
73
+ end
74
+
75
+ def version1_included?
76
+ true
77
+ end
78
+ end
79
+
80
+ version2 = @integration.version '2.0' do
81
+ def self.active?
82
+ false
83
+ end
84
+
85
+ def version2_included?
86
+ true
87
+ end
88
+ end
89
+
90
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
91
+ assert @machine.version1_included?
92
+ assert !@machine.version2_included?
93
+ end
94
+
95
+ def teardown
96
+ StateMachine::Integrations.send(:remove_const, 'Custom')
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,1909 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ # Load library
4
+ require 'rubygems'
5
+
6
+ gem 'dm-core', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=0.9.4'
7
+ require 'dm-core'
8
+ require 'dm-core/version' unless defined?(::DataMapper::VERSION)
9
+
10
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.3')
11
+ gem 'dm-migrations', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=0.10.3'
12
+ require 'dm-migrations'
13
+ end
14
+
15
+ # Establish database connection
16
+ DataMapper.setup(:default, 'sqlite3::memory:')
17
+ DataObjects::Sqlite3.logger = DataObjects::Logger.new("#{File.dirname(__FILE__)}/../../data_mapper.log", :info)
18
+
19
+ module DataMapperTest
20
+ class BaseTestCase < Test::Unit::TestCase
21
+ def default_test
22
+ end
23
+
24
+ protected
25
+ # Creates a new DataMapper resource (and the associated table)
26
+ def new_resource(create_table = :foo, &block)
27
+ table_name = create_table || :foo
28
+
29
+ resource = Class.new do
30
+ (class << self; self; end).class_eval do
31
+ define_method(:name) { "DataMapperTest::#{table_name.to_s.capitalize}" }
32
+ end
33
+ end
34
+
35
+ resource.class_eval do
36
+ include DataMapper::Resource
37
+
38
+ storage_names[:default] = table_name.to_s
39
+
40
+ property :id, resource.class_eval('Serial')
41
+ property :state, String
42
+
43
+ auto_migrate! if create_table
44
+ end
45
+ resource.class_eval(&block) if block_given?
46
+ resource
47
+ end
48
+
49
+ # Creates a new DataMapper observer
50
+ def new_observer(resource, &block)
51
+ observer = Class.new do
52
+ include DataMapper::Observer
53
+ end
54
+ observer.observe(resource)
55
+ observer.class_eval(&block) if block_given?
56
+ observer
57
+ end
58
+ end
59
+
60
+ class IntegrationTest < BaseTestCase
61
+ def test_should_have_an_integration_name
62
+ assert_equal :data_mapper, StateMachine::Integrations::DataMapper.integration_name
63
+ end
64
+
65
+ def test_should_be_available
66
+ assert StateMachine::Integrations::DataMapper.available?
67
+ end
68
+
69
+ def test_should_match_if_class_includes_data_mapper
70
+ assert StateMachine::Integrations::DataMapper.matches?(new_resource)
71
+ end
72
+
73
+ def test_should_not_match_if_class_does_not_include_data_mapper
74
+ assert !StateMachine::Integrations::DataMapper.matches?(Class.new)
75
+ end
76
+
77
+ def test_should_have_defaults
78
+ assert_equal e = {:action => :save, :use_transactions => false}, StateMachine::Integrations::DataMapper.defaults
79
+ end
80
+
81
+ def test_should_not_have_a_locale_path
82
+ assert_nil StateMachine::Integrations::DataMapper.locale_path
83
+ end
84
+ end
85
+
86
+ class MachineWithoutDatabaseTest < BaseTestCase
87
+ def setup
88
+ @resource = new_resource(false) do
89
+ # Simulate the database not being available entirely
90
+ def self.repository
91
+ raise DataObjects::SyntaxError
92
+ end
93
+ end
94
+ end
95
+
96
+ def test_should_allow_machine_creation
97
+ assert_nothing_raised { StateMachine::Machine.new(@resource) }
98
+ end
99
+ end
100
+
101
+ class MachineUnmigratedTest < BaseTestCase
102
+ def setup
103
+ @resource = new_resource(false)
104
+ end
105
+
106
+ def test_should_allow_machine_creation
107
+ assert_nothing_raised { StateMachine::Machine.new(@resource) }
108
+ end
109
+ end
110
+
111
+ class MachineByDefaultTest < BaseTestCase
112
+ def setup
113
+ @resource = new_resource
114
+ @machine = StateMachine::Machine.new(@resource)
115
+ end
116
+
117
+ def test_should_use_save_as_action
118
+ assert_equal :save, @machine.action
119
+ end
120
+
121
+ def test_should_not_use_transactions
122
+ assert_equal false, @machine.use_transactions
123
+ end
124
+
125
+ def test_should_not_have_any_before_callbacks
126
+ assert_equal 0, @machine.callbacks[:before].size
127
+ end
128
+
129
+ def test_should_not_have_any_after_callbacks
130
+ assert_equal 0, @machine.callbacks[:after].size
131
+ end
132
+ end
133
+
134
+ class MachineWithStatesTest < BaseTestCase
135
+ def setup
136
+ @resource = new_resource
137
+ @machine = StateMachine::Machine.new(@resource)
138
+ @machine.state :first_gear
139
+ end
140
+
141
+ def test_should_humanize_name
142
+ assert_equal 'first gear', @machine.state(:first_gear).human_name
143
+ end
144
+ end
145
+
146
+ class MachineWithStaticInitialStateTest < BaseTestCase
147
+ def setup
148
+ @resource = new_resource do
149
+ attr_accessor :value
150
+ end
151
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
152
+ end
153
+
154
+ def test_should_set_initial_state_on_created_object
155
+ record = @resource.new
156
+ assert_equal 'parked', record.state
157
+ end
158
+
159
+ def test_should_set_initial_state_with_nil_attributes
160
+ @resource.class_eval do
161
+ def attributes=(attributes)
162
+ super(attributes || {})
163
+ end
164
+ end
165
+
166
+ record = @resource.new(nil)
167
+ assert_equal 'parked', record.state
168
+ end
169
+
170
+ def test_should_still_set_attributes
171
+ record = @resource.new(:value => 1)
172
+ assert_equal 1, record.value
173
+ end
174
+
175
+ def test_should_not_allow_initialize_blocks
176
+ block_args = nil
177
+ record = @resource.new do |*args|
178
+ block_args = args
179
+ end
180
+
181
+ assert_nil block_args
182
+ end
183
+
184
+ def test_should_set_initial_state_before_setting_attributes
185
+ @resource.class_eval do
186
+ attr_accessor :state_during_setter
187
+
188
+ define_method(:value=) do |value|
189
+ self.state_during_setter = state
190
+ end
191
+ end
192
+
193
+ record = @resource.new(:value => 1)
194
+ assert_equal 'parked', record.state_during_setter
195
+ end
196
+
197
+ def test_should_not_set_initial_state_after_already_initialized
198
+ record = @resource.new(:value => 1)
199
+ assert_equal 'parked', record.state
200
+
201
+ record.state = 'idling'
202
+ record.attributes = {}
203
+ assert_equal 'idling', record.state
204
+ end
205
+
206
+ def test_should_use_stored_values_when_loading_from_database
207
+ @machine.state :idling
208
+
209
+ record = @resource.get(@resource.create(:state => 'idling').id)
210
+ assert_equal 'idling', record.state
211
+ end
212
+
213
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
214
+ @machine.state nil
215
+
216
+ record = @resource.get(@resource.create(:state => nil).id)
217
+ assert_nil record.state
218
+ end
219
+ end
220
+
221
+ class MachineWithDynamicInitialStateTest < BaseTestCase
222
+ def setup
223
+ @resource = new_resource do
224
+ attr_accessor :value
225
+ end
226
+ @machine = StateMachine::Machine.new(@resource, :initial => lambda {|object| :parked})
227
+ @machine.state :parked
228
+ end
229
+
230
+ def test_should_set_initial_state_on_created_object
231
+ record = @resource.new
232
+ assert_equal 'parked', record.state
233
+ end
234
+
235
+ def test_should_still_set_attributes
236
+ record = @resource.new(:value => 1)
237
+ assert_equal 1, record.value
238
+ end
239
+
240
+ def test_should_not_allow_initialize_blocks
241
+ block_args = nil
242
+ record = @resource.new do |*args|
243
+ block_args = args
244
+ end
245
+
246
+ assert_nil block_args
247
+ end
248
+
249
+ def test_should_set_initial_state_after_setting_attributes
250
+ @resource.class_eval do
251
+ attr_accessor :state_during_setter
252
+
253
+ define_method(:value=) do |value|
254
+ self.state_during_setter = state || 'nil'
255
+ end
256
+ end
257
+
258
+ record = @resource.new(:value => 1)
259
+ assert_equal 'nil', record.state_during_setter
260
+ end
261
+
262
+ def test_should_not_set_initial_state_after_already_initialized
263
+ record = @resource.new(:value => 1)
264
+ assert_equal 'parked', record.state
265
+
266
+ record.state = 'idling'
267
+ record.attributes = {}
268
+ assert_equal 'idling', record.state
269
+ end
270
+
271
+ def test_should_use_stored_values_when_loading_from_database
272
+ @machine.state :idling
273
+
274
+ record = @resource.get(@resource.create(:state => 'idling').id)
275
+ assert_equal 'idling', record.state
276
+ end
277
+
278
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
279
+ @machine.state nil
280
+
281
+ record = @resource.get(@resource.create(:state => nil).id)
282
+ assert_nil record.state
283
+ end
284
+ end
285
+
286
+ class MachineWithEventsTest < BaseTestCase
287
+ def setup
288
+ @resource = new_resource
289
+ @machine = StateMachine::Machine.new(@resource)
290
+ @machine.event :shift_up
291
+ end
292
+
293
+ def test_should_humanize_name
294
+ assert_equal 'shift up', @machine.event(:shift_up).human_name
295
+ end
296
+ end
297
+
298
+ class MachineWithColumnDefaultTest < BaseTestCase
299
+ def setup
300
+ @resource = new_resource do
301
+ property :status, String, :default => 'idling'
302
+ auto_migrate!
303
+ end
304
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
305
+ @record = @resource.new
306
+ end
307
+
308
+ def test_should_use_machine_default
309
+ assert_equal 'parked', @record.status
310
+ end
311
+ end
312
+
313
+ class MachineWithConflictingPredicateTest < BaseTestCase
314
+ def setup
315
+ @resource = new_resource do
316
+ def state?(*args)
317
+ true
318
+ end
319
+ end
320
+
321
+ @machine = StateMachine::Machine.new(@resource)
322
+ @record = @resource.new
323
+ end
324
+
325
+ def test_should_not_define_attribute_predicate
326
+ assert @record.state?
327
+ end
328
+ end
329
+
330
+ class MachineWithColumnStateAttributeTest < BaseTestCase
331
+ def setup
332
+ @resource = new_resource
333
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
334
+ @machine.other_states(:idling)
335
+
336
+ @record = @resource.new
337
+ end
338
+
339
+ def test_should_not_override_the_column_reader
340
+ @record.attribute_set(:state, 'parked')
341
+ assert_equal 'parked', @record.state
342
+ end
343
+
344
+ def test_should_not_override_the_column_writer
345
+ @record.state = 'parked'
346
+ assert_equal 'parked', @record.attribute_get(:state)
347
+ end
348
+
349
+ def test_should_have_an_attribute_predicate
350
+ assert @record.respond_to?(:state?)
351
+ end
352
+
353
+ def test_should_raise_exception_for_predicate_without_parameters
354
+ exception = assert_raise(ArgumentError) { @record.state? }
355
+ assert_equal 'wrong number of arguments (1 for 2)', exception.message
356
+ end
357
+
358
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
359
+ assert !@record.state?(:idling)
360
+ end
361
+
362
+ def test_should_return_true_for_predicate_if_matches_current_value
363
+ assert @record.state?(:parked)
364
+ end
365
+
366
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
367
+ assert_raise(IndexError) { @record.state?(:invalid) }
368
+ end
369
+ end
370
+
371
+ class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
372
+ def setup
373
+ @resource = new_resource do
374
+ def initialize
375
+ # Skip attribute initialization
376
+ @initialized_state_machines = true
377
+ super
378
+ end
379
+ end
380
+
381
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => 'parked')
382
+ @record = @resource.new
383
+ end
384
+
385
+ def test_should_define_a_new_property_for_the_attribute
386
+ assert_not_nil @resource.properties[:status]
387
+ end
388
+
389
+ def test_should_define_a_reader_attribute_for_the_attribute
390
+ assert @record.respond_to?(:status)
391
+ end
392
+
393
+ def test_should_define_a_writer_attribute_for_the_attribute
394
+ assert @record.respond_to?(:status=)
395
+ end
396
+
397
+ def test_should_define_an_attribute_predicate
398
+ assert @record.respond_to?(:status?)
399
+ end
400
+ end
401
+
402
+ class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
403
+ def setup
404
+ @resource = new_resource do
405
+ attr_accessor :status
406
+ end
407
+
408
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
409
+ @machine.other_states(:idling)
410
+ @record = @resource.new
411
+ end
412
+
413
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
414
+ assert !@record.status?(:idling)
415
+ end
416
+
417
+ def test_should_return_true_for_predicate_if_matches_current_value
418
+ assert @record.status?(:parked)
419
+ end
420
+
421
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
422
+ assert_raise(IndexError) { @record.status?(:invalid) }
423
+ end
424
+
425
+ def test_should_set_initial_state_on_created_object
426
+ assert_equal 'parked', @record.status
427
+ end
428
+ end
429
+
430
+ class MachineWithInitializedStateTest < BaseTestCase
431
+ def setup
432
+ @resource = new_resource
433
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
434
+ @machine.state :idling
435
+ end
436
+
437
+ def test_should_allow_nil_initial_state_when_static
438
+ @machine.state nil
439
+
440
+ record = @resource.new(:state => nil)
441
+ assert_nil record.state
442
+ end
443
+
444
+ def test_should_allow_nil_initial_state_when_dynamic
445
+ @machine.state nil
446
+
447
+ @machine.initial_state = lambda {:parked}
448
+ record = @resource.new(:state => nil)
449
+ assert_nil record.state
450
+ end
451
+
452
+ def test_should_allow_different_initial_state_when_static
453
+ record = @resource.new(:state => 'idling')
454
+ assert_equal 'idling', record.state
455
+ end
456
+
457
+ def test_should_allow_different_initial_state_when_dynamic
458
+ @machine.initial_state = lambda {:parked}
459
+ record = @resource.new(:state => 'idling')
460
+ assert_equal 'idling', record.state
461
+ end
462
+
463
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.9.8')
464
+ def test_should_raise_exception_if_protected
465
+ @resource.class_eval do
466
+ protected :state=
467
+ end
468
+
469
+ assert_raise(ArgumentError) { @resource.new(:state => 'idling') }
470
+ end
471
+ end
472
+ end
473
+
474
+ class MachineMultipleTest < BaseTestCase
475
+ def setup
476
+ @resource = new_resource do
477
+ property :status, String
478
+ auto_migrate!
479
+ end
480
+ @state_machine = StateMachine::Machine.new(@resource, :initial => :parked)
481
+ @status_machine = StateMachine::Machine.new(@resource, :status, :initial => :idling)
482
+ end
483
+
484
+ def test_should_should_initialize_each_state
485
+ record = @resource.new
486
+ assert_equal 'parked', record.state
487
+ assert_equal 'idling', record.status
488
+ end
489
+ end
490
+
491
+ class MachineWithLoopbackTest < BaseTestCase
492
+ def setup
493
+ @resource = new_resource do
494
+ property :updated_at, DateTime
495
+ auto_migrate!
496
+
497
+ # Simulate dm-timestamps
498
+ before :update do
499
+ return unless dirty?
500
+ self.updated_at = DateTime.now
501
+ end
502
+ end
503
+
504
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
505
+ @machine.event :park
506
+
507
+ @record = @resource.create(:updated_at => Time.now - 1)
508
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
509
+
510
+ @timestamp = @record.updated_at
511
+ @transition.perform
512
+ end
513
+
514
+ def test_should_update_record
515
+ assert_not_equal @timestamp, @record.updated_at
516
+ end
517
+ end
518
+
519
+ class MachineWithDirtyAttributesTest < BaseTestCase
520
+ def setup
521
+ @resource = new_resource
522
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
523
+ @machine.event :ignite
524
+ @machine.state :idling
525
+
526
+ @record = @resource.create
527
+
528
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
529
+ @transition.perform(false)
530
+ end
531
+
532
+ def test_should_include_state_in_changed_attributes
533
+ assert_equal e = {@resource.properties[:state] => 'idling'}, @record.dirty_attributes
534
+ end
535
+
536
+ def test_should_track_attribute_change
537
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
538
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.original_attributes
539
+ else
540
+ assert_equal e = {:state => 'parked'}, @record.original_values
541
+ end
542
+ end
543
+
544
+ def test_should_not_reset_changes_on_multiple_transitions
545
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
546
+ transition.perform(false)
547
+
548
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
549
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.original_attributes
550
+ else
551
+ assert_equal e = {:state => 'parked'}, @record.original_values
552
+ end
553
+ end
554
+
555
+ def test_should_have_changes_when_loaded_from_database
556
+ record = @resource.get(@record.id)
557
+ assert record.dirty_attributes.empty?
558
+ end
559
+ end
560
+
561
+ class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
562
+ def setup
563
+ @resource = new_resource
564
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
565
+ @machine.event :park
566
+
567
+ @record = @resource.create
568
+
569
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
570
+ @transition.perform(false)
571
+ end
572
+
573
+ def test_should_include_state_in_changed_attributes
574
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.dirty_attributes
575
+ end
576
+
577
+ def test_should_track_attribute_change
578
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.3')
579
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.original_attributes
580
+ elsif Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
581
+ assert_equal e = {@resource.properties[:state] => 'parked-ignored'}, @record.original_attributes
582
+ else
583
+ assert_equal e = {:state => 'parked-ignored'}, @record.original_values
584
+ end
585
+ end
586
+ end
587
+
588
+ class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
589
+ def setup
590
+ @resource = new_resource do
591
+ property :status, String, :default => 'idling'
592
+ auto_migrate!
593
+ end
594
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
595
+ @machine.event :ignite
596
+ @machine.state :idling
597
+
598
+ @record = @resource.create
599
+
600
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
601
+ @transition.perform(false)
602
+ end
603
+
604
+ def test_should_include_state_in_changed_attributes
605
+ assert_equal e = {@resource.properties[:status] => 'idling'}, @record.dirty_attributes
606
+ end
607
+
608
+ def test_should_track_attribute_change
609
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
610
+ assert_equal e = {@resource.properties[:status] => 'parked'}, @record.original_attributes
611
+ else
612
+ assert_equal e = {:status => 'parked'}, @record.original_values
613
+ end
614
+ end
615
+
616
+ def test_should_not_reset_changes_on_multiple_transitions
617
+ transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
618
+ transition.perform(false)
619
+
620
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
621
+ assert_equal e = {@resource.properties[:status] => 'parked'}, @record.original_attributes
622
+ else
623
+ assert_equal e = {:status => 'parked'}, @record.original_values
624
+ end
625
+ end
626
+ end
627
+
628
+ class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
629
+ def setup
630
+ @resource = new_resource do
631
+ property :status, String, :default => 'idling'
632
+ auto_migrate!
633
+ end
634
+ @machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
635
+ @machine.event :park
636
+
637
+ @record = @resource.create
638
+
639
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
640
+ @transition.perform(false)
641
+ end
642
+
643
+ def test_should_include_state_in_changed_attributes
644
+ assert_equal e = {@resource.properties[:status] => 'parked'}, @record.dirty_attributes
645
+ end
646
+
647
+ def test_should_track_attribute_changes
648
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.3')
649
+ assert_equal e = {@resource.properties[:status] => 'parked'}, @record.original_attributes
650
+ elsif Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
651
+ assert_equal e = {@resource.properties[:status] => 'parked-ignored'}, @record.original_attributes
652
+ else
653
+ assert_equal e = {:status => 'parked-ignored'}, @record.original_values
654
+ end
655
+ end
656
+ end
657
+
658
+ class MachineWithDirtyAttributeAndStateEventsTest < BaseTestCase
659
+ def setup
660
+ @resource = new_resource
661
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
662
+ @machine.event :ignite
663
+
664
+ @record = @resource.create
665
+ @record.state_event = 'ignite'
666
+ end
667
+
668
+ def test_should_include_state_in_changed_attributes
669
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.dirty_attributes
670
+ end
671
+
672
+ def test_should_track_attribute_change
673
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.3')
674
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.original_attributes
675
+ elsif Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
676
+ assert_equal e = {@resource.properties[:state] => 'parked-ignored'}, @record.original_attributes
677
+ else
678
+ assert_equal e = {:state => 'parked-ignored'}, @record.original_values
679
+ end
680
+ end
681
+
682
+ def test_should_not_reset_changes_on_multiple_changes
683
+ @record.state_event = 'ignite'
684
+
685
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.3')
686
+ assert_equal e = {@resource.properties[:state] => 'parked'}, @record.original_attributes
687
+ elsif Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
688
+ assert_equal e = {@resource.properties[:state] => 'parked-ignored'}, @record.original_attributes
689
+ else
690
+ assert_equal e = {:state => 'parked-ignored'}, @record.original_values
691
+ end
692
+ end
693
+
694
+ def test_should_not_include_state_in_changed_attributes_if_nil
695
+ @record = @resource.create
696
+ @record.state_event = nil
697
+
698
+ assert_equal e = {}, @record.dirty_attributes
699
+ end
700
+ end
701
+
702
+ class MachineWithoutTransactionsTest < BaseTestCase
703
+ def setup
704
+ @resource = new_resource
705
+ @machine = StateMachine::Machine.new(@resource, :use_transactions => false)
706
+ end
707
+
708
+ def test_should_not_rollback_transaction_if_false
709
+ @machine.within_transaction(@resource.new) do
710
+ @resource.create
711
+ false
712
+ end
713
+
714
+ assert_equal 1, @resource.all.size
715
+ end
716
+
717
+ def test_should_not_rollback_transaction_if_true
718
+ @machine.within_transaction(@resource.new) do
719
+ @resource.create
720
+ true
721
+ end
722
+
723
+ assert_equal 1, @resource.all.size
724
+ end
725
+ end
726
+
727
+ begin
728
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.3')
729
+ gem 'dm-transactions', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=0.10.3'
730
+ require 'dm-transactions'
731
+ end
732
+
733
+ class MachineWithTransactionsTest < BaseTestCase
734
+ def setup
735
+ @resource = new_resource
736
+ @machine = StateMachine::Machine.new(@resource, :use_transactions => true)
737
+ end
738
+
739
+ def test_should_rollback_transaction_if_false
740
+ @machine.within_transaction(@resource.new) do
741
+ @resource.create
742
+ false
743
+ end
744
+
745
+ assert_equal 0, @resource.all.size
746
+ end
747
+
748
+ def test_should_not_rollback_transaction_if_true
749
+ @machine.within_transaction(@resource.new) do
750
+ @resource.create
751
+ true
752
+ end
753
+
754
+ assert_equal 1, @resource.all.size
755
+ end
756
+ end
757
+ rescue LoadError
758
+ $stderr.puts "Skipping DataMapper Transaction tests. `gem install dm-transactions#{" -v #{ENV['VERSION']}" if ENV['VERSION']}` and try again."
759
+ end
760
+
761
+ class MachineWithCallbacksTest < BaseTestCase
762
+ def setup
763
+ @resource = new_resource
764
+ @machine = StateMachine::Machine.new(@resource)
765
+ @machine.state :parked, :idling
766
+ @machine.event :ignite
767
+
768
+ @record = @resource.new(:state => 'parked')
769
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
770
+ end
771
+
772
+ def test_should_run_before_callbacks
773
+ called = false
774
+ @machine.before_transition {called = true}
775
+
776
+ @transition.perform
777
+ assert called
778
+ end
779
+
780
+ def test_should_pass_transition_to_before_callbacks_with_one_argument
781
+ transition = nil
782
+ @machine.before_transition {|arg| transition = arg}
783
+
784
+ @transition.perform
785
+ assert_equal @transition, transition
786
+ end
787
+
788
+ def test_should_pass_transition_to_before_callbacks_with_multiple_arguments
789
+ callback_args = nil
790
+ @machine.before_transition {|*args| callback_args = args}
791
+
792
+ @transition.perform
793
+ assert_equal [@transition], callback_args
794
+ end
795
+
796
+ def test_should_run_before_callbacks_within_the_context_of_the_record
797
+ context = nil
798
+ @machine.before_transition {context = self}
799
+
800
+ @transition.perform
801
+ assert_equal @record, context
802
+ end
803
+
804
+ def test_should_run_after_callbacks
805
+ called = false
806
+ @machine.after_transition {called = true}
807
+
808
+ @transition.perform
809
+ assert called
810
+ end
811
+
812
+ def test_should_pass_transition_to_after_callbacks_with_multiple_arguments
813
+ callback_args = nil
814
+ @machine.after_transition {|*args| callback_args = args}
815
+
816
+ @transition.perform
817
+ assert_equal [@transition], callback_args
818
+ end
819
+
820
+ def test_should_run_after_callbacks_with_the_context_of_the_record
821
+ context = nil
822
+ @machine.after_transition {context = self}
823
+
824
+ @transition.perform
825
+ assert_equal @record, context
826
+ end
827
+
828
+ def test_should_run_around_callbacks
829
+ before_called = false
830
+ after_called = [false]
831
+ @machine.around_transition {|block| before_called = true; block.call; after_called[0] = true}
832
+
833
+ @transition.perform
834
+ assert before_called
835
+ assert after_called[0]
836
+ end
837
+
838
+ def test_should_run_around_callbacks_with_the_context_of_the_record
839
+ context = nil
840
+ @machine.around_transition {|block| context = self; block.call}
841
+
842
+ @transition.perform
843
+ assert_equal @record, context
844
+ end
845
+
846
+ def test_should_allow_symbolic_callbacks
847
+ callback_args = nil
848
+
849
+ klass = class << @record; self; end
850
+ klass.send(:define_method, :after_ignite) do |*args|
851
+ callback_args = args
852
+ end
853
+
854
+ @machine.before_transition(:after_ignite)
855
+
856
+ @transition.perform
857
+ assert_equal [@transition], callback_args
858
+ end
859
+
860
+ def test_should_allow_string_callbacks
861
+ class << @record
862
+ attr_reader :callback_result
863
+ end
864
+
865
+ @machine.before_transition('@callback_result = [1, 2, 3]')
866
+ @transition.perform
867
+
868
+ assert_equal [1, 2, 3], @record.callback_result
869
+ end
870
+ end
871
+
872
+ class MachineWithFailedBeforeCallbacksTest < BaseTestCase
873
+ def setup
874
+ callbacks = []
875
+
876
+ @resource = new_resource
877
+ @machine = StateMachine::Machine.new(@resource)
878
+ @machine.state :parked, :idling
879
+ @machine.event :ignite
880
+ @machine.before_transition {callbacks << :before_1; throw :halt}
881
+ @machine.before_transition {callbacks << :before_2}
882
+ @machine.after_transition {callbacks << :after}
883
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
884
+
885
+ @record = @resource.new(:state => 'parked')
886
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
887
+ @result = @transition.perform
888
+
889
+ @callbacks = callbacks
890
+ end
891
+
892
+ def test_should_not_be_successful
893
+ assert !@result
894
+ end
895
+
896
+ def test_should_not_change_current_state
897
+ assert_equal 'parked', @record.state
898
+ end
899
+
900
+ def test_should_not_run_action
901
+ assert @record.respond_to?(:new?) ? @record.new? : @record.new_record?
902
+ end
903
+
904
+ def test_should_not_run_further_callbacks
905
+ assert_equal [:before_1], @callbacks
906
+ end
907
+ end
908
+
909
+ class MachineWithFailedActionTest < BaseTestCase
910
+ def setup
911
+ @resource = new_resource do
912
+ before(:create) { throw :halt }
913
+ end
914
+
915
+ @machine = StateMachine::Machine.new(@resource)
916
+ @machine.state :parked, :idling
917
+ @machine.event :ignite
918
+
919
+ callbacks = []
920
+ @machine.before_transition {callbacks << :before}
921
+ @machine.after_transition {callbacks << :after}
922
+ @machine.after_failure {callbacks << :after_failure}
923
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
924
+
925
+ @record = @resource.new(:state => 'parked')
926
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
927
+ @result = @transition.perform
928
+
929
+ @callbacks = callbacks
930
+ end
931
+
932
+ def test_should_not_be_successful
933
+ assert !@result
934
+ end
935
+
936
+ def test_should_not_change_current_state
937
+ assert_equal 'parked', @record.state
938
+ end
939
+
940
+ def test_should_not_save_record
941
+ assert @record.respond_to?(:new?) ? @record.new? : @record.new_record?
942
+ end
943
+
944
+ def test_should_run_before_callbacks_and_after_callbacks_with_failures
945
+ assert_equal [:before, :around_before, :after_failure], @callbacks
946
+ end
947
+ end
948
+
949
+ class MachineWithFailedAfterCallbacksTest < BaseTestCase
950
+ def setup
951
+ callbacks = []
952
+
953
+ @resource = new_resource
954
+ @machine = StateMachine::Machine.new(@resource)
955
+ @machine.state :parked, :idling
956
+ @machine.event :ignite
957
+ @machine.after_transition {callbacks << :after_1; throw :halt}
958
+ @machine.after_transition {callbacks << :after_2}
959
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
960
+
961
+ @record = @resource.new(:state => 'parked')
962
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
963
+ @result = @transition.perform
964
+
965
+ @callbacks = callbacks
966
+ end
967
+
968
+ def test_should_be_successful
969
+ assert @result
970
+ end
971
+
972
+ def test_should_change_current_state
973
+ assert_equal 'idling', @record.state
974
+ end
975
+
976
+ def test_should_save_record
977
+ assert !(@record.respond_to?(:new?) ? @record.new? : @record.new_record?)
978
+ end
979
+
980
+ def test_should_not_run_further_after_callbacks
981
+ assert_equal [:around_before, :around_after, :after_1], @callbacks
982
+ end
983
+ end
984
+
985
+ begin
986
+ gem 'dm-validations', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=0.9.4'
987
+ require 'dm-validations'
988
+
989
+ class MachineWithValidationsTest < BaseTestCase
990
+ def setup
991
+ @resource = new_resource
992
+ @machine = StateMachine::Machine.new(@resource)
993
+ @machine.state :parked
994
+
995
+ @record = @resource.new
996
+ end
997
+
998
+ def test_should_invalidate_using_errors
999
+ @record.state = 'parked'
1000
+
1001
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
1002
+ assert_equal ['cannot transition via "park"'], @record.errors.on(:state)
1003
+ end
1004
+
1005
+ def test_should_auto_prefix_custom_attributes_on_invalidation
1006
+ @machine.invalidate(@record, :event, :invalid)
1007
+
1008
+ assert_equal ['is invalid'], @record.errors.on(:state_event)
1009
+ end
1010
+
1011
+ def test_should_clear_errors_on_reset
1012
+ @record.state = 'parked'
1013
+ @record.errors.add(:state, 'is invalid')
1014
+
1015
+ @machine.reset(@record)
1016
+ assert_nil @record.errors.on(:id)
1017
+ end
1018
+
1019
+ def test_should_be_valid_if_state_is_known
1020
+ @record.state = 'parked'
1021
+
1022
+ assert @record.valid?
1023
+ end
1024
+
1025
+ def test_should_not_be_valid_if_state_is_unknown
1026
+ @record.state = 'invalid'
1027
+
1028
+ assert !@record.valid?
1029
+ assert_equal ['is invalid'], @record.errors.on(:state)
1030
+ end
1031
+ end
1032
+
1033
+ class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
1034
+ def setup
1035
+ @resource = new_resource
1036
+ @machine = StateMachine::Machine.new(@resource, :status, :attribute => :state)
1037
+ @machine.state :parked
1038
+
1039
+ @record = @resource.new
1040
+ end
1041
+
1042
+ def test_should_add_validation_errors_to_custom_attribute
1043
+ @record.state = 'invalid'
1044
+
1045
+ assert !@record.valid?
1046
+ assert_equal ['is invalid'], @record.errors.on(:state)
1047
+
1048
+ @record.state = 'parked'
1049
+ assert @record.valid?
1050
+ end
1051
+ end
1052
+
1053
+ class MachineWithStateDrivenValidationsTest < BaseTestCase
1054
+ def setup
1055
+ @resource = resource = new_resource do
1056
+ attr_accessor :seatbelt
1057
+ end
1058
+
1059
+ @machine = StateMachine::Machine.new(@resource)
1060
+ @machine.state :first_gear, :second_gear do
1061
+ if resource.respond_to?(:validates_presence_of)
1062
+ validates_presence_of :seatbelt
1063
+ else
1064
+ validates_present :seatbelt
1065
+ end
1066
+ end
1067
+ @machine.other_states :parked
1068
+ end
1069
+
1070
+ def test_should_be_valid_if_validation_fails_outside_state_scope
1071
+ record = @resource.new(:state => 'parked', :seatbelt => nil)
1072
+ assert record.valid?
1073
+ end
1074
+
1075
+ def test_should_be_invalid_if_validation_fails_within_state_scope
1076
+ record = @resource.new(:state => 'first_gear', :seatbelt => nil)
1077
+ assert !record.valid?
1078
+ end
1079
+
1080
+ def test_should_be_valid_if_validation_succeeds_within_state_scope
1081
+ record = @resource.new(:state => 'second_gear', :seatbelt => true)
1082
+ assert record.valid?
1083
+ end
1084
+ end
1085
+
1086
+ # See README caveats
1087
+ if Gem::Version.new(::DataMapper::VERSION) > Gem::Version.new('0.9.6')
1088
+ class MachineWithEventAttributesOnValidationTest < BaseTestCase
1089
+ def setup
1090
+ @resource = new_resource
1091
+ @machine = StateMachine::Machine.new(@resource)
1092
+ @machine.event :ignite do
1093
+ transition :parked => :idling
1094
+ end
1095
+
1096
+ @record = @resource.new
1097
+ @record.state = 'parked'
1098
+ @record.state_event = 'ignite'
1099
+ end
1100
+
1101
+ def test_should_fail_if_event_is_invalid
1102
+ @record.state_event = 'invalid'
1103
+ assert !@record.valid?
1104
+ assert_equal ['is invalid'], @record.errors.full_messages
1105
+ end
1106
+
1107
+ def test_should_fail_if_event_has_no_transition
1108
+ @record.state = 'idling'
1109
+ assert !@record.valid?
1110
+ assert_equal ['cannot transition when idling'], @record.errors.full_messages
1111
+ end
1112
+
1113
+ def test_should_be_successful_if_event_has_transition
1114
+ assert @record.valid?
1115
+ end
1116
+
1117
+ def test_should_run_before_callbacks
1118
+ ran_callback = false
1119
+ @machine.before_transition { ran_callback = true }
1120
+
1121
+ @record.valid?
1122
+ assert ran_callback
1123
+ end
1124
+
1125
+ def test_should_run_around_callbacks_before_yield
1126
+ ran_callback = false
1127
+ @machine.around_transition {|block| ran_callback = true; block.call }
1128
+
1129
+ @record.valid?
1130
+ assert ran_callback
1131
+ end
1132
+
1133
+ def test_should_persist_new_state
1134
+ @record.valid?
1135
+ assert_equal 'idling', @record.state
1136
+ end
1137
+
1138
+ def test_should_not_run_after_callbacks
1139
+ ran_callback = false
1140
+ @machine.after_transition { ran_callback = true }
1141
+
1142
+ @record.valid?
1143
+ assert !ran_callback
1144
+ end
1145
+
1146
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
1147
+ @resource.class_eval do
1148
+ attr_accessor :seatbelt
1149
+ if respond_to?(:validates_presence_of)
1150
+ validates_presence_of :seatbelt
1151
+ else
1152
+ validates_present :seatbelt
1153
+ end
1154
+ end
1155
+
1156
+ ran_callback = false
1157
+ @machine.after_transition { ran_callback = true }
1158
+
1159
+ @record.valid?
1160
+ assert !ran_callback
1161
+ end
1162
+
1163
+ def test_should_run_failure_callbacks_if_validation_fails
1164
+ @resource.class_eval do
1165
+ attr_accessor :seatbelt
1166
+ if respond_to?(:validates_presence_of)
1167
+ validates_presence_of :seatbelt
1168
+ else
1169
+ validates_present :seatbelt
1170
+ end
1171
+ end
1172
+
1173
+ ran_callback = false
1174
+ @machine.after_failure { ran_callback = true }
1175
+
1176
+ @record.valid?
1177
+ assert ran_callback
1178
+ end
1179
+
1180
+ def test_should_not_run_around_callbacks_after_yield
1181
+ ran_callback = [false]
1182
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1183
+
1184
+ @record.valid?
1185
+ assert !ran_callback[0]
1186
+ end
1187
+
1188
+ def test_should_not_run_around_callbacks_after_yield_with_failures_disabled_if_validation_fails
1189
+ @resource.class_eval do
1190
+ attr_accessor :seatbelt
1191
+ if respond_to?(:validates_presence_of)
1192
+ validates_presence_of :seatbelt
1193
+ else
1194
+ validates_present :seatbelt
1195
+ end
1196
+ end
1197
+
1198
+ ran_callback = [false]
1199
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1200
+
1201
+ @record.valid?
1202
+ assert !ran_callback[0]
1203
+ end
1204
+
1205
+ def test_should_not_run_before_transitions_within_transaction
1206
+ @machine.before_transition { self.class.create; throw :halt }
1207
+
1208
+ assert !@record.valid?
1209
+ assert_equal 1, @resource.all.size
1210
+ end
1211
+ end
1212
+
1213
+ class MachineWithEventAttributesOnSaveTest < BaseTestCase
1214
+ def setup
1215
+ @resource = new_resource
1216
+ @machine = StateMachine::Machine.new(@resource)
1217
+ @machine.event :ignite do
1218
+ transition :parked => :idling
1219
+ end
1220
+
1221
+ @record = @resource.new
1222
+ @record.state = 'parked'
1223
+ @record.state_event = 'ignite'
1224
+ end
1225
+
1226
+ def test_should_fail_if_event_is_invalid
1227
+ @record.state_event = 'invalid'
1228
+ assert !@record.save
1229
+ end
1230
+
1231
+ def test_should_fail_if_event_has_no_transition
1232
+ @record.state = 'idling'
1233
+ assert !@record.save
1234
+ end
1235
+
1236
+ def test_should_be_successful_if_event_has_transition
1237
+ assert_equal true, @record.save
1238
+ end
1239
+
1240
+ def test_should_run_before_callbacks
1241
+ ran_callback = false
1242
+ @machine.before_transition { ran_callback = true }
1243
+
1244
+ @record.save
1245
+ assert ran_callback
1246
+ end
1247
+
1248
+ def test_should_run_before_callbacks_once
1249
+ before_count = 0
1250
+ @machine.before_transition { before_count += 1 }
1251
+
1252
+ @record.save
1253
+ assert_equal 1, before_count
1254
+ end
1255
+
1256
+ def test_should_run_around_callbacks_before_yield
1257
+ ran_callback = false
1258
+ @machine.around_transition {|block| ran_callback = true; block.call }
1259
+
1260
+ @record.save
1261
+ assert ran_callback
1262
+ end
1263
+
1264
+ def test_should_run_around_callbacks_before_yield_once
1265
+ around_before_count = 0
1266
+ @machine.around_transition {|block| around_before_count += 1; block.call }
1267
+
1268
+ @record.save
1269
+ assert_equal 1, around_before_count
1270
+ end
1271
+
1272
+ def test_should_persist_new_state
1273
+ @record.save
1274
+ assert_equal 'idling', @record.state
1275
+ end
1276
+
1277
+ def test_should_run_after_callbacks
1278
+ ran_callback = false
1279
+ @machine.after_transition { ran_callback = true }
1280
+
1281
+ @record.save
1282
+ assert ran_callback
1283
+ end
1284
+
1285
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
1286
+ @resource.before(:create) { throw :halt }
1287
+
1288
+ ran_callback = false
1289
+ @machine.after_transition { ran_callback = true }
1290
+
1291
+ @record.save
1292
+ assert !ran_callback
1293
+ end
1294
+
1295
+ def test_should_run_failure_callbacks_if_fails
1296
+ @resource.before(:create) { throw :halt }
1297
+
1298
+ ran_callback = false
1299
+ @machine.after_failure { ran_callback = true }
1300
+
1301
+ @record.save
1302
+ assert ran_callback
1303
+ end
1304
+
1305
+ def test_should_not_run_around_callbacks_with_failures_disabled_if_fails
1306
+ @resource.before(:create) { throw :halt }
1307
+
1308
+ ran_callback = [false]
1309
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1310
+
1311
+ @record.save
1312
+ assert !ran_callback[0]
1313
+ end
1314
+
1315
+ def test_should_run_around_callbacks_after_yield
1316
+ ran_callback = [false]
1317
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1318
+
1319
+ @record.save
1320
+ assert ran_callback[0]
1321
+ end
1322
+
1323
+ def test_should_not_run_before_transitions_within_transaction
1324
+ @machine.before_transition { self.class.create; throw :halt }
1325
+
1326
+ assert_equal false, @record.save
1327
+ assert_equal 1, @resource.all.size
1328
+ end
1329
+
1330
+ def test_should_not_run_after_transitions_within_transaction
1331
+ @machine.before_transition { self.class.create; throw :halt }
1332
+
1333
+ assert_equal false, @record.save
1334
+ assert_equal 1, @resource.all.size
1335
+ end
1336
+
1337
+ def test_should_not_run_around_transition_within_transaction
1338
+ @machine.around_transition { self.class.create; throw :halt }
1339
+
1340
+ assert_equal false, @record.save
1341
+ assert_equal 1, @resource.all.size
1342
+ end
1343
+ end
1344
+ end
1345
+
1346
+ if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
1347
+ class MachineWithEventAttributesOnSaveBangTest < BaseTestCase
1348
+ def setup
1349
+ @resource = new_resource
1350
+ @machine = StateMachine::Machine.new(@resource)
1351
+ @machine.event :ignite do
1352
+ transition :parked => :idling
1353
+ end
1354
+
1355
+ @record = @resource.new
1356
+ @record.state = 'parked'
1357
+ @record.state_event = 'ignite'
1358
+ end
1359
+
1360
+ def test_should_fail_if_event_is_invalid
1361
+ @record.state_event = 'invalid'
1362
+ assert !@record.save!
1363
+ end
1364
+
1365
+ def test_should_fail_if_event_has_no_transition
1366
+ @record.state = 'idling'
1367
+ assert !@record.save!
1368
+ end
1369
+
1370
+ def test_should_be_successful_if_event_has_transition
1371
+ assert_equal true, @record.save!
1372
+ end
1373
+
1374
+ def test_should_run_before_callbacks
1375
+ ran_callback = false
1376
+ @machine.before_transition { ran_callback = true }
1377
+
1378
+ @record.save!
1379
+ assert ran_callback
1380
+ end
1381
+
1382
+ def test_should_run_before_callbacks_once
1383
+ before_count = 0
1384
+ @machine.before_transition { before_count += 1 }
1385
+
1386
+ @record.save!
1387
+ assert_equal 1, before_count
1388
+ end
1389
+
1390
+ def test_should_run_around_callbacks_before_yield
1391
+ ran_callback = false
1392
+ @machine.around_transition {|block| ran_callback = true; block.call }
1393
+
1394
+ @record.save!
1395
+ assert ran_callback
1396
+ end
1397
+
1398
+ def test_should_run_around_callbacks_before_yield_once
1399
+ around_before_count = 0
1400
+ @machine.around_transition {|block| around_before_count += 1; block.call }
1401
+
1402
+ @record.save!
1403
+ assert_equal 1, around_before_count
1404
+ end
1405
+
1406
+ def test_should_persist_new_state
1407
+ @record.save!
1408
+ assert_equal 'idling', @record.state
1409
+ end
1410
+
1411
+ def test_should_run_after_callbacks
1412
+ ran_callback = false
1413
+ @machine.after_transition { ran_callback = true }
1414
+
1415
+ @record.save!
1416
+ assert ran_callback
1417
+ end
1418
+
1419
+ def test_should_run_around_callbacks_after_yield
1420
+ ran_callback = [false]
1421
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1422
+
1423
+ @record.save!
1424
+ assert ran_callback[0]
1425
+ end
1426
+ end
1427
+ end
1428
+
1429
+ if Gem::Version.new(::DataMapper::VERSION) > Gem::Version.new('0.9.6')
1430
+ class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
1431
+ def setup
1432
+ @superclass = new_resource do
1433
+ def persist
1434
+ save
1435
+ end
1436
+ end
1437
+ @resource = Class.new(@superclass)
1438
+ @machine = StateMachine::Machine.new(@resource, :action => :persist)
1439
+ @machine.event :ignite do
1440
+ transition :parked => :idling
1441
+ end
1442
+
1443
+ @record = @resource.new
1444
+ @record.state = 'parked'
1445
+ @record.state_event = 'ignite'
1446
+ end
1447
+
1448
+ def test_should_not_transition_on_valid?
1449
+ @record.valid?
1450
+ assert_equal 'parked', @record.state
1451
+ end
1452
+
1453
+ def test_should_not_transition_on_save
1454
+ @record.save
1455
+ assert_equal 'parked', @record.state
1456
+ end
1457
+
1458
+ def test_should_transition_on_custom_action
1459
+ @record.persist
1460
+ assert_equal 'idling', @record.state
1461
+ end
1462
+ end
1463
+ end
1464
+ rescue LoadError
1465
+ $stderr.puts "Skipping DataMapper Validation tests. `gem install dm-validations#{" -v #{ENV['VERSION']}" if ENV['VERSION']}` and try again."
1466
+ end
1467
+
1468
+ begin
1469
+ gem 'dm-observer', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=0.9.4'
1470
+ require 'dm-observer'
1471
+
1472
+ class MachineWithObserversTest < BaseTestCase
1473
+ def setup
1474
+ @resource = new_resource
1475
+ @machine = StateMachine::Machine.new(@resource)
1476
+ @machine.state :parked, :idling
1477
+ @machine.event :ignite
1478
+ @record = @resource.new(:state => 'parked')
1479
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1480
+ end
1481
+
1482
+ def test_should_provide_matcher_helpers
1483
+ matchers = []
1484
+
1485
+ new_observer(@resource) do
1486
+ matchers = [all, any, same]
1487
+ end
1488
+
1489
+ assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
1490
+ end
1491
+
1492
+ def test_should_call_before_transition_callback_if_requirements_match
1493
+ called = false
1494
+
1495
+ observer = new_observer(@resource) do
1496
+ before_transition :from => :parked do
1497
+ called = true
1498
+ end
1499
+ end
1500
+
1501
+ @transition.perform
1502
+ assert called
1503
+ end
1504
+
1505
+ def test_should_not_call_before_transition_callback_if_requirements_do_not_match
1506
+ called = false
1507
+
1508
+ observer = new_observer(@resource) do
1509
+ before_transition :from => :idling do
1510
+ called = true
1511
+ end
1512
+ end
1513
+
1514
+ @transition.perform
1515
+ assert !called
1516
+ end
1517
+
1518
+ def test_should_pass_transition_to_before_callbacks
1519
+ callback_args = nil
1520
+
1521
+ observer = new_observer(@resource) do
1522
+ before_transition do |*args|
1523
+ callback_args = args
1524
+ end
1525
+ end
1526
+
1527
+ @transition.perform
1528
+ assert_equal [@transition], callback_args
1529
+ end
1530
+
1531
+ def test_should_call_after_transition_callback_if_requirements_match
1532
+ called = false
1533
+
1534
+ observer = new_observer(@resource) do
1535
+ after_transition :from => :parked do
1536
+ called = true
1537
+ end
1538
+ end
1539
+
1540
+ @transition.perform
1541
+ assert called
1542
+ end
1543
+
1544
+ def test_should_not_call_after_transition_callback_if_requirements_do_not_match
1545
+ called = false
1546
+
1547
+ observer = new_observer(@resource) do
1548
+ after_transition :from => :idling do
1549
+ called = true
1550
+ end
1551
+ end
1552
+
1553
+ @transition.perform
1554
+ assert !called
1555
+ end
1556
+
1557
+ def test_should_pass_transition_to_after_callbacks
1558
+ callback_args = nil
1559
+
1560
+ observer = new_observer(@resource) do
1561
+ after_transition do |*args|
1562
+ callback_args = args
1563
+ end
1564
+ end
1565
+
1566
+ @transition.perform
1567
+ assert_equal [@transition], callback_args
1568
+ end
1569
+
1570
+ def test_should_call_around_transition_callback_if_requirements_match
1571
+ called = false
1572
+
1573
+ observer = new_observer(@resource) do
1574
+ around_transition :from => :parked do |block|
1575
+ called = true
1576
+ block.call
1577
+ end
1578
+ end
1579
+
1580
+ @transition.perform
1581
+ assert called
1582
+ end
1583
+
1584
+ def test_should_not_call_after_transition_callback_if_requirements_do_not_match
1585
+ called = false
1586
+
1587
+ observer = new_observer(@resource) do
1588
+ around_transition :from => :idling do |block|
1589
+ called = true
1590
+ block.call
1591
+ end
1592
+ end
1593
+
1594
+ @transition.perform
1595
+ assert !called
1596
+ end
1597
+
1598
+ def test_should_pass_transition_to_around_callbacks
1599
+ callback_args = nil
1600
+
1601
+ observer = new_observer(@resource) do
1602
+ around_transition do |*args|
1603
+ block = args.pop
1604
+ callback_args = args
1605
+ block.call
1606
+ end
1607
+ end
1608
+
1609
+ @transition.perform
1610
+ assert_equal [@transition], callback_args
1611
+ end
1612
+
1613
+ def test_should_call_failure_callback_if_requirements_match
1614
+ @resource.before(:create) { throw :halt }
1615
+
1616
+ called = false
1617
+
1618
+ observer = new_observer(@resource) do
1619
+ after_transition_failure :on => :ignite do
1620
+ called = true
1621
+ end
1622
+ end
1623
+
1624
+ @transition.perform
1625
+ assert called
1626
+ end
1627
+
1628
+ def test_should_not_call_failure_callback_if_requirements_do_not_match
1629
+ @resource.before(:create) { throw :halt }
1630
+
1631
+ called = false
1632
+
1633
+ observer = new_observer(@resource) do
1634
+ after_transition_failure :on => :park do
1635
+ called = true
1636
+ end
1637
+ end
1638
+
1639
+ @transition.perform
1640
+ assert !called
1641
+ end
1642
+
1643
+ def test_should_pass_transition_to_failure_callbacks
1644
+ @resource.before(:create) { throw :halt }
1645
+
1646
+ callback_args = nil
1647
+
1648
+ observer = new_observer(@resource) do
1649
+ after_transition_failure do |*args|
1650
+ callback_args = args
1651
+ end
1652
+ end
1653
+
1654
+ @transition.perform
1655
+ assert_equal [@transition], callback_args
1656
+ end
1657
+
1658
+ def test_should_raise_exception_if_targeting_invalid_machine
1659
+ assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) do
1660
+ new_observer(@resource) do
1661
+ before_transition :invalid, :from => :parked do
1662
+ end
1663
+ end
1664
+ end
1665
+ end
1666
+
1667
+ def test_should_allow_targeting_specific_machine
1668
+ @second_machine = StateMachine::Machine.new(@resource, :status, :namespace => 'alarm')
1669
+ @resource.auto_migrate!
1670
+
1671
+ called_state = false
1672
+ called_status = false
1673
+
1674
+ observer = new_observer(@resource) do
1675
+ before_transition :state, :from => :parked do
1676
+ called_state = true
1677
+ end
1678
+
1679
+ before_transition :status, :from => :parked do
1680
+ called_status = true
1681
+ end
1682
+ end
1683
+
1684
+ @transition.perform
1685
+
1686
+ assert called_state
1687
+ assert !called_status
1688
+ end
1689
+
1690
+ def test_should_allow_targeting_multiple_specific_machines
1691
+ @second_machine = StateMachine::Machine.new(@resource, :status, :namespace => 'alarm')
1692
+ @second_machine.state :parked, :idling
1693
+ @second_machine.event :ignite
1694
+ @resource.auto_migrate!
1695
+
1696
+ called_attribute = nil
1697
+
1698
+ attributes = []
1699
+ observer = new_observer(@resource) do
1700
+ before_transition :state, :status, :from => :parked do |transition|
1701
+ called_attribute = transition.attribute
1702
+ end
1703
+ end
1704
+
1705
+ @transition.perform
1706
+ assert_equal :state, called_attribute
1707
+
1708
+ StateMachine::Transition.new(@record, @second_machine, :ignite, :parked, :idling).perform
1709
+ assert_equal :status, called_attribute
1710
+ end
1711
+ end
1712
+
1713
+ class MachineWithMixedCallbacksTest < BaseTestCase
1714
+ def setup
1715
+ @resource = new_resource
1716
+ @machine = StateMachine::Machine.new(@resource)
1717
+ @machine.state :parked, :idling
1718
+ @machine.event :ignite
1719
+ @record = @resource.new(:state => 'parked')
1720
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1721
+
1722
+ @notifications = notifications = []
1723
+
1724
+ # Create callbacks
1725
+ @machine.before_transition {notifications << :callback_before_transition}
1726
+ @machine.after_transition {notifications << :callback_after_transition}
1727
+ @machine.around_transition do |block|
1728
+ notifications << :callback_around_before_transition
1729
+ block.call
1730
+ notifications << :callback_around_after_transition
1731
+ end
1732
+
1733
+ observer = new_observer(@resource) do
1734
+ before_transition do
1735
+ notifications << :observer_before_transition
1736
+ end
1737
+
1738
+ after_transition do
1739
+ notifications << :observer_after_transition
1740
+ end
1741
+
1742
+ around_transition do |block|
1743
+ notifications << :observer_around_before_transition
1744
+ block.call
1745
+ notifications << :observer_around_after_transition
1746
+ end
1747
+ end
1748
+
1749
+ @transition.perform
1750
+ end
1751
+
1752
+ def test_should_invoke_callbacks_in_specific_order
1753
+ expected = [
1754
+ :callback_before_transition,
1755
+ :callback_around_before_transition,
1756
+ :observer_before_transition,
1757
+ :observer_around_before_transition,
1758
+ :observer_around_after_transition,
1759
+ :callback_around_after_transition,
1760
+ :callback_after_transition,
1761
+ :observer_after_transition
1762
+ ]
1763
+
1764
+ assert_equal expected, @notifications
1765
+ end
1766
+ end
1767
+ rescue LoadError
1768
+ $stderr.puts "Skipping DataMapper Observer tests. `gem install dm-observer#{" -v #{ENV['VERSION']}" if ENV['VERSION']}` and try again."
1769
+ end
1770
+
1771
+ class MachineWithScopesTest < BaseTestCase
1772
+ def setup
1773
+ @resource = new_resource
1774
+ @machine = StateMachine::Machine.new(@resource)
1775
+ @machine.state :parked, :first_gear
1776
+ @machine.state :idling, :value => lambda {'idling'}
1777
+ end
1778
+
1779
+ def test_should_create_singular_with_scope
1780
+ assert @resource.respond_to?(:with_state)
1781
+ end
1782
+
1783
+ def test_should_only_include_records_with_state_in_singular_with_scope
1784
+ parked = @resource.create :state => 'parked'
1785
+ idling = @resource.create :state => 'idling'
1786
+
1787
+ assert_equal [parked], @resource.with_state(:parked)
1788
+ end
1789
+
1790
+ def test_should_create_plural_with_scope
1791
+ assert @resource.respond_to?(:with_states)
1792
+ end
1793
+
1794
+ def test_should_only_include_records_with_states_in_plural_with_scope
1795
+ parked = @resource.create :state => 'parked'
1796
+ idling = @resource.create :state => 'idling'
1797
+
1798
+ assert_equal [parked, idling], @resource.with_states(:parked, :idling)
1799
+ end
1800
+
1801
+ def test_should_create_singular_without_scope
1802
+ assert @resource.respond_to?(:without_state)
1803
+ end
1804
+
1805
+ def test_should_only_include_records_without_state_in_singular_without_scope
1806
+ parked = @resource.create :state => 'parked'
1807
+ idling = @resource.create :state => 'idling'
1808
+
1809
+ assert_equal [parked], @resource.without_state(:idling)
1810
+ end
1811
+
1812
+ def test_should_create_plural_without_scope
1813
+ assert @resource.respond_to?(:without_states)
1814
+ end
1815
+
1816
+ def test_should_only_include_records_without_states_in_plural_without_scope
1817
+ parked = @resource.create :state => 'parked'
1818
+ idling = @resource.create :state => 'idling'
1819
+ first_gear = @resource.create :state => 'first_gear'
1820
+
1821
+ assert_equal [parked, idling], @resource.without_states(:first_gear)
1822
+ end
1823
+
1824
+ def test_should_allow_chaining_scopes
1825
+ parked = @resource.create :state => 'parked'
1826
+ idling = @resource.create :state => 'idling'
1827
+
1828
+ assert_equal [idling], @resource.without_state(:parked).with_state(:idling)
1829
+ end
1830
+ end
1831
+
1832
+ class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
1833
+ def setup
1834
+ @resource = new_resource
1835
+ @machine = StateMachine::Machine.new(@resource, :state)
1836
+
1837
+ @subclass = Class.new(@resource)
1838
+ @subclass_machine = @subclass.state_machine(:state) {}
1839
+ @subclass_machine.state :parked, :idling, :first_gear
1840
+ end
1841
+
1842
+ def test_should_only_include_records_with_subclass_states_in_with_scope
1843
+ parked = @subclass.create :state => 'parked'
1844
+ idling = @subclass.create :state => 'idling'
1845
+
1846
+ assert_equal [parked, idling], @subclass.with_states(:parked, :idling)
1847
+ end
1848
+
1849
+ def test_should_only_include_records_without_subclass_states_in_without_scope
1850
+ parked = @subclass.create :state => 'parked'
1851
+ idling = @subclass.create :state => 'idling'
1852
+ first_gear = @subclass.create :state => 'first_gear'
1853
+
1854
+ assert_equal [parked, idling], @subclass.without_states(:first_gear)
1855
+ end
1856
+ end
1857
+
1858
+ class MachineWithComplexPluralizationScopesTest < BaseTestCase
1859
+ def setup
1860
+ @resource = new_resource
1861
+ @machine = StateMachine::Machine.new(@resource, :status)
1862
+ end
1863
+
1864
+ def test_should_create_singular_with_scope
1865
+ assert @resource.respond_to?(:with_status)
1866
+ end
1867
+
1868
+ def test_should_create_plural_with_scope
1869
+ assert @resource.respond_to?(:with_statuses)
1870
+ end
1871
+ end
1872
+
1873
+ class MachineWithScopesAndJoinsTest < BaseTestCase
1874
+ def setup
1875
+ @company = new_resource(:company)
1876
+ DataMapperTest.const_set('Company', @company)
1877
+
1878
+ @vehicle = new_resource(:vehicle) do
1879
+ property :company_id, Integer
1880
+ auto_migrate!
1881
+
1882
+ belongs_to :company
1883
+ end
1884
+ DataMapperTest.const_set('Vehicle', @vehicle)
1885
+
1886
+ @company_machine = StateMachine::Machine.new(@company, :initial => :active)
1887
+ @vehicle_machine = StateMachine::Machine.new(@vehicle, :initial => :parked)
1888
+ @vehicle_machine.state :idling
1889
+
1890
+ @ford = @company.create
1891
+ @mustang = @vehicle.create(:company => @ford)
1892
+ end
1893
+
1894
+ def test_should_find_records_in_with_scope
1895
+ assert_equal [@mustang], @vehicle.with_states(:parked).all(Vehicle.company.state => 'active')
1896
+ end
1897
+
1898
+ def test_should_find_records_in_without_scope
1899
+ assert_equal [@mustang], @vehicle.without_states(:idling).all(Vehicle.company.state => 'active')
1900
+ end
1901
+
1902
+ def teardown
1903
+ DataMapperTest.class_eval do
1904
+ remove_const('Vehicle')
1905
+ remove_const('Company')
1906
+ end
1907
+ end
1908
+ end
1909
+ end