hsume2-state_machine 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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