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