state_machine 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +0 -2
- data/.yardopts +3 -2
- data/Appraisals +48 -0
- data/{CHANGELOG.rdoc → CHANGELOG.md} +63 -46
- data/README.md +1029 -0
- data/gemfiles/active_model-3.0.0.gemfile.lock +1 -3
- data/gemfiles/active_model-3.0.5.gemfile.lock +1 -3
- data/gemfiles/active_model-3.1.1.gemfile +7 -0
- data/gemfiles/active_model-3.1.1.gemfile.lock +32 -0
- data/gemfiles/active_record-2.0.0.gemfile.lock +1 -3
- data/gemfiles/active_record-2.0.5.gemfile.lock +1 -3
- data/gemfiles/active_record-2.1.0.gemfile.lock +1 -3
- data/gemfiles/active_record-2.1.2.gemfile.lock +1 -3
- data/gemfiles/active_record-2.2.3.gemfile.lock +1 -3
- data/gemfiles/active_record-2.3.12.gemfile.lock +1 -3
- data/gemfiles/active_record-3.0.0.gemfile.lock +1 -3
- data/gemfiles/active_record-3.0.5.gemfile.lock +1 -3
- data/gemfiles/active_record-3.1.1.gemfile +8 -0
- data/gemfiles/active_record-3.1.1.gemfile.lock +43 -0
- data/gemfiles/data_mapper-0.10.2.gemfile.lock +1 -3
- data/gemfiles/data_mapper-0.9.11.gemfile.lock +1 -3
- data/gemfiles/data_mapper-0.9.4.gemfile.lock +1 -3
- data/gemfiles/data_mapper-0.9.7.gemfile.lock +1 -3
- data/gemfiles/data_mapper-1.0.0.gemfile.lock +1 -3
- data/gemfiles/data_mapper-1.0.1.gemfile.lock +1 -3
- data/gemfiles/data_mapper-1.0.2.gemfile.lock +1 -3
- data/gemfiles/data_mapper-1.1.0.gemfile.lock +1 -3
- data/gemfiles/data_mapper-1.2.0.gemfile +12 -0
- data/gemfiles/data_mapper-1.2.0.gemfile.lock +49 -0
- data/gemfiles/default.gemfile.lock +1 -3
- data/gemfiles/graphviz-0.9.0.gemfile +7 -0
- data/gemfiles/graphviz-0.9.0.gemfile.lock +24 -0
- data/gemfiles/graphviz-0.9.21.gemfile +7 -0
- data/gemfiles/graphviz-0.9.21.gemfile.lock +24 -0
- data/gemfiles/graphviz-1.0.0.gemfile +7 -0
- data/gemfiles/graphviz-1.0.0.gemfile.lock +24 -0
- data/gemfiles/mongo_mapper-0.10.0.gemfile +7 -0
- data/gemfiles/mongo_mapper-0.10.0.gemfile.lock +41 -0
- data/gemfiles/mongo_mapper-0.5.5.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.5.8.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.6.0.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.6.10.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.7.0.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.7.5.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.8.0.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.8.3.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.8.4.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.8.6.gemfile.lock +1 -3
- data/gemfiles/mongo_mapper-0.9.0.gemfile.lock +1 -3
- data/gemfiles/mongoid-2.0.0.gemfile.lock +1 -3
- data/gemfiles/mongoid-2.1.4.gemfile.lock +1 -3
- data/gemfiles/mongoid-2.2.4.gemfile +7 -0
- data/gemfiles/mongoid-2.2.4.gemfile.lock +40 -0
- data/gemfiles/mongoid-2.3.3.gemfile +7 -0
- data/gemfiles/mongoid-2.3.3.gemfile.lock +40 -0
- data/gemfiles/sequel-2.11.0.gemfile.lock +1 -3
- data/gemfiles/sequel-2.12.0.gemfile.lock +1 -3
- data/gemfiles/sequel-2.8.0.gemfile.lock +1 -3
- data/gemfiles/sequel-3.0.0.gemfile.lock +1 -3
- data/gemfiles/sequel-3.13.0.gemfile.lock +1 -3
- data/gemfiles/sequel-3.14.0.gemfile.lock +1 -3
- data/gemfiles/sequel-3.23.0.gemfile.lock +1 -3
- data/gemfiles/sequel-3.24.0.gemfile.lock +1 -3
- data/gemfiles/sequel-3.29.0.gemfile +8 -0
- data/gemfiles/sequel-3.29.0.gemfile.lock +26 -0
- data/lib/state_machine.rb +45 -0
- data/lib/state_machine/event.rb +18 -3
- data/lib/state_machine/event_collection.rb +1 -1
- data/lib/state_machine/integrations/active_model.rb +59 -16
- data/lib/state_machine/integrations/active_model/observer.rb +3 -15
- data/lib/state_machine/integrations/active_record.rb +46 -9
- data/lib/state_machine/integrations/data_mapper.rb +42 -2
- data/lib/state_machine/integrations/data_mapper/versions.rb +22 -10
- data/lib/state_machine/integrations/mongo_mapper.rb +55 -0
- data/lib/state_machine/integrations/mongo_mapper/versions.rb +3 -3
- data/lib/state_machine/integrations/mongoid.rb +57 -12
- data/lib/state_machine/integrations/mongoid/versions.rb +22 -4
- data/lib/state_machine/integrations/sequel.rb +45 -0
- data/lib/state_machine/integrations/sequel/versions.rb +3 -0
- data/lib/state_machine/machine.rb +148 -34
- data/lib/state_machine/node_collection.rb +36 -3
- data/lib/state_machine/state.rb +6 -3
- data/lib/state_machine/state_collection.rb +1 -1
- data/lib/state_machine/version.rb +1 -1
- data/lib/tasks/state_machine.rb +11 -9
- data/state_machine.gemspec +2 -3
- data/test/functional/state_machine_test.rb +54 -1
- data/test/unit/event_collection_test.rb +4 -0
- data/test/unit/event_test.rb +34 -1
- data/test/unit/integrations/active_model_test.rb +80 -0
- data/test/unit/integrations/active_record_test.rb +105 -2
- data/test/unit/integrations/data_mapper_test.rb +27 -25
- data/test/unit/integrations/mongo_mapper_test.rb +80 -25
- data/test/unit/integrations/mongoid_test.rb +61 -6
- data/test/unit/integrations/sequel_test.rb +8 -2
- data/test/unit/machine_test.rb +87 -9
- data/test/unit/node_collection_test.rb +129 -12
- data/test/unit/state_collection_test.rb +4 -0
- data/test/unit/state_test.rb +2 -2
- metadata +30 -24
- data/README.rdoc +0 -844
@@ -24,6 +24,7 @@ module StateMachine
|
|
24
24
|
@nodes = []
|
25
25
|
@indices = Array(options[:index]).inject({}) {|indices, attribute| indices[attribute] = {}; indices}
|
26
26
|
@default_index = Array(options[:index]).first
|
27
|
+
@contexts = []
|
27
28
|
end
|
28
29
|
|
29
30
|
# Creates a copy of this collection such that modifications don't affect
|
@@ -32,9 +33,15 @@ module StateMachine
|
|
32
33
|
super
|
33
34
|
|
34
35
|
nodes = @nodes
|
36
|
+
contexts = @contexts
|
35
37
|
@nodes = []
|
38
|
+
@contexts = []
|
36
39
|
@indices = @indices.inject({}) {|indices, (name, index)| indices[name] = {}; indices}
|
40
|
+
|
41
|
+
# Add nodes *prior* to copying over the contexts so that they don't get
|
42
|
+
# evaluated multiple times
|
37
43
|
concat(nodes.map {|n| n.dup})
|
44
|
+
@contexts = contexts.dup
|
38
45
|
end
|
39
46
|
|
40
47
|
# Changes the current machine associated with the collection. In turn, this
|
@@ -54,11 +61,26 @@ module StateMachine
|
|
54
61
|
index(index_name).keys
|
55
62
|
end
|
56
63
|
|
64
|
+
# Tracks a context that should be evaluated for any nodes that get added
|
65
|
+
# which match the given set of nodes. Matchers can be used so that the
|
66
|
+
# context can get added once and evaluated after multiple adds.
|
67
|
+
def context(nodes, &block)
|
68
|
+
nodes = nodes.first.is_a?(Matcher) ? nodes.first : WhitelistMatcher.new(nodes)
|
69
|
+
@contexts << context = {:nodes => nodes, :block => block}
|
70
|
+
|
71
|
+
# Evaluate the new context for existing nodes
|
72
|
+
each {|node| eval_context(context, node)}
|
73
|
+
|
74
|
+
context
|
75
|
+
end
|
76
|
+
|
57
77
|
# Adds a new node to the collection. By doing so, this will also add it to
|
58
|
-
# the configured indices.
|
78
|
+
# the configured indices. This will also evaluate any existings contexts
|
79
|
+
# that match the new node.
|
59
80
|
def <<(node)
|
60
81
|
@nodes << node
|
61
82
|
@indices.each {|attribute, index| index[value(node, attribute)] = node}
|
83
|
+
@contexts.each {|context| eval_context(context, node)}
|
62
84
|
self
|
63
85
|
end
|
64
86
|
|
@@ -122,7 +144,12 @@ module StateMachine
|
|
122
144
|
#
|
123
145
|
# If the key cannot be found, then nil will be returned.
|
124
146
|
def [](key, index_name = @default_index)
|
125
|
-
index(index_name)
|
147
|
+
index = self.index(index_name)
|
148
|
+
if index.include?(key)
|
149
|
+
index[key]
|
150
|
+
elsif @indices.include?(:"#{index_name}_to_s")
|
151
|
+
self[key.to_s, :"#{index_name}_to_s"]
|
152
|
+
end
|
126
153
|
end
|
127
154
|
|
128
155
|
# Gets the node indexed by the given key. By default, this will look up the
|
@@ -141,7 +168,7 @@ module StateMachine
|
|
141
168
|
self[key, index_name] || raise(IndexError, "#{key.inspect} is an invalid #{index_name}")
|
142
169
|
end
|
143
170
|
|
144
|
-
|
171
|
+
protected
|
145
172
|
# Gets the given index. If the index does not exist, then an ArgumentError
|
146
173
|
# is raised.
|
147
174
|
def index(name)
|
@@ -153,5 +180,11 @@ module StateMachine
|
|
153
180
|
def value(node, attribute)
|
154
181
|
node.send(attribute)
|
155
182
|
end
|
183
|
+
|
184
|
+
# Evaluates the given context for a particular node. This will only
|
185
|
+
# evaluate the context if the node matches.
|
186
|
+
def eval_context(context, node)
|
187
|
+
node.context(&context[:block]) if context[:nodes].matches?(node.name)
|
188
|
+
end
|
156
189
|
end
|
157
190
|
end
|
data/lib/state_machine/state.rb
CHANGED
@@ -96,6 +96,11 @@ module StateMachine
|
|
96
96
|
@methods = methods.dup
|
97
97
|
end
|
98
98
|
|
99
|
+
# Converts the name of this state to a string
|
100
|
+
def name_to_s
|
101
|
+
name.to_s
|
102
|
+
end
|
103
|
+
|
99
104
|
# Determines whether there are any states that can be transitioned to from
|
100
105
|
# this state. If there are none, then this state is considered *final*.
|
101
106
|
# Any objects in a final state will remain so forever given the current
|
@@ -179,9 +184,7 @@ module StateMachine
|
|
179
184
|
# This can be called multiple times. Each time a new context is created,
|
180
185
|
# a new module will be included in the owner class.
|
181
186
|
def context(&block)
|
182
|
-
owner_class = machine.owner_class
|
183
187
|
machine_name = machine.name
|
184
|
-
name = self.name
|
185
188
|
|
186
189
|
# Evaluate the method definitions
|
187
190
|
context = StateContext.new(self)
|
@@ -199,7 +202,7 @@ module StateMachine
|
|
199
202
|
|
200
203
|
# Include the context so that it can be bound to the owner class (the
|
201
204
|
# context is considered an ancestor, so it's allowed to be bound)
|
202
|
-
owner_class.class_eval { include context }
|
205
|
+
machine.owner_class.class_eval { include context }
|
203
206
|
|
204
207
|
context
|
205
208
|
end
|
@@ -4,7 +4,7 @@ module StateMachine
|
|
4
4
|
# Represents a collection of states in a state machine
|
5
5
|
class StateCollection < NodeCollection
|
6
6
|
def initialize(machine) #:nodoc:
|
7
|
-
super(machine, :index => [:name, :qualified_name, :value])
|
7
|
+
super(machine, :index => [:name, :name_to_s, :qualified_name, :value])
|
8
8
|
end
|
9
9
|
|
10
10
|
# Determines whether the given object is in a specific state. If the
|
data/lib/tasks/state_machine.rb
CHANGED
@@ -1,9 +1,19 @@
|
|
1
1
|
namespace :state_machine do
|
2
|
-
desc 'Draws
|
2
|
+
desc 'Draws state machines using GraphViz (options: CLASS=User,Vehicle; FILE=user.rb,vehicle.rb [not required in Rails / Merb]; FONT=Arial; FORMAT=png; ORIENTATION=portrait'
|
3
3
|
task :draw do
|
4
|
+
# Build drawing options
|
5
|
+
options = {}
|
6
|
+
options[:file] = ENV['FILE'] if ENV['FILE']
|
7
|
+
options[:path] = ENV['TARGET'] if ENV['TARGET']
|
8
|
+
options[:format] = ENV['FORMAT'] if ENV['FORMAT']
|
9
|
+
options[:font] = ENV['FONT'] if ENV['FONT']
|
10
|
+
options[:orientation] = ENV['ORIENTATION'] if ENV['ORIENTATION']
|
11
|
+
|
4
12
|
if defined?(Rails)
|
13
|
+
puts "Files are automatically loaded in Rails; ignoring FILE option" if options.delete(:file)
|
5
14
|
Rake::Task['environment'].invoke
|
6
15
|
elsif defined?(Merb)
|
16
|
+
puts "Files are automatically loaded in Merb; ignoring FILE option" if options.delete(:file)
|
7
17
|
Rake::Task['merb_env'].invoke
|
8
18
|
|
9
19
|
# Fix ruby-graphviz being incompatible with Merb's process title
|
@@ -14,14 +24,6 @@ namespace :state_machine do
|
|
14
24
|
require 'state_machine'
|
15
25
|
end
|
16
26
|
|
17
|
-
# Build drawing options
|
18
|
-
options = {}
|
19
|
-
options[:file] = ENV['FILE'] if ENV['FILE']
|
20
|
-
options[:path] = ENV['TARGET'] if ENV['TARGET']
|
21
|
-
options[:format] = ENV['FORMAT'] if ENV['FORMAT']
|
22
|
-
options[:font] = ENV['FONT'] if ENV['FONT']
|
23
|
-
options[:orientation] = ENV['ORIENTATION'] if ENV['ORIENTATION']
|
24
|
-
|
25
27
|
StateMachine::Machine.draw(ENV['CLASS'], options)
|
26
28
|
end
|
27
29
|
end
|
data/state_machine.gemspec
CHANGED
@@ -12,11 +12,10 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.files = `git ls-files`.split("\n")
|
14
14
|
s.test_files = `git ls-files -- test/*`.split("\n")
|
15
|
-
s.rdoc_options = %w(--line-numbers --inline-source --title state_machine --main README.
|
16
|
-
s.extra_rdoc_files = %w(README.
|
15
|
+
s.rdoc_options = %w(--line-numbers --inline-source --title state_machine --main README.md)
|
16
|
+
s.extra_rdoc_files = %w(README.md CHANGELOG.md LICENSE)
|
17
17
|
|
18
18
|
s.add_development_dependency("rake")
|
19
19
|
s.add_development_dependency("rcov")
|
20
20
|
s.add_development_dependency("appraisal", "~> 0.3.8")
|
21
|
-
s.add_development_dependency("ruby-graphviz", "~> 1.0")
|
22
21
|
end
|
@@ -74,6 +74,10 @@ class Vehicle < ModelBase
|
|
74
74
|
vehicle.time_elapsed = Time.now - time
|
75
75
|
end
|
76
76
|
|
77
|
+
event all do
|
78
|
+
transition :locked => :parked
|
79
|
+
end
|
80
|
+
|
77
81
|
event :park do
|
78
82
|
transition [:idling, :first_gear] => :parked
|
79
83
|
end
|
@@ -191,7 +195,7 @@ end
|
|
191
195
|
class TrafficLight
|
192
196
|
state_machine :initial => :stop do
|
193
197
|
event :cycle do
|
194
|
-
transition :stop => :proceed, :proceed=> :caution, :caution => :stop
|
198
|
+
transition :stop => :proceed, :proceed => :caution, :caution => :stop
|
195
199
|
end
|
196
200
|
|
197
201
|
state :stop do
|
@@ -208,10 +212,20 @@ class TrafficLight
|
|
208
212
|
end
|
209
213
|
end
|
210
214
|
|
215
|
+
state all - :proceed do
|
216
|
+
def capture_violations?
|
217
|
+
true
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
211
221
|
state :proceed do
|
212
222
|
def color(transform)
|
213
223
|
'green'
|
214
224
|
end
|
225
|
+
|
226
|
+
def capture_violations?
|
227
|
+
false
|
228
|
+
end
|
215
229
|
end
|
216
230
|
|
217
231
|
state :caution do
|
@@ -713,6 +727,33 @@ class VehicleRepairedTest < Test::Unit::TestCase
|
|
713
727
|
end
|
714
728
|
end
|
715
729
|
|
730
|
+
class VehicleLockedTest < Test::Unit::TestCase
|
731
|
+
def setup
|
732
|
+
@vehicle = Vehicle.new
|
733
|
+
@vehicle.state = 'locked'
|
734
|
+
end
|
735
|
+
|
736
|
+
def test_should_be_parked_after_park
|
737
|
+
@vehicle.park
|
738
|
+
assert @vehicle.parked?
|
739
|
+
end
|
740
|
+
|
741
|
+
def test_should_be_parked_after_ignite
|
742
|
+
@vehicle.ignite
|
743
|
+
assert @vehicle.parked?
|
744
|
+
end
|
745
|
+
|
746
|
+
def test_should_be_parked_after_shift_up
|
747
|
+
@vehicle.shift_up
|
748
|
+
assert @vehicle.parked?
|
749
|
+
end
|
750
|
+
|
751
|
+
def test_should_be_parked_after_shift_down
|
752
|
+
@vehicle.shift_down
|
753
|
+
assert @vehicle.parked?
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
716
757
|
class VehicleWithParallelEventsTest < Test::Unit::TestCase
|
717
758
|
def setup
|
718
759
|
@vehicle = Vehicle.new
|
@@ -968,6 +1009,10 @@ class TrafficLightStopTest < Test::Unit::TestCase
|
|
968
1009
|
color = @light.color {|value| value.upcase!}
|
969
1010
|
assert_equal 'RED', color
|
970
1011
|
end
|
1012
|
+
|
1013
|
+
def test_should_use_stop_capture_violations
|
1014
|
+
assert_equal true, @light.capture_violations?
|
1015
|
+
end
|
971
1016
|
end
|
972
1017
|
|
973
1018
|
class TrafficLightProceedTest < Test::Unit::TestCase
|
@@ -979,6 +1024,10 @@ class TrafficLightProceedTest < Test::Unit::TestCase
|
|
979
1024
|
def test_should_use_proceed_color
|
980
1025
|
assert_equal 'green', @light.color
|
981
1026
|
end
|
1027
|
+
|
1028
|
+
def test_should_use_proceed_capture_violations
|
1029
|
+
assert_equal false, @light.capture_violations?
|
1030
|
+
end
|
982
1031
|
end
|
983
1032
|
|
984
1033
|
class TrafficLightCautionTest < Test::Unit::TestCase
|
@@ -990,4 +1039,8 @@ class TrafficLightCautionTest < Test::Unit::TestCase
|
|
990
1039
|
def test_should_use_caution_color
|
991
1040
|
assert_equal 'yellow', @light.color
|
992
1041
|
end
|
1042
|
+
|
1043
|
+
def test_should_use_caution_capture_violations
|
1044
|
+
assert_equal true, @light.capture_violations?
|
1045
|
+
end
|
993
1046
|
end
|
@@ -40,6 +40,10 @@ class EventCollectionTest < Test::Unit::TestCase
|
|
40
40
|
assert_equal @open, @events[:enable]
|
41
41
|
end
|
42
42
|
|
43
|
+
def test_should_index_by_string_name
|
44
|
+
assert_equal @open, @events['enable']
|
45
|
+
end
|
46
|
+
|
43
47
|
def test_should_index_by_qualified_name
|
44
48
|
assert_equal @open, @events[:enable_alarm, :qualified_name]
|
45
49
|
end
|
data/test/unit/event_test.rb
CHANGED
@@ -173,7 +173,7 @@ class EventWithConflictingHelpersBeforeDefinitionTest < Test::Unit::TestCase
|
|
173
173
|
|
174
174
|
def test_should_output_warning
|
175
175
|
expected = %w(can_ignite? ignite_transition ignite ignite!).map do |method|
|
176
|
-
"Instance method \"#{method}\" is already defined in #{@superclass.to_s}, use generic helper instead.\n"
|
176
|
+
"Instance method \"#{method}\" is already defined in #{@superclass.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n"
|
177
177
|
end.join
|
178
178
|
|
179
179
|
assert_equal expected, $stderr.string
|
@@ -341,6 +341,20 @@ class EventWithNamespaceTest < Test::Unit::TestCase
|
|
341
341
|
end
|
342
342
|
end
|
343
343
|
|
344
|
+
class EventContextTest < Test::Unit::TestCase
|
345
|
+
def setup
|
346
|
+
@klass = Class.new
|
347
|
+
@machine = StateMachine::Machine.new(@klass)
|
348
|
+
@machine.events << @event = StateMachine::Event.new(@machine, :ignite, :human_name => 'start')
|
349
|
+
end
|
350
|
+
|
351
|
+
def test_should_evaluate_within_the_event
|
352
|
+
scope = nil
|
353
|
+
@event.context { scope = self }
|
354
|
+
assert_equal @event, scope
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
344
358
|
class EventTransitionsTest < Test::Unit::TestCase
|
345
359
|
def setup
|
346
360
|
@machine = StateMachine::Machine.new(Class.new)
|
@@ -568,6 +582,25 @@ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
|
|
568
582
|
assert_equal ['cannot transition via "start"'], @object.errors
|
569
583
|
end
|
570
584
|
|
585
|
+
def test_should_invalid_with_human_state_name_if_specified
|
586
|
+
klass = Class.new do
|
587
|
+
attr_accessor :errors
|
588
|
+
end
|
589
|
+
|
590
|
+
machine = StateMachine::Machine.new(klass, :integration => :custom, :messages => {:invalid_transition => 'cannot transition via "%s" from "%s"'})
|
591
|
+
parked, idling = machine.state :parked, :idling
|
592
|
+
parked.human_name = 'stopped'
|
593
|
+
|
594
|
+
machine.events << event = StateMachine::Event.new(machine, :ignite)
|
595
|
+
event.transition(:parked => :idling, :if => lambda {false})
|
596
|
+
|
597
|
+
object = @klass.new
|
598
|
+
object.state = 'parked'
|
599
|
+
|
600
|
+
event.fire(object)
|
601
|
+
assert_equal ['cannot transition via "ignite" from "stopped"'], object.errors
|
602
|
+
end
|
603
|
+
|
571
604
|
def test_should_reset_existing_error
|
572
605
|
@object.errors = ['invalid']
|
573
606
|
|
@@ -798,6 +798,54 @@ module ActiveModelTest
|
|
798
798
|
@transition.perform
|
799
799
|
assert_equal [instance], instance.notifications
|
800
800
|
end
|
801
|
+
|
802
|
+
def test_should_support_nil_from_states
|
803
|
+
callbacks = [
|
804
|
+
:before_ignite_from_nil_to_idling,
|
805
|
+
:before_ignite_from_nil,
|
806
|
+
:before_transition_state_from_nil_to_idling,
|
807
|
+
:before_transition_state_from_nil
|
808
|
+
]
|
809
|
+
|
810
|
+
notified = false
|
811
|
+
observer = new_observer(@model) do
|
812
|
+
callbacks.each do |callback|
|
813
|
+
define_method(callback) do |*args|
|
814
|
+
notifications << callback
|
815
|
+
end
|
816
|
+
end
|
817
|
+
end
|
818
|
+
|
819
|
+
instance = observer.instance
|
820
|
+
|
821
|
+
transition = StateMachine::Transition.new(@record, @machine, :ignite, nil, :idling)
|
822
|
+
transition.perform
|
823
|
+
assert_equal callbacks, instance.notifications
|
824
|
+
end
|
825
|
+
|
826
|
+
def test_should_support_nil_to_states
|
827
|
+
callbacks = [
|
828
|
+
:before_ignite_from_parked_to_nil,
|
829
|
+
:before_ignite_to_nil,
|
830
|
+
:before_transition_state_from_parked_to_nil,
|
831
|
+
:before_transition_state_to_nil
|
832
|
+
]
|
833
|
+
|
834
|
+
notified = false
|
835
|
+
observer = new_observer(@model) do
|
836
|
+
callbacks.each do |callback|
|
837
|
+
define_method(callback) do |*args|
|
838
|
+
notifications << callback
|
839
|
+
end
|
840
|
+
end
|
841
|
+
end
|
842
|
+
|
843
|
+
instance = observer.instance
|
844
|
+
|
845
|
+
transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, nil)
|
846
|
+
transition.perform
|
847
|
+
assert_equal callbacks, instance.notifications
|
848
|
+
end
|
801
849
|
end
|
802
850
|
|
803
851
|
class MachineWithNamespacedObserversTest < BaseTestCase
|
@@ -994,6 +1042,17 @@ module ActiveModelTest
|
|
994
1042
|
assert_equal 'shutdown', machine.state(:parked).human_name
|
995
1043
|
end
|
996
1044
|
|
1045
|
+
def test_should_allow_customized_state_key_scoped_to_class
|
1046
|
+
I18n.backend.store_translations(:en, {
|
1047
|
+
:activemodel => {:state_machines => {:'active_model_test/foo' => {:states => {:parked => 'shutdown'}}}}
|
1048
|
+
})
|
1049
|
+
|
1050
|
+
machine = StateMachine::Machine.new(@model)
|
1051
|
+
machine.state :parked
|
1052
|
+
|
1053
|
+
assert_equal 'shutdown', machine.state(:parked).human_name
|
1054
|
+
end
|
1055
|
+
|
997
1056
|
def test_should_allow_customized_state_key_scoped_to_machine
|
998
1057
|
I18n.backend.store_translations(:en, {
|
999
1058
|
:activemodel => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
|
@@ -1016,6 +1075,16 @@ module ActiveModelTest
|
|
1016
1075
|
assert_equal 'shutdown', machine.state(:parked).human_name
|
1017
1076
|
end
|
1018
1077
|
|
1078
|
+
def test_should_support_nil_state_key
|
1079
|
+
I18n.backend.store_translations(:en, {
|
1080
|
+
:activemodel => {:state_machines => {:states => {:nil => 'empty'}}}
|
1081
|
+
})
|
1082
|
+
|
1083
|
+
machine = StateMachine::Machine.new(@model)
|
1084
|
+
|
1085
|
+
assert_equal 'empty', machine.state(nil).human_name
|
1086
|
+
end
|
1087
|
+
|
1019
1088
|
def test_should_allow_customized_event_key_scoped_to_class_and_machine
|
1020
1089
|
I18n.backend.store_translations(:en, {
|
1021
1090
|
:activemodel => {:state_machines => {:'active_model_test/foo' => {:state => {:events => {:park => 'stop'}}}}}
|
@@ -1027,6 +1096,17 @@ module ActiveModelTest
|
|
1027
1096
|
assert_equal 'stop', machine.event(:park).human_name
|
1028
1097
|
end
|
1029
1098
|
|
1099
|
+
def test_should_allow_customized_event_key_scoped_to_class
|
1100
|
+
I18n.backend.store_translations(:en, {
|
1101
|
+
:activemodel => {:state_machines => {:'active_model_test/foo' => {:events => {:park => 'stop'}}}}
|
1102
|
+
})
|
1103
|
+
|
1104
|
+
machine = StateMachine::Machine.new(@model)
|
1105
|
+
machine.event :park
|
1106
|
+
|
1107
|
+
assert_equal 'stop', machine.event(:park).human_name
|
1108
|
+
end
|
1109
|
+
|
1030
1110
|
def test_should_allow_customized_event_key_scoped_to_machine
|
1031
1111
|
I18n.backend.store_translations(:en, {
|
1032
1112
|
:activemodel => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
|