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
@@ -28,7 +28,7 @@ class EvalHelpersSymbolTest < EvalHelpersBaseTest
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_should_call_method_on_object_with_no_arguments
|
31
|
-
|
31
|
+
assert_equal true, evaluate_method(@object, :callback, 1, 2, 3)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -46,6 +46,34 @@ class EvalHelpersSymbolWithArgumentsTest < EvalHelpersBaseTest
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
class EvalHelpersSymbolWithBlockTest < EvalHelpersBaseTest
|
50
|
+
def setup
|
51
|
+
class << (@object = Object.new)
|
52
|
+
def callback
|
53
|
+
yield
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_should_call_method_on_object_with_block
|
59
|
+
assert_equal true, evaluate_method(@object, :callback) { true }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class EvalHelpersSymbolWithArgumentsAndBlockTest < EvalHelpersBaseTest
|
64
|
+
def setup
|
65
|
+
class << (@object = Object.new)
|
66
|
+
def callback(*args)
|
67
|
+
args << yield
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_should_call_method_on_object_with_all_arguments_and_block
|
73
|
+
assert_equal [1, 2, 3, true], evaluate_method(@object, :callback, 1, 2, 3) { true }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
49
77
|
class EvalHelpersSymbolTaintedMethodTest < EvalHelpersBaseTest
|
50
78
|
def setup
|
51
79
|
class << (@object = Object.new)
|
@@ -81,6 +109,16 @@ class EvalHelpersStringTest < EvalHelpersBaseTest
|
|
81
109
|
end
|
82
110
|
end
|
83
111
|
|
112
|
+
class EvalHelpersStringWithBlockTest < EvalHelpersBaseTest
|
113
|
+
def setup
|
114
|
+
@object = Object.new
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_should_call_method_on_object_with_block
|
118
|
+
assert_equal 1, evaluate_method(@object, 'yield') { 1 }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
84
122
|
class EvalHelpersProcTest < EvalHelpersBaseTest
|
85
123
|
def setup
|
86
124
|
@object = Object.new
|
@@ -118,3 +156,67 @@ class EvalHelpersProcWithArgumentsTest < EvalHelpersBaseTest
|
|
118
156
|
assert_equal [@object, 1, 2, 3], evaluate_method(@object, @proc, 1, 2, 3)
|
119
157
|
end
|
120
158
|
end
|
159
|
+
|
160
|
+
class EvalHelpersProcWithBlockTest < EvalHelpersBaseTest
|
161
|
+
def setup
|
162
|
+
@object = Object.new
|
163
|
+
@proc = lambda {|obj, block| block.call}
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_should_call_method_on_object_with_block
|
167
|
+
assert_equal true, evaluate_method(@object, @proc, 1, 2, 3) { true }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class EvalHelpersProcWithBlockWithoutArgumentsTest < EvalHelpersBaseTest
|
172
|
+
def setup
|
173
|
+
@object = Object.new
|
174
|
+
@proc = lambda {|*args| args}
|
175
|
+
class << @proc
|
176
|
+
def arity
|
177
|
+
0
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_should_call_proc_without_arguments
|
183
|
+
block = lambda { true }
|
184
|
+
assert_equal [], evaluate_method(@object, @proc, 1, 2, 3, &block)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class EvalHelpersProcWithBlockWithoutObjectTest < EvalHelpersBaseTest
|
189
|
+
def setup
|
190
|
+
@object = Object.new
|
191
|
+
@proc = lambda {|block| [block]}
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_should_call_proc_with_block_only
|
195
|
+
block = lambda { true }
|
196
|
+
assert_equal [block], evaluate_method(@object, @proc, 1, 2, 3, &block)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class EvalHelpersProcBlockAndImplicitArgumentsTest < EvalHelpersBaseTest
|
201
|
+
def setup
|
202
|
+
@object = Object.new
|
203
|
+
@proc = lambda {|*args| args}
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_should_call_method_on_object_with_all_arguments_and_block
|
207
|
+
block = lambda { true }
|
208
|
+
assert_equal [@object, 1, 2, 3, block], evaluate_method(@object, @proc, 1, 2, 3, &block)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class EvalHelpersProcBlockAndExplicitArgumentsTest < EvalHelpersBaseTest
|
213
|
+
def setup
|
214
|
+
@object = Object.new
|
215
|
+
@proc = lambda {|object, arg1, arg2, arg3, block| [object, arg1, arg2, arg3, block]}
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_should_call_method_on_object_with_all_arguments_and_block
|
219
|
+
block = lambda { true }
|
220
|
+
assert_equal [@object, 1, 2, 3, block], evaluate_method(@object, @proc, 1, 2, 3, &block)
|
221
|
+
end
|
222
|
+
end
|
data/test/unit/event_test.rb
CHANGED
@@ -710,6 +710,7 @@ end
|
|
710
710
|
begin
|
711
711
|
# Load library
|
712
712
|
require 'rubygems'
|
713
|
+
gem 'ruby-graphviz', '>=0.9.0'
|
713
714
|
require 'graphviz'
|
714
715
|
|
715
716
|
class EventDrawingTest < Test::Unit::TestCase
|
@@ -739,5 +740,5 @@ begin
|
|
739
740
|
end
|
740
741
|
end
|
741
742
|
rescue LoadError
|
742
|
-
$stderr.puts 'Skipping GraphViz StateMachine::Event tests. `gem install ruby-graphviz` and try again.'
|
743
|
+
$stderr.puts 'Skipping GraphViz StateMachine::Event tests. `gem install ruby-graphviz` >= v0.9.0 and try again.'
|
743
744
|
end
|
data/test/unit/guard_test.rb
CHANGED
@@ -776,6 +776,7 @@ end
|
|
776
776
|
begin
|
777
777
|
# Load library
|
778
778
|
require 'rubygems'
|
779
|
+
gem 'ruby-graphviz', '>=0.9.0'
|
779
780
|
require 'graphviz'
|
780
781
|
|
781
782
|
class GuardDrawingTest < Test::Unit::TestCase
|
@@ -904,5 +905,5 @@ begin
|
|
904
905
|
end
|
905
906
|
end
|
906
907
|
rescue LoadError
|
907
|
-
$stderr.puts 'Skipping GraphViz StateMachine::Guard tests. `gem install ruby-graphviz` and try again.'
|
908
|
+
$stderr.puts 'Skipping GraphViz StateMachine::Guard tests. `gem install ruby-graphviz` >= v0.9.0 and try again.'
|
908
909
|
end
|
@@ -0,0 +1,909 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
|
2
|
+
|
3
|
+
# Load library
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
gem 'activemodel', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=3.0.0.beta'
|
7
|
+
require 'active_model'
|
8
|
+
require 'active_model/observing'
|
9
|
+
require 'active_support/all'
|
10
|
+
|
11
|
+
module ActiveModelTest
|
12
|
+
class BaseTestCase < Test::Unit::TestCase
|
13
|
+
def default_test
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
# Creates a new ActiveRecord model (and the associated table)
|
18
|
+
def new_model(&block)
|
19
|
+
# Simple ActiveModel superclass
|
20
|
+
parent = Class.new do
|
21
|
+
def self.model_attribute(name)
|
22
|
+
define_method(name) { instance_variable_get("@#{name}") }
|
23
|
+
define_method("#{name}=") do |value|
|
24
|
+
send("#{name}_will_change!") if self.class <= ActiveModel::Dirty && !send("#{name}_changed?")
|
25
|
+
instance_variable_set("@#{name}", value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.create
|
30
|
+
object = new
|
31
|
+
object.save
|
32
|
+
object
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(attrs = {})
|
36
|
+
attrs.each {|attr, value| send("#{attr}=", value)}
|
37
|
+
@changed_attributes = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def attributes
|
41
|
+
@attributes ||= {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def save
|
45
|
+
@changed_attributes = {}
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
model = Class.new(parent) do
|
51
|
+
def self.name
|
52
|
+
'ActiveModelTest::Foo'
|
53
|
+
end
|
54
|
+
|
55
|
+
model_attribute :state
|
56
|
+
end
|
57
|
+
model.class_eval(&block) if block_given?
|
58
|
+
model
|
59
|
+
end
|
60
|
+
|
61
|
+
# Creates a new ActiveRecord observer
|
62
|
+
def new_observer(model, &block)
|
63
|
+
observer = Class.new(ActiveModel::Observer) do
|
64
|
+
attr_accessor :notifications
|
65
|
+
|
66
|
+
def initialize
|
67
|
+
super
|
68
|
+
@notifications = []
|
69
|
+
end
|
70
|
+
end
|
71
|
+
observer.observe(model)
|
72
|
+
observer.class_eval(&block) if block_given?
|
73
|
+
observer
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class IntegrationTest < BaseTestCase
|
78
|
+
def test_should_match_if_class_includes_dirty_feature
|
79
|
+
assert StateMachine::Integrations::ActiveModel.matches?(new_model { include ActiveModel::Dirty })
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_should_match_if_class_includes_observing_feature
|
83
|
+
assert StateMachine::Integrations::ActiveModel.matches?(new_model { include ActiveModel::Observing })
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_should_match_if_class_includes_validations_feature
|
87
|
+
assert StateMachine::Integrations::ActiveModel.matches?(new_model { include ActiveModel::Validations })
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_should_not_match_if_class_does_not_include_active_model_features
|
91
|
+
assert !StateMachine::Integrations::ActiveModel.matches?(new_model)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_should_have_no_defaults
|
95
|
+
assert_equal e = {}, StateMachine::Integrations::ActiveModel.defaults
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class MachineByDefaultTest < BaseTestCase
|
100
|
+
def setup
|
101
|
+
@model = new_model
|
102
|
+
@machine = StateMachine::Machine.new(@model, :integration => :active_model)
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_should_not_have_action
|
106
|
+
assert_nil @machine.action
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_should_use_transactions
|
110
|
+
assert_equal true, @machine.use_transactions
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_should_not_have_any_before_callbacks
|
114
|
+
assert_equal 0, @machine.callbacks[:before].size
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_should_not_have_any_after_callbacks
|
118
|
+
assert_equal 0, @machine.callbacks[:after].size
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class MachineWithStaticInitialStateTest < BaseTestCase
|
123
|
+
def setup
|
124
|
+
@model = new_model
|
125
|
+
@machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_should_set_initial_state_on_created_object
|
129
|
+
record = @model.new
|
130
|
+
assert_equal 'parked', record.state
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class MachineWithDynamicInitialStateTest < BaseTestCase
|
135
|
+
def setup
|
136
|
+
@model = new_model
|
137
|
+
@machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked}, :integration => :active_model)
|
138
|
+
@machine.state :parked
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_should_set_initial_state_on_created_object
|
142
|
+
record = @model.new
|
143
|
+
assert_equal 'parked', record.state
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class MachineWithModelStateAttributeTest < BaseTestCase
|
148
|
+
def setup
|
149
|
+
@model = new_model
|
150
|
+
@machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
|
151
|
+
@machine.other_states(:idling)
|
152
|
+
|
153
|
+
@record = @model.new
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_should_have_an_attribute_predicate
|
157
|
+
assert @record.respond_to?(:state?)
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_should_raise_exception_for_predicate_without_parameters
|
161
|
+
assert_raise(IndexError) { @record.state? }
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_should_return_false_for_predicate_if_does_not_match_current_value
|
165
|
+
assert !@record.state?(:idling)
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_should_return_true_for_predicate_if_matches_current_value
|
169
|
+
assert @record.state?(:parked)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_should_raise_exception_for_predicate_if_invalid_state_specified
|
173
|
+
assert_raise(IndexError) { @record.state?(:invalid) }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class MachineWithNonModelStateAttributeUndefinedTest < BaseTestCase
|
178
|
+
def setup
|
179
|
+
@model = new_model do
|
180
|
+
def initialize
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
@machine = StateMachine::Machine.new(@model, :status, :initial => :parked, :integration => :active_model)
|
185
|
+
@machine.other_states(:idling)
|
186
|
+
@record = @model.new
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_should_not_define_a_reader_attribute_for_the_attribute
|
190
|
+
assert !@record.respond_to?(:status)
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_should_not_define_a_writer_attribute_for_the_attribute
|
194
|
+
assert !@record.respond_to?(:status=)
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_should_define_an_attribute_predicate
|
198
|
+
assert @record.respond_to?(:status?)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class MachineWithInitializedStateTest < BaseTestCase
|
203
|
+
def setup
|
204
|
+
@model = new_model
|
205
|
+
@machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
|
206
|
+
@machine.state nil, :idling
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_should_should_use_initialized_state_when_static
|
210
|
+
record = @model.new(:state => nil)
|
211
|
+
assert_nil record.state
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_should_should_not_use_initialized_state_when_dynamic
|
215
|
+
@machine.initial_state = lambda {:parked}
|
216
|
+
record = @model.new(:state => nil)
|
217
|
+
assert_equal 'parked', record.state
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class MachineWithDirtyAttributesTest < BaseTestCase
|
222
|
+
def setup
|
223
|
+
@model = new_model do
|
224
|
+
include ActiveModel::Dirty
|
225
|
+
define_attribute_methods [:state]
|
226
|
+
end
|
227
|
+
@machine = StateMachine::Machine.new(@model, :initial => :parked)
|
228
|
+
@machine.event :ignite
|
229
|
+
@machine.state :idling
|
230
|
+
|
231
|
+
@record = @model.create
|
232
|
+
|
233
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
234
|
+
@transition.perform
|
235
|
+
end
|
236
|
+
|
237
|
+
def test_should_include_state_in_changed_attributes
|
238
|
+
assert_equal %w(state), @record.changed
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_should_track_attribute_change
|
242
|
+
assert_equal %w(parked idling), @record.changes['state']
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_should_not_reset_changes_on_multiple_transitions
|
246
|
+
transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
|
247
|
+
transition.perform
|
248
|
+
|
249
|
+
assert_equal %w(parked idling), @record.changes['state']
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
|
254
|
+
def setup
|
255
|
+
@model = new_model do
|
256
|
+
include ActiveModel::Dirty
|
257
|
+
define_attribute_methods [:state]
|
258
|
+
end
|
259
|
+
@machine = StateMachine::Machine.new(@model, :initial => :parked)
|
260
|
+
@machine.event :park
|
261
|
+
|
262
|
+
@record = @model.create
|
263
|
+
|
264
|
+
@transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
|
265
|
+
@transition.perform
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_should_include_state_in_changed_attributes
|
269
|
+
assert_equal %w(state), @record.changed
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_should_track_attribute_changes
|
273
|
+
assert_equal %w(parked parked), @record.changes['state']
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
|
278
|
+
def setup
|
279
|
+
@model = new_model do
|
280
|
+
include ActiveModel::Dirty
|
281
|
+
model_attribute :status
|
282
|
+
define_attribute_methods [:status]
|
283
|
+
end
|
284
|
+
@machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
|
285
|
+
@machine.event :ignite
|
286
|
+
@machine.state :idling
|
287
|
+
|
288
|
+
@record = @model.create
|
289
|
+
|
290
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
291
|
+
@transition.perform
|
292
|
+
end
|
293
|
+
|
294
|
+
def test_should_include_state_in_changed_attributes
|
295
|
+
assert_equal %w(status), @record.changed
|
296
|
+
end
|
297
|
+
|
298
|
+
def test_should_track_attribute_change
|
299
|
+
assert_equal %w(parked idling), @record.changes['status']
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_should_not_reset_changes_on_multiple_transitions
|
303
|
+
transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
|
304
|
+
transition.perform
|
305
|
+
|
306
|
+
assert_equal %w(parked idling), @record.changes['status']
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
|
311
|
+
def setup
|
312
|
+
@model = new_model do
|
313
|
+
include ActiveModel::Dirty
|
314
|
+
model_attribute :status
|
315
|
+
define_attribute_methods [:status]
|
316
|
+
end
|
317
|
+
@machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
|
318
|
+
@machine.event :park
|
319
|
+
|
320
|
+
@record = @model.create
|
321
|
+
|
322
|
+
@transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
|
323
|
+
@transition.perform
|
324
|
+
end
|
325
|
+
|
326
|
+
def test_should_include_state_in_changed_attributes
|
327
|
+
assert_equal %w(status), @record.changed
|
328
|
+
end
|
329
|
+
|
330
|
+
def test_should_track_attribute_changes
|
331
|
+
assert_equal %w(parked parked), @record.changes['status']
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
class MachineWithCallbacksTest < BaseTestCase
|
336
|
+
def setup
|
337
|
+
@model = new_model
|
338
|
+
@machine = StateMachine::Machine.new(@model, :initial => :parked, :integration => :active_model)
|
339
|
+
@machine.other_states :idling
|
340
|
+
@machine.event :ignite
|
341
|
+
|
342
|
+
@record = @model.new(:state => 'parked')
|
343
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
344
|
+
end
|
345
|
+
|
346
|
+
def test_should_run_before_callbacks
|
347
|
+
called = false
|
348
|
+
@machine.before_transition {called = true}
|
349
|
+
|
350
|
+
@transition.perform
|
351
|
+
assert called
|
352
|
+
end
|
353
|
+
|
354
|
+
def test_should_pass_record_to_before_callbacks_with_one_argument
|
355
|
+
record = nil
|
356
|
+
@machine.before_transition {|arg| record = arg}
|
357
|
+
|
358
|
+
@transition.perform
|
359
|
+
assert_equal @record, record
|
360
|
+
end
|
361
|
+
|
362
|
+
def test_should_pass_record_and_transition_to_before_callbacks_with_multiple_arguments
|
363
|
+
callback_args = nil
|
364
|
+
@machine.before_transition {|*args| callback_args = args}
|
365
|
+
|
366
|
+
@transition.perform
|
367
|
+
assert_equal [@record, @transition], callback_args
|
368
|
+
end
|
369
|
+
|
370
|
+
def test_should_run_before_callbacks_outside_the_context_of_the_record
|
371
|
+
context = nil
|
372
|
+
@machine.before_transition {context = self}
|
373
|
+
|
374
|
+
@transition.perform
|
375
|
+
assert_equal self, context
|
376
|
+
end
|
377
|
+
|
378
|
+
def test_should_run_after_callbacks
|
379
|
+
called = false
|
380
|
+
@machine.after_transition {called = true}
|
381
|
+
|
382
|
+
@transition.perform
|
383
|
+
assert called
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_should_pass_record_to_after_callbacks_with_one_argument
|
387
|
+
record = nil
|
388
|
+
@machine.after_transition {|arg| record = arg}
|
389
|
+
|
390
|
+
@transition.perform
|
391
|
+
assert_equal @record, record
|
392
|
+
end
|
393
|
+
|
394
|
+
def test_should_pass_record_and_transition_to_after_callbacks_with_multiple_arguments
|
395
|
+
callback_args = nil
|
396
|
+
@machine.after_transition {|*args| callback_args = args}
|
397
|
+
|
398
|
+
@transition.perform
|
399
|
+
assert_equal [@record, @transition], callback_args
|
400
|
+
end
|
401
|
+
|
402
|
+
def test_should_run_after_callbacks_outside_the_context_of_the_record
|
403
|
+
context = nil
|
404
|
+
@machine.after_transition {context = self}
|
405
|
+
|
406
|
+
@transition.perform
|
407
|
+
assert_equal self, context
|
408
|
+
end
|
409
|
+
|
410
|
+
def test_should_run_around_callbacks
|
411
|
+
before_called = false
|
412
|
+
after_called = false
|
413
|
+
@machine.around_transition {|block| before_called = true; block.call; after_called = true}
|
414
|
+
|
415
|
+
@transition.perform
|
416
|
+
assert before_called
|
417
|
+
assert after_called
|
418
|
+
end
|
419
|
+
|
420
|
+
def test_should_include_transition_states_in_known_states
|
421
|
+
@machine.before_transition :to => :first_gear, :do => lambda {}
|
422
|
+
|
423
|
+
assert_equal [:parked, :idling, :first_gear], @machine.states.map {|state| state.name}
|
424
|
+
end
|
425
|
+
|
426
|
+
def test_should_allow_symbolic_callbacks
|
427
|
+
callback_args = nil
|
428
|
+
|
429
|
+
klass = class << @record; self; end
|
430
|
+
klass.send(:define_method, :after_ignite) do |*args|
|
431
|
+
callback_args = args
|
432
|
+
end
|
433
|
+
|
434
|
+
@machine.before_transition(:after_ignite)
|
435
|
+
|
436
|
+
@transition.perform
|
437
|
+
assert_equal [@transition], callback_args
|
438
|
+
end
|
439
|
+
|
440
|
+
def test_should_allow_string_callbacks
|
441
|
+
class << @record
|
442
|
+
attr_reader :callback_result
|
443
|
+
end
|
444
|
+
|
445
|
+
@machine.before_transition('@callback_result = [1, 2, 3]')
|
446
|
+
@transition.perform
|
447
|
+
|
448
|
+
assert_equal [1, 2, 3], @record.callback_result
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
class MachineWithFailedBeforeCallbacksTest < BaseTestCase
|
453
|
+
def setup
|
454
|
+
@callbacks = []
|
455
|
+
|
456
|
+
@model = new_model
|
457
|
+
@machine = StateMachine::Machine.new(@model, :integration => :active_model)
|
458
|
+
@machine.state :parked, :idling
|
459
|
+
@machine.event :ignite
|
460
|
+
@machine.before_transition {@callbacks << :before_1; false}
|
461
|
+
@machine.before_transition {@callbacks << :before_2}
|
462
|
+
@machine.after_transition {@callbacks << :after}
|
463
|
+
@machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
|
464
|
+
|
465
|
+
@record = @model.new(:state => 'parked')
|
466
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
467
|
+
@result = @transition.perform
|
468
|
+
end
|
469
|
+
|
470
|
+
def test_should_not_be_successful
|
471
|
+
assert !@result
|
472
|
+
end
|
473
|
+
|
474
|
+
def test_should_not_change_current_state
|
475
|
+
assert_equal 'parked', @record.state
|
476
|
+
end
|
477
|
+
|
478
|
+
def test_should_not_run_further_callbacks
|
479
|
+
assert_equal [:before_1], @callbacks
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
class MachineWithFailedAfterCallbacksTest < BaseTestCase
|
484
|
+
def setup
|
485
|
+
@callbacks = []
|
486
|
+
|
487
|
+
@model = new_model
|
488
|
+
@machine = StateMachine::Machine.new(@model, :integration => :active_model)
|
489
|
+
@machine.state :parked, :idling
|
490
|
+
@machine.event :ignite
|
491
|
+
@machine.after_transition {@callbacks << :after_1; false}
|
492
|
+
@machine.after_transition {@callbacks << :after_2}
|
493
|
+
@machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
|
494
|
+
|
495
|
+
@record = @model.new(:state => 'parked')
|
496
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
497
|
+
@result = @transition.perform
|
498
|
+
end
|
499
|
+
|
500
|
+
def test_should_be_successful
|
501
|
+
assert @result
|
502
|
+
end
|
503
|
+
|
504
|
+
def test_should_change_current_state
|
505
|
+
assert_equal 'idling', @record.state
|
506
|
+
end
|
507
|
+
|
508
|
+
def test_should_not_run_further_after_callbacks
|
509
|
+
assert_equal [:around_before, :around_after, :after_1], @callbacks
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
class MachineWithValidationsTest < BaseTestCase
|
514
|
+
def setup
|
515
|
+
@model = new_model { include ActiveModel::Validations }
|
516
|
+
@machine = StateMachine::Machine.new(@model, :action => :save)
|
517
|
+
@machine.state :parked
|
518
|
+
|
519
|
+
@record = @model.new
|
520
|
+
end
|
521
|
+
|
522
|
+
def test_should_invalidate_using_errors
|
523
|
+
I18n.backend = I18n::Backend::Simple.new if Object.const_defined?(:I18n)
|
524
|
+
@record.state = 'parked'
|
525
|
+
|
526
|
+
@machine.invalidate(@record, :state, :invalid_transition, [[:event, :park]])
|
527
|
+
assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
|
528
|
+
end
|
529
|
+
|
530
|
+
def test_should_auto_prefix_custom_attributes_on_invalidation
|
531
|
+
@machine.invalidate(@record, :event, :invalid)
|
532
|
+
|
533
|
+
assert_equal ['State event is invalid'], @record.errors.full_messages
|
534
|
+
end
|
535
|
+
|
536
|
+
def test_should_clear_errors_on_reset
|
537
|
+
@record.state = 'parked'
|
538
|
+
@record.errors.add(:state, 'is invalid')
|
539
|
+
|
540
|
+
@machine.reset(@record)
|
541
|
+
assert_equal [], @record.errors.full_messages
|
542
|
+
end
|
543
|
+
|
544
|
+
def test_should_be_valid_if_state_is_known
|
545
|
+
@record.state = 'parked'
|
546
|
+
|
547
|
+
assert @record.valid?
|
548
|
+
end
|
549
|
+
|
550
|
+
def test_should_not_be_valid_if_state_is_unknown
|
551
|
+
@record.state = 'invalid'
|
552
|
+
|
553
|
+
assert !@record.valid?
|
554
|
+
assert_equal ['State is invalid'], @record.errors.full_messages
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
|
559
|
+
def setup
|
560
|
+
@model = new_model { include ActiveModel::Validations }
|
561
|
+
|
562
|
+
@machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
|
563
|
+
@machine.state :parked
|
564
|
+
|
565
|
+
@record = @model.new
|
566
|
+
end
|
567
|
+
|
568
|
+
def test_should_add_validation_errors_to_custom_attribute
|
569
|
+
@record.state = 'invalid'
|
570
|
+
|
571
|
+
assert !@record.valid?
|
572
|
+
assert_equal ['State is invalid'], @record.errors.full_messages
|
573
|
+
|
574
|
+
@record.state = 'parked'
|
575
|
+
assert @record.valid?
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
class MachineWithStateDrivenValidationsTest < BaseTestCase
|
580
|
+
def setup
|
581
|
+
@model = new_model do
|
582
|
+
include ActiveModel::Validations
|
583
|
+
attr_accessor :seatbelt
|
584
|
+
end
|
585
|
+
|
586
|
+
@machine = StateMachine::Machine.new(@model)
|
587
|
+
@machine.state :first_gear, :second_gear do
|
588
|
+
validates_presence_of :seatbelt
|
589
|
+
end
|
590
|
+
@machine.other_states :parked
|
591
|
+
end
|
592
|
+
|
593
|
+
def test_should_be_valid_if_validation_fails_outside_state_scope
|
594
|
+
record = @model.new(:state => 'parked', :seatbelt => nil)
|
595
|
+
assert record.valid?
|
596
|
+
end
|
597
|
+
|
598
|
+
def test_should_be_invalid_if_validation_fails_within_state_scope
|
599
|
+
record = @model.new(:state => 'first_gear', :seatbelt => nil)
|
600
|
+
assert !record.valid?
|
601
|
+
end
|
602
|
+
|
603
|
+
def test_should_be_valid_if_validation_succeeds_within_state_scope
|
604
|
+
record = @model.new(:state => 'second_gear', :seatbelt => true)
|
605
|
+
assert record.valid?
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
class MachineWithObserversTest < BaseTestCase
|
610
|
+
def setup
|
611
|
+
@model = new_model { include ActiveModel::Observing }
|
612
|
+
@machine = StateMachine::Machine.new(@model)
|
613
|
+
@machine.state :parked, :idling
|
614
|
+
@machine.event :ignite
|
615
|
+
@record = @model.new(:state => 'parked')
|
616
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
617
|
+
end
|
618
|
+
|
619
|
+
def test_should_call_all_transition_callback_permutations
|
620
|
+
callbacks = [
|
621
|
+
:before_ignite_from_parked_to_idling,
|
622
|
+
:before_ignite_from_parked,
|
623
|
+
:before_ignite_to_idling,
|
624
|
+
:before_ignite,
|
625
|
+
:before_transition_state_from_parked_to_idling,
|
626
|
+
:before_transition_state_from_parked,
|
627
|
+
:before_transition_state_to_idling,
|
628
|
+
:before_transition_state,
|
629
|
+
:before_transition
|
630
|
+
]
|
631
|
+
|
632
|
+
notified = false
|
633
|
+
observer = new_observer(@model) do
|
634
|
+
callbacks.each do |callback|
|
635
|
+
define_method(callback) do |*args|
|
636
|
+
notifications << callback
|
637
|
+
end
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
instance = observer.instance
|
642
|
+
|
643
|
+
@transition.perform
|
644
|
+
assert_equal callbacks, instance.notifications
|
645
|
+
end
|
646
|
+
|
647
|
+
def test_should_pass_record_and_transition_to_before_callbacks
|
648
|
+
observer = new_observer(@model) do
|
649
|
+
def before_transition(*args)
|
650
|
+
notifications << args
|
651
|
+
end
|
652
|
+
end
|
653
|
+
instance = observer.instance
|
654
|
+
|
655
|
+
@transition.perform
|
656
|
+
assert_equal [[@record, @transition]], instance.notifications
|
657
|
+
end
|
658
|
+
|
659
|
+
def test_should_pass_record_and_transition_to_after_callbacks
|
660
|
+
observer = new_observer(@model) do
|
661
|
+
def after_transition(*args)
|
662
|
+
notifications << args
|
663
|
+
end
|
664
|
+
end
|
665
|
+
instance = observer.instance
|
666
|
+
|
667
|
+
@transition.perform
|
668
|
+
assert_equal [[@record, @transition]], instance.notifications
|
669
|
+
end
|
670
|
+
|
671
|
+
def test_should_call_methods_outside_the_context_of_the_record
|
672
|
+
observer = new_observer(@model) do
|
673
|
+
def before_ignite(*args)
|
674
|
+
notifications << self
|
675
|
+
end
|
676
|
+
end
|
677
|
+
instance = observer.instance
|
678
|
+
|
679
|
+
@transition.perform
|
680
|
+
assert_equal [instance], instance.notifications
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
class MachineWithNamespacedObserversTest < BaseTestCase
|
685
|
+
def setup
|
686
|
+
@model = new_model { include ActiveModel::Observing }
|
687
|
+
@machine = StateMachine::Machine.new(@model, :state, :namespace => 'alarm')
|
688
|
+
@machine.state :active, :off
|
689
|
+
@machine.event :enable
|
690
|
+
@record = @model.new(:state => 'off')
|
691
|
+
@transition = StateMachine::Transition.new(@record, @machine, :enable, :off, :active)
|
692
|
+
end
|
693
|
+
|
694
|
+
def test_should_call_namespaced_before_event_method
|
695
|
+
observer = new_observer(@model) do
|
696
|
+
def before_enable_alarm(*args)
|
697
|
+
notifications << args
|
698
|
+
end
|
699
|
+
end
|
700
|
+
instance = observer.instance
|
701
|
+
|
702
|
+
@transition.perform
|
703
|
+
assert_equal [[@record, @transition]], instance.notifications
|
704
|
+
end
|
705
|
+
|
706
|
+
def test_should_call_namespaced_after_event_method
|
707
|
+
observer = new_observer(@model) do
|
708
|
+
def after_enable_alarm(*args)
|
709
|
+
notifications << args
|
710
|
+
end
|
711
|
+
end
|
712
|
+
instance = observer.instance
|
713
|
+
|
714
|
+
@transition.perform
|
715
|
+
assert_equal [[@record, @transition]], instance.notifications
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
class MachineWithMixedCallbacksTest < BaseTestCase
|
720
|
+
def setup
|
721
|
+
@model = new_model { include ActiveModel::Observing }
|
722
|
+
@machine = StateMachine::Machine.new(@model)
|
723
|
+
@machine.state :parked, :idling
|
724
|
+
@machine.event :ignite
|
725
|
+
@record = @model.new(:state => 'parked')
|
726
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
727
|
+
|
728
|
+
@notifications = []
|
729
|
+
|
730
|
+
# Create callbacks
|
731
|
+
@machine.before_transition {@notifications << :callback_before_transition}
|
732
|
+
@machine.after_transition {@notifications << :callback_after_transition}
|
733
|
+
@machine.around_transition {|block| @notifications << :callback_around_before_transition; block.call; @notifications << :callback_around_after_transition}
|
734
|
+
|
735
|
+
# Create observer callbacks
|
736
|
+
observer = new_observer(@model) do
|
737
|
+
def before_ignite(*args)
|
738
|
+
notifications << :observer_before_ignite
|
739
|
+
end
|
740
|
+
|
741
|
+
def before_transition(*args)
|
742
|
+
notifications << :observer_before_transition
|
743
|
+
end
|
744
|
+
|
745
|
+
def after_ignite(*args)
|
746
|
+
notifications << :observer_after_ignite
|
747
|
+
end
|
748
|
+
|
749
|
+
def after_transition(*args)
|
750
|
+
notifications << :observer_after_transition
|
751
|
+
end
|
752
|
+
end
|
753
|
+
instance = observer.instance
|
754
|
+
instance.notifications = @notifications
|
755
|
+
|
756
|
+
@transition.perform
|
757
|
+
end
|
758
|
+
|
759
|
+
def test_should_invoke_callbacks_in_specific_order
|
760
|
+
expected = [
|
761
|
+
:callback_before_transition,
|
762
|
+
:callback_around_before_transition,
|
763
|
+
:observer_before_ignite,
|
764
|
+
:observer_before_transition,
|
765
|
+
:callback_around_after_transition,
|
766
|
+
:callback_after_transition,
|
767
|
+
:observer_after_ignite,
|
768
|
+
:observer_after_transition
|
769
|
+
]
|
770
|
+
|
771
|
+
assert_equal expected, @notifications
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
class MachineWithInternationalizationTest < BaseTestCase
|
776
|
+
def setup
|
777
|
+
I18n.backend = I18n::Backend::Simple.new
|
778
|
+
|
779
|
+
# Initialize the backend
|
780
|
+
I18n.backend.translate(:en, 'activemodel.errors.messages.invalid_transition', :event => 'ignite', :value => 'idling')
|
781
|
+
|
782
|
+
@model = new_model { include ActiveModel::Validations }
|
783
|
+
end
|
784
|
+
|
785
|
+
def test_should_use_defaults
|
786
|
+
I18n.backend.store_translations(:en, {
|
787
|
+
:activemodel => {:errors => {:messages => {:invalid_transition => 'cannot {{event}}'}}}
|
788
|
+
})
|
789
|
+
|
790
|
+
machine = StateMachine::Machine.new(@model, :action => :save)
|
791
|
+
machine.state :parked, :idling
|
792
|
+
machine.event :ignite
|
793
|
+
|
794
|
+
record = @model.new(:state => 'idling')
|
795
|
+
|
796
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, :ignite]])
|
797
|
+
assert_equal ['State cannot ignite'], record.errors.full_messages
|
798
|
+
end
|
799
|
+
|
800
|
+
def test_should_allow_customized_error_key
|
801
|
+
I18n.backend.store_translations(:en, {
|
802
|
+
:activemodel => {:errors => {:messages => {:bad_transition => 'cannot {{event}}'}}}
|
803
|
+
})
|
804
|
+
|
805
|
+
machine = StateMachine::Machine.new(@model, :action => :save, :messages => {:invalid_transition => :bad_transition})
|
806
|
+
machine.state :parked, :idling
|
807
|
+
|
808
|
+
record = @model.new
|
809
|
+
record.state = 'idling'
|
810
|
+
|
811
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, :ignite]])
|
812
|
+
assert_equal ['State cannot ignite'], record.errors.full_messages
|
813
|
+
end
|
814
|
+
|
815
|
+
def test_should_allow_customized_error_string
|
816
|
+
machine = StateMachine::Machine.new(@model, :action => :save, :messages => {:invalid_transition => 'cannot {{event}}'})
|
817
|
+
machine.state :parked, :idling
|
818
|
+
|
819
|
+
record = @model.new(:state => 'idling')
|
820
|
+
|
821
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, :ignite]])
|
822
|
+
assert_equal ['State cannot ignite'], record.errors.full_messages
|
823
|
+
end
|
824
|
+
|
825
|
+
def test_should_allow_customized_state_key_scoped_to_class_and_machine
|
826
|
+
I18n.backend.store_translations(:en, {
|
827
|
+
:activemodel => {:state_machines => {:'active_model_test/foo' => {:state => {:states => {:parked => 'shutdown'}}}}}
|
828
|
+
})
|
829
|
+
|
830
|
+
machine = StateMachine::Machine.new(@model, :initial => :parked, :action => :save)
|
831
|
+
record = @model.new
|
832
|
+
|
833
|
+
machine.invalidate(record, :event, :invalid_event, [[:state, :parked]])
|
834
|
+
assert_equal ['State event cannot transition when shutdown'], record.errors.full_messages
|
835
|
+
end
|
836
|
+
|
837
|
+
def test_should_allow_customized_state_key_scoped_to_machine
|
838
|
+
I18n.backend.store_translations(:en, {
|
839
|
+
:activemodel => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
|
840
|
+
})
|
841
|
+
|
842
|
+
machine = StateMachine::Machine.new(@model, :initial => :parked, :action => :save)
|
843
|
+
record = @model.new
|
844
|
+
|
845
|
+
machine.invalidate(record, :event, :invalid_event, [[:state, :parked]])
|
846
|
+
assert_equal ['State event cannot transition when shutdown'], record.errors.full_messages
|
847
|
+
end
|
848
|
+
|
849
|
+
def test_should_allow_customized_state_key_unscoped
|
850
|
+
I18n.backend.store_translations(:en, {
|
851
|
+
:activemodel => {:state_machines => {:states => {:parked => 'shutdown'}}}
|
852
|
+
})
|
853
|
+
|
854
|
+
machine = StateMachine::Machine.new(@model, :initial => :parked, :action => :save)
|
855
|
+
record = @model.new
|
856
|
+
|
857
|
+
machine.invalidate(record, :event, :invalid_event, [[:state, :parked]])
|
858
|
+
assert_equal ['State event cannot transition when shutdown'], record.errors.full_messages
|
859
|
+
end
|
860
|
+
|
861
|
+
def test_should_allow_customized_event_key_scoped_to_class_and_machine
|
862
|
+
I18n.backend.store_translations(:en, {
|
863
|
+
:activemodel => {:state_machines => {:'active_model_test/foo' => {:state => {:events => {:park => 'stop'}}}}}
|
864
|
+
})
|
865
|
+
|
866
|
+
machine = StateMachine::Machine.new(@model, :action => :save)
|
867
|
+
machine.event :park
|
868
|
+
record = @model.new
|
869
|
+
|
870
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, :park]])
|
871
|
+
assert_equal ['State cannot transition via "stop"'], record.errors.full_messages
|
872
|
+
end
|
873
|
+
|
874
|
+
def test_should_allow_customized_event_key_scoped_to_machine
|
875
|
+
I18n.backend.store_translations(:en, {
|
876
|
+
:activemodel => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
|
877
|
+
})
|
878
|
+
|
879
|
+
machine = StateMachine::Machine.new(@model, :action => :save)
|
880
|
+
machine.event :park
|
881
|
+
record = @model.new
|
882
|
+
|
883
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, :park]])
|
884
|
+
assert_equal ['State cannot transition via "stop"'], record.errors.full_messages
|
885
|
+
end
|
886
|
+
|
887
|
+
def test_should_allow_customized_event_key_unscoped
|
888
|
+
I18n.backend.store_translations(:en, {
|
889
|
+
:activemodel => {:state_machines => {:events => {:park => 'stop'}}}
|
890
|
+
})
|
891
|
+
|
892
|
+
machine = StateMachine::Machine.new(@model, :action => :save)
|
893
|
+
machine.event :park
|
894
|
+
record = @model.new
|
895
|
+
|
896
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, :park]])
|
897
|
+
assert_equal ['State cannot transition via "stop"'], record.errors.full_messages
|
898
|
+
end
|
899
|
+
|
900
|
+
def test_should_only_add_locale_once_in_load_path
|
901
|
+
assert_equal 1, I18n.load_path.select {|path| path =~ %r{state_machine/integrations/active_model/locale\.rb$}}.length
|
902
|
+
|
903
|
+
# Create another ActiveRecord model that will triger the i18n feature
|
904
|
+
new_model
|
905
|
+
|
906
|
+
assert_equal 1, I18n.load_path.select {|path| path =~ %r{state_machine/integrations/active_model/locale\.rb$}}.length
|
907
|
+
end
|
908
|
+
end
|
909
|
+
end
|