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