state_machine 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +10 -0
- data/README.rdoc +8 -0
- data/Rakefile +1 -1
- data/examples/merb-rest/view_edit.html.erb +2 -2
- data/examples/merb-rest/view_index.html.erb +2 -2
- data/examples/merb-rest/view_show.html.erb +2 -2
- data/examples/rails-rest/view_edit.html.erb +2 -2
- data/examples/rails-rest/view_index.html.erb +2 -2
- data/examples/rails-rest/view_show.html.erb +2 -2
- data/lib/state_machine.rb +34 -0
- data/lib/state_machine/event.rb +17 -2
- data/lib/state_machine/event_collection.rb +1 -1
- data/lib/state_machine/integrations/active_model.rb +39 -15
- data/lib/state_machine/integrations/active_model/locale.rb +2 -2
- data/lib/state_machine/integrations/active_record.rb +15 -3
- data/lib/state_machine/integrations/active_record/locale.rb +16 -0
- data/lib/state_machine/integrations/mongo_mapper.rb +16 -2
- data/lib/state_machine/machine.rb +53 -10
- data/lib/state_machine/machine_collection.rb +1 -1
- data/lib/state_machine/state.rb +12 -1
- data/lib/state_machine/transition.rb +50 -34
- data/test/files/en.yml +9 -0
- data/test/{classes → files}/switch.rb +0 -0
- data/test/functional/state_machine_test.rb +9 -0
- data/test/unit/event_collection_test.rb +5 -7
- data/test/unit/event_test.rb +51 -0
- data/test/unit/integrations/active_model_test.rb +80 -33
- data/test/unit/integrations/active_record_test.rb +89 -30
- data/test/unit/integrations/data_mapper_test.rb +25 -1
- data/test/unit/integrations/mongo_mapper_test.rb +40 -7
- data/test/unit/integrations/sequel_test.rb +25 -1
- data/test/unit/machine_collection_test.rb +1 -1
- data/test/unit/machine_test.rb +123 -4
- data/test/unit/state_test.rb +53 -0
- data/test/unit/transition_test.rb +20 -0
- metadata +4 -3
@@ -612,6 +612,9 @@ module StateMachine
|
|
612
612
|
# * <tt>:if</tt> - Determines whether an object's value matches the state
|
613
613
|
# (e.g. :value => lambda {Time.now}, :if => lambda {|state| !state.nil?}).
|
614
614
|
# By default, the configured value is matched.
|
615
|
+
# * <tt>:human_name</tt> - The human-readable version of this state's name.
|
616
|
+
# By default, this is either defined by the integration or stringifies the
|
617
|
+
# name and converts underscores to spaces.
|
615
618
|
#
|
616
619
|
# == Customizing the stored value
|
617
620
|
#
|
@@ -846,7 +849,7 @@ module StateMachine
|
|
846
849
|
# options hash which contains at least <tt>:if</tt> condition support.
|
847
850
|
def state(*names, &block)
|
848
851
|
options = names.last.is_a?(Hash) ? names.pop : {}
|
849
|
-
assert_valid_keys(options, :value, :cache, :if)
|
852
|
+
assert_valid_keys(options, :value, :cache, :if, :human_name)
|
850
853
|
|
851
854
|
states = add_states(names)
|
852
855
|
states.each do |state|
|
@@ -855,6 +858,7 @@ module StateMachine
|
|
855
858
|
self.states.update(state)
|
856
859
|
end
|
857
860
|
|
861
|
+
state.human_name = options[:human_name] if options.include?(:human_name)
|
858
862
|
state.cache = options[:cache] if options.include?(:cache)
|
859
863
|
state.matcher = options[:if] if options.include?(:if)
|
860
864
|
state.context(&block) if block_given?
|
@@ -907,6 +911,11 @@ module StateMachine
|
|
907
911
|
# This method is also aliased as +on+ for improved compatibility with
|
908
912
|
# using a domain-specific language.
|
909
913
|
#
|
914
|
+
# Configuration options:
|
915
|
+
# * <tt>:human_name</tt> - The human-readable version of this event's name.
|
916
|
+
# By default, this is either defined by the integration or stringifies the
|
917
|
+
# name and converts underscores to spaces.
|
918
|
+
#
|
910
919
|
# == Instance methods
|
911
920
|
#
|
912
921
|
# The following instance methods are generated when a new event is defined
|
@@ -1014,10 +1023,12 @@ module StateMachine
|
|
1014
1023
|
# end
|
1015
1024
|
# end
|
1016
1025
|
def event(*names, &block)
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1026
|
+
options = names.last.is_a?(Hash) ? names.pop : {}
|
1027
|
+
assert_valid_keys(options, :human_name)
|
1028
|
+
|
1029
|
+
events = add_events(names)
|
1030
|
+
events.each do |event|
|
1031
|
+
event.human_name = options[:human_name] if options.include?(:human_name)
|
1021
1032
|
|
1022
1033
|
if block_given?
|
1023
1034
|
event.instance_eval(&block)
|
@@ -1415,11 +1426,7 @@ module StateMachine
|
|
1415
1426
|
define_state_predicate
|
1416
1427
|
define_event_helpers
|
1417
1428
|
define_action_helpers if action
|
1418
|
-
|
1419
|
-
# Gets the state name for the current value
|
1420
|
-
define_instance_method(attribute(:name)) do |machine, object|
|
1421
|
-
machine.states.match!(object).name
|
1422
|
-
end
|
1429
|
+
define_name_helpers
|
1423
1430
|
end
|
1424
1431
|
|
1425
1432
|
# Defines the initial values for state machine attributes. Static values
|
@@ -1514,6 +1521,30 @@ module StateMachine
|
|
1514
1521
|
end
|
1515
1522
|
end
|
1516
1523
|
|
1524
|
+
# Adds helper methods for accessing naming information about states and
|
1525
|
+
# events on the owner class
|
1526
|
+
def define_name_helpers
|
1527
|
+
# Gets the humanized version of a state
|
1528
|
+
define_class_method("human_#{attribute(:name)}") do |machine, klass, state|
|
1529
|
+
machine.states.fetch(state).human_name(klass)
|
1530
|
+
end
|
1531
|
+
|
1532
|
+
# Gets the humanized version of an event
|
1533
|
+
define_class_method("human_#{attribute(:event_name)}") do |machine, klass, event|
|
1534
|
+
machine.events.fetch(event).human_name(klass)
|
1535
|
+
end
|
1536
|
+
|
1537
|
+
# Gets the state name for the current value
|
1538
|
+
define_instance_method(attribute(:name)) do |machine, object|
|
1539
|
+
machine.states.match!(object).name
|
1540
|
+
end
|
1541
|
+
|
1542
|
+
# Gets the human state name for the current value
|
1543
|
+
define_instance_method("human_#{attribute(:name)}") do |machine, object|
|
1544
|
+
machine.states.match!(object).human_name(object.class)
|
1545
|
+
end
|
1546
|
+
end
|
1547
|
+
|
1517
1548
|
# Defines the with/without scope helpers for this attribute. Both the
|
1518
1549
|
# singular and plural versions of the attribute are defined for each
|
1519
1550
|
# scope helper. A custom plural can be specified if it cannot be
|
@@ -1586,5 +1617,17 @@ module StateMachine
|
|
1586
1617
|
state
|
1587
1618
|
end
|
1588
1619
|
end
|
1620
|
+
|
1621
|
+
# Tracks the given set of events in the list of all known events for
|
1622
|
+
# this machine
|
1623
|
+
def add_events(new_events)
|
1624
|
+
new_events.map do |new_event|
|
1625
|
+
unless event = events[new_event]
|
1626
|
+
events << event = Event.new(self, new_event)
|
1627
|
+
end
|
1628
|
+
|
1629
|
+
event
|
1630
|
+
end
|
1631
|
+
end
|
1589
1632
|
end
|
1590
1633
|
end
|
@@ -33,7 +33,7 @@ module StateMachine
|
|
33
33
|
# Get the transition that will be performed for the event
|
34
34
|
unless transition = event.transition_for(object)
|
35
35
|
machine = event.machine
|
36
|
-
machine.invalidate(object, :state, :invalid_transition, [[:event,
|
36
|
+
machine.invalidate(object, :state, :invalid_transition, [[:event, event.human_name]])
|
37
37
|
end
|
38
38
|
|
39
39
|
transition
|
data/lib/state_machine/state.rb
CHANGED
@@ -23,6 +23,9 @@ module StateMachine
|
|
23
23
|
# namespace
|
24
24
|
attr_reader :qualified_name
|
25
25
|
|
26
|
+
# The human-readable name for the state
|
27
|
+
attr_writer :human_name
|
28
|
+
|
26
29
|
# The value that is written to a machine's attribute when an object
|
27
30
|
# transitions into this state
|
28
31
|
attr_writer :value
|
@@ -56,12 +59,14 @@ module StateMachine
|
|
56
59
|
# * <tt>:if</tt> - Determines whether a value matches this state
|
57
60
|
# (e.g. :value => lambda {Time.now}, :if => lambda {|state| !state.nil?}).
|
58
61
|
# By default, the configured value is matched.
|
62
|
+
# * <tt>:human_name</tt> - The human-readable version of this state's name
|
59
63
|
def initialize(machine, name, options = {}) #:nodoc:
|
60
|
-
assert_valid_keys(options, :initial, :value, :cache, :if)
|
64
|
+
assert_valid_keys(options, :initial, :value, :cache, :if, :human_name)
|
61
65
|
|
62
66
|
@machine = machine
|
63
67
|
@name = name
|
64
68
|
@qualified_name = name && machine.namespace ? :"#{machine.namespace}_#{name}" : name
|
69
|
+
@human_name = options[:human_name] || (@name ? @name.to_s.tr('_', ' ') : 'nil')
|
65
70
|
@value = options.include?(:value) ? options[:value] : name && name.to_s
|
66
71
|
@cache = options[:cache]
|
67
72
|
@matcher = options[:if]
|
@@ -92,6 +97,12 @@ module StateMachine
|
|
92
97
|
end
|
93
98
|
end
|
94
99
|
|
100
|
+
# Transforms the state name into a more human-readable format, such as
|
101
|
+
# "first gear" instead of "first_gear"
|
102
|
+
def human_name(klass = @machine.owner_class)
|
103
|
+
@human_name.is_a?(Proc) ? @human_name.call(self, klass) : @human_name
|
104
|
+
end
|
105
|
+
|
95
106
|
# Generates a human-readable description of this state's name / value:
|
96
107
|
#
|
97
108
|
# For example,
|
@@ -18,30 +18,12 @@ module StateMachine
|
|
18
18
|
# The state machine for which this transition is defined
|
19
19
|
attr_reader :machine
|
20
20
|
|
21
|
-
# The event that triggered the transition
|
22
|
-
attr_reader :event
|
23
|
-
|
24
|
-
# The fully-qualified name of the event that triggered the transition
|
25
|
-
attr_reader :qualified_event
|
26
|
-
|
27
21
|
# The original state value *before* the transition
|
28
22
|
attr_reader :from
|
29
23
|
|
30
|
-
# The original state name *before* the transition
|
31
|
-
attr_reader :from_name
|
32
|
-
|
33
|
-
# The original fully-qualified state name *before* transition
|
34
|
-
attr_reader :qualified_from_name
|
35
|
-
|
36
24
|
# The new state value *after* the transition
|
37
25
|
attr_reader :to
|
38
26
|
|
39
|
-
# The new state name *after* the transition
|
40
|
-
attr_reader :to_name
|
41
|
-
|
42
|
-
# The new fully-qualified state name *after* the transition
|
43
|
-
attr_reader :qualified_to_name
|
44
|
-
|
45
27
|
# The arguments passed in to the event that triggered the transition
|
46
28
|
# (does not include the +run_action+ boolean argument if specified)
|
47
29
|
attr_accessor :args
|
@@ -59,22 +41,11 @@ module StateMachine
|
|
59
41
|
@args = []
|
60
42
|
@transient = false
|
61
43
|
|
62
|
-
|
63
|
-
|
64
|
-
@
|
65
|
-
@
|
66
|
-
|
67
|
-
# From state information
|
68
|
-
from_state = machine.states.fetch(from_name)
|
69
|
-
@from = read_state ? machine.read(object, :state) : from_state.value
|
70
|
-
@from_name = from_state.name
|
71
|
-
@qualified_from_name = from_state.qualified_name
|
72
|
-
|
73
|
-
# To state information
|
74
|
-
to_state = machine.states.fetch(to_name)
|
75
|
-
@to = to_state.value
|
76
|
-
@to_name = to_state.name
|
77
|
-
@qualified_to_name = to_state.qualified_name
|
44
|
+
@event = machine.events.fetch(event)
|
45
|
+
@from_state = machine.states.fetch(from_name)
|
46
|
+
@from = read_state ? machine.read(object, :state) : @from_state.value
|
47
|
+
@to_state = machine.states.fetch(to_name)
|
48
|
+
@to = @to_state.value
|
78
49
|
|
79
50
|
reset
|
80
51
|
end
|
@@ -89,6 +60,51 @@ module StateMachine
|
|
89
60
|
machine.action
|
90
61
|
end
|
91
62
|
|
63
|
+
# The event that triggered the transition
|
64
|
+
def event
|
65
|
+
@event.name
|
66
|
+
end
|
67
|
+
|
68
|
+
# The fully-qualified name of the event that triggered the transition
|
69
|
+
def qualified_event
|
70
|
+
@event.qualified_name
|
71
|
+
end
|
72
|
+
|
73
|
+
# The human-readable name of the event that triggered the transition
|
74
|
+
def human_event
|
75
|
+
@event.human_name(@object.class)
|
76
|
+
end
|
77
|
+
|
78
|
+
# The state name *before* the transition
|
79
|
+
def from_name
|
80
|
+
@from_state.name
|
81
|
+
end
|
82
|
+
|
83
|
+
# The fully-qualified state name *before* the transition
|
84
|
+
def qualified_from_name
|
85
|
+
@from_state.qualified_name
|
86
|
+
end
|
87
|
+
|
88
|
+
# The human-readable state name *before* the transition
|
89
|
+
def human_from_name
|
90
|
+
@from_state.human_name(@object.class)
|
91
|
+
end
|
92
|
+
|
93
|
+
# The new state name *after* the transition
|
94
|
+
def to_name
|
95
|
+
@to_state.name
|
96
|
+
end
|
97
|
+
|
98
|
+
# The new fully-qualified state name *after* the transition
|
99
|
+
def qualified_to_name
|
100
|
+
@to_state.qualified_name
|
101
|
+
end
|
102
|
+
|
103
|
+
# The new human-readable state name *after* the transition
|
104
|
+
def human_to_name
|
105
|
+
@to_state.human_name(@object.class)
|
106
|
+
end
|
107
|
+
|
92
108
|
# Does this transition represent a loopback (i.e. the from and to state
|
93
109
|
# are the same)
|
94
110
|
#
|
data/test/files/en.yml
ADDED
File without changes
|
@@ -234,6 +234,14 @@ class VehicleTest < Test::Unit::TestCase
|
|
234
234
|
def test_should_not_allow_access_to_subclass_events
|
235
235
|
assert !@vehicle.respond_to?(:reverse)
|
236
236
|
end
|
237
|
+
|
238
|
+
def test_should_have_human_state_names
|
239
|
+
assert_equal 'parked', Vehicle.human_state_name(:parked)
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_should_have_human_state_event_names
|
243
|
+
assert_equal 'park', Vehicle.human_state_event_name(:park)
|
244
|
+
end
|
237
245
|
end
|
238
246
|
|
239
247
|
class VehicleUnsavedTest < Test::Unit::TestCase
|
@@ -258,6 +266,7 @@ class VehicleUnsavedTest < Test::Unit::TestCase
|
|
258
266
|
assert @vehicle.parked?
|
259
267
|
assert @vehicle.state?(:parked)
|
260
268
|
assert_equal :parked, @vehicle.state_name
|
269
|
+
assert_equal 'parked', @vehicle.human_state_name
|
261
270
|
end
|
262
271
|
|
263
272
|
def test_should_not_be_idling
|
@@ -250,7 +250,7 @@ class EventCollectionWithValidationsTest < Test::Unit::TestCase
|
|
250
250
|
@events = StateMachine::EventCollection.new(@machine)
|
251
251
|
|
252
252
|
@machine.event :ignite
|
253
|
-
@machine.state :parked, :idling
|
253
|
+
@parked, @idling = @machine.state :parked, :idling
|
254
254
|
@events << @ignite = StateMachine::Event.new(@machine, :ignite)
|
255
255
|
|
256
256
|
@object = @klass.new
|
@@ -271,15 +271,13 @@ class EventCollectionWithValidationsTest < Test::Unit::TestCase
|
|
271
271
|
assert_equal ['cannot transition when idling'], @object.errors
|
272
272
|
end
|
273
273
|
|
274
|
-
def
|
275
|
-
|
276
|
-
@
|
277
|
-
|
278
|
-
@object.state = nil
|
274
|
+
def test_should_invalidate_with_human_name_if_invalid_event_specified
|
275
|
+
@idling.human_name = 'waiting'
|
276
|
+
@object.state = 'idling'
|
279
277
|
@object.state_event = 'ignite'
|
280
278
|
@events.attribute_transition_for(@object, true)
|
281
279
|
|
282
|
-
assert_equal ['cannot transition when
|
280
|
+
assert_equal ['cannot transition when waiting'], @object.errors
|
283
281
|
end
|
284
282
|
|
285
283
|
def test_should_not_invalidate_event_can_be_fired
|
data/test/unit/event_test.rb
CHANGED
@@ -21,6 +21,10 @@ class EventByDefaultTest < Test::Unit::TestCase
|
|
21
21
|
assert_equal :ignite, @event.qualified_name
|
22
22
|
end
|
23
23
|
|
24
|
+
def test_should_have_a_human_name
|
25
|
+
assert_equal 'ignite', @event.human_name
|
26
|
+
end
|
27
|
+
|
24
28
|
def test_should_not_have_any_guards
|
25
29
|
assert @event.guards.empty?
|
26
30
|
end
|
@@ -67,6 +71,11 @@ class EventTest < Test::Unit::TestCase
|
|
67
71
|
assert_equal new_machine, @event.machine
|
68
72
|
end
|
69
73
|
|
74
|
+
def test_should_allow_changing_human_name
|
75
|
+
@event.human_name = 'Stop'
|
76
|
+
assert_equal 'Stop', @event.human_name
|
77
|
+
end
|
78
|
+
|
70
79
|
def test_should_provide_matcher_helpers_during_initialization
|
71
80
|
matchers = []
|
72
81
|
|
@@ -82,6 +91,42 @@ class EventTest < Test::Unit::TestCase
|
|
82
91
|
end
|
83
92
|
end
|
84
93
|
|
94
|
+
class EventWithHumanNameTest < Test::Unit::TestCase
|
95
|
+
def setup
|
96
|
+
@klass = Class.new
|
97
|
+
@machine = StateMachine::Machine.new(@klass)
|
98
|
+
@event = StateMachine::Event.new(@machine, :ignite, :human_name => 'start')
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_should_use_custom_human_name
|
102
|
+
assert_equal 'start', @event.human_name
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class EventWithDynamicHumanNameTest < Test::Unit::TestCase
|
107
|
+
def setup
|
108
|
+
@klass = Class.new
|
109
|
+
@machine = StateMachine::Machine.new(@klass)
|
110
|
+
@event = StateMachine::Event.new(@machine, :ignite, :human_name => lambda {|event, object| ['start', object]})
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_should_use_custom_human_name
|
114
|
+
human_name, klass = @event.human_name
|
115
|
+
assert_equal 'start', human_name
|
116
|
+
assert_equal @klass, klass
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_should_allow_custom_class_to_be_passed_through
|
120
|
+
human_name, klass = @event.human_name(1)
|
121
|
+
assert_equal 'start', human_name
|
122
|
+
assert_equal 1, klass
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_should_not_cache_value
|
126
|
+
assert_not_same @event.human_name, @event.human_name
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
85
130
|
class EventWithConflictingHelpersTest < Test::Unit::TestCase
|
86
131
|
def setup
|
87
132
|
@klass = Class.new do
|
@@ -389,6 +434,12 @@ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
|
|
389
434
|
assert_equal ['cannot transition via "ignite"'], @object.errors
|
390
435
|
end
|
391
436
|
|
437
|
+
def test_should_invalidate_with_human_event_name
|
438
|
+
@event.human_name = 'start'
|
439
|
+
@event.fire(@object)
|
440
|
+
assert_equal ['cannot transition via "start"'], @object.errors
|
441
|
+
end
|
442
|
+
|
392
443
|
def test_should_reset_existing_error
|
393
444
|
@object.errors = ['invalid']
|
394
445
|
|
@@ -119,6 +119,18 @@ module ActiveModelTest
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
class MachineWithStatesTest < BaseTestCase
|
123
|
+
def setup
|
124
|
+
@model = new_model
|
125
|
+
@machine = StateMachine::Machine.new(@model)
|
126
|
+
@machine.state :first_gear
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_should_humanize_name
|
130
|
+
assert_equal 'first gear', @machine.state(:first_gear).human_name
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
122
134
|
class MachineWithStaticInitialStateTest < BaseTestCase
|
123
135
|
def setup
|
124
136
|
@model = new_model
|
@@ -144,6 +156,18 @@ module ActiveModelTest
|
|
144
156
|
end
|
145
157
|
end
|
146
158
|
|
159
|
+
class MachineWithEventsTest < BaseTestCase
|
160
|
+
def setup
|
161
|
+
@model = new_model
|
162
|
+
@machine = StateMachine::Machine.new(@model)
|
163
|
+
@machine.event :shift_up
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_should_humanize_name
|
167
|
+
assert_equal 'shift up', @machine.event(:shift_up).human_name
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
147
171
|
class MachineWithModelStateAttributeTest < BaseTestCase
|
148
172
|
def setup
|
149
173
|
@model = new_model
|
@@ -523,7 +547,7 @@ module ActiveModelTest
|
|
523
547
|
I18n.backend = I18n::Backend::Simple.new if Object.const_defined?(:I18n)
|
524
548
|
@record.state = 'parked'
|
525
549
|
|
526
|
-
@machine.invalidate(@record, :state, :invalid_transition, [[:event,
|
550
|
+
@machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
|
527
551
|
assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
|
528
552
|
end
|
529
553
|
|
@@ -784,7 +808,7 @@ module ActiveModelTest
|
|
784
808
|
|
785
809
|
def test_should_use_defaults
|
786
810
|
I18n.backend.store_translations(:en, {
|
787
|
-
:activemodel => {:errors => {:messages => {:invalid_transition => 'cannot {
|
811
|
+
:activemodel => {:errors => {:messages => {:invalid_transition => 'cannot %{event}'}}}
|
788
812
|
})
|
789
813
|
|
790
814
|
machine = StateMachine::Machine.new(@model, :action => :save)
|
@@ -793,13 +817,13 @@ module ActiveModelTest
|
|
793
817
|
|
794
818
|
record = @model.new(:state => 'idling')
|
795
819
|
|
796
|
-
machine.invalidate(record, :state, :invalid_transition, [[:event,
|
820
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
|
797
821
|
assert_equal ['State cannot ignite'], record.errors.full_messages
|
798
822
|
end
|
799
823
|
|
800
824
|
def test_should_allow_customized_error_key
|
801
825
|
I18n.backend.store_translations(:en, {
|
802
|
-
:activemodel => {:errors => {:messages => {:bad_transition => 'cannot {
|
826
|
+
:activemodel => {:errors => {:messages => {:bad_transition => 'cannot %{event}'}}}
|
803
827
|
})
|
804
828
|
|
805
829
|
machine = StateMachine::Machine.new(@model, :action => :save, :messages => {:invalid_transition => :bad_transition})
|
@@ -808,17 +832,17 @@ module ActiveModelTest
|
|
808
832
|
record = @model.new
|
809
833
|
record.state = 'idling'
|
810
834
|
|
811
|
-
machine.invalidate(record, :state, :invalid_transition, [[:event,
|
835
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
|
812
836
|
assert_equal ['State cannot ignite'], record.errors.full_messages
|
813
837
|
end
|
814
838
|
|
815
839
|
def test_should_allow_customized_error_string
|
816
|
-
machine = StateMachine::Machine.new(@model, :action => :save, :messages => {:invalid_transition => 'cannot {
|
840
|
+
machine = StateMachine::Machine.new(@model, :action => :save, :messages => {:invalid_transition => 'cannot %{event}'})
|
817
841
|
machine.state :parked, :idling
|
818
842
|
|
819
843
|
record = @model.new(:state => 'idling')
|
820
844
|
|
821
|
-
machine.invalidate(record, :state, :invalid_transition, [[:event,
|
845
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
|
822
846
|
assert_equal ['State cannot ignite'], record.errors.full_messages
|
823
847
|
end
|
824
848
|
|
@@ -827,11 +851,10 @@ module ActiveModelTest
|
|
827
851
|
:activemodel => {:state_machines => {:'active_model_test/foo' => {:state => {:states => {:parked => 'shutdown'}}}}}
|
828
852
|
})
|
829
853
|
|
830
|
-
machine = StateMachine::Machine.new(@model
|
831
|
-
|
854
|
+
machine = StateMachine::Machine.new(@model)
|
855
|
+
machine.state :parked
|
832
856
|
|
833
|
-
|
834
|
-
assert_equal ['State event cannot transition when shutdown'], record.errors.full_messages
|
857
|
+
assert_equal 'shutdown', machine.state(:parked).human_name
|
835
858
|
end
|
836
859
|
|
837
860
|
def test_should_allow_customized_state_key_scoped_to_machine
|
@@ -839,11 +862,10 @@ module ActiveModelTest
|
|
839
862
|
:activemodel => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
|
840
863
|
})
|
841
864
|
|
842
|
-
machine = StateMachine::Machine.new(@model
|
843
|
-
|
865
|
+
machine = StateMachine::Machine.new(@model)
|
866
|
+
machine.state :parked
|
844
867
|
|
845
|
-
|
846
|
-
assert_equal ['State event cannot transition when shutdown'], record.errors.full_messages
|
868
|
+
assert_equal 'shutdown', machine.state(:parked).human_name
|
847
869
|
end
|
848
870
|
|
849
871
|
def test_should_allow_customized_state_key_unscoped
|
@@ -851,11 +873,10 @@ module ActiveModelTest
|
|
851
873
|
:activemodel => {:state_machines => {:states => {:parked => 'shutdown'}}}
|
852
874
|
})
|
853
875
|
|
854
|
-
machine = StateMachine::Machine.new(@model
|
855
|
-
|
876
|
+
machine = StateMachine::Machine.new(@model)
|
877
|
+
machine.state :parked
|
856
878
|
|
857
|
-
|
858
|
-
assert_equal ['State event cannot transition when shutdown'], record.errors.full_messages
|
879
|
+
assert_equal 'shutdown', machine.state(:parked).human_name
|
859
880
|
end
|
860
881
|
|
861
882
|
def test_should_allow_customized_event_key_scoped_to_class_and_machine
|
@@ -863,12 +884,10 @@ module ActiveModelTest
|
|
863
884
|
:activemodel => {:state_machines => {:'active_model_test/foo' => {:state => {:events => {:park => 'stop'}}}}}
|
864
885
|
})
|
865
886
|
|
866
|
-
machine = StateMachine::Machine.new(@model
|
887
|
+
machine = StateMachine::Machine.new(@model)
|
867
888
|
machine.event :park
|
868
|
-
record = @model.new
|
869
889
|
|
870
|
-
|
871
|
-
assert_equal ['State cannot transition via "stop"'], record.errors.full_messages
|
890
|
+
assert_equal 'stop', machine.event(:park).human_name
|
872
891
|
end
|
873
892
|
|
874
893
|
def test_should_allow_customized_event_key_scoped_to_machine
|
@@ -876,12 +895,10 @@ module ActiveModelTest
|
|
876
895
|
:activemodel => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
|
877
896
|
})
|
878
897
|
|
879
|
-
machine = StateMachine::Machine.new(@model
|
898
|
+
machine = StateMachine::Machine.new(@model)
|
880
899
|
machine.event :park
|
881
|
-
record = @model.new
|
882
900
|
|
883
|
-
|
884
|
-
assert_equal ['State cannot transition via "stop"'], record.errors.full_messages
|
901
|
+
assert_equal 'stop', machine.event(:park).human_name
|
885
902
|
end
|
886
903
|
|
887
904
|
def test_should_allow_customized_event_key_unscoped
|
@@ -889,21 +906,51 @@ module ActiveModelTest
|
|
889
906
|
:activemodel => {:state_machines => {:events => {:park => 'stop'}}}
|
890
907
|
})
|
891
908
|
|
892
|
-
machine = StateMachine::Machine.new(@model
|
909
|
+
machine = StateMachine::Machine.new(@model)
|
893
910
|
machine.event :park
|
894
|
-
record = @model.new
|
895
911
|
|
896
|
-
|
897
|
-
assert_equal ['State cannot transition via "stop"'], record.errors.full_messages
|
912
|
+
assert_equal 'stop', machine.event(:park).human_name
|
898
913
|
end
|
899
914
|
|
900
915
|
def test_should_only_add_locale_once_in_load_path
|
901
|
-
assert_equal 1, I18n.load_path.select {|path| path =~ %r{
|
916
|
+
assert_equal 1, I18n.load_path.select {|path| path =~ %r{active_model/locale\.rb$}}.length
|
902
917
|
|
903
918
|
# Create another ActiveRecord model that will triger the i18n feature
|
904
919
|
new_model
|
905
920
|
|
906
|
-
assert_equal 1, I18n.load_path.select {|path| path =~ %r{
|
921
|
+
assert_equal 1, I18n.load_path.select {|path| path =~ %r{active_model/locale\.rb$}}.length
|
922
|
+
end
|
923
|
+
|
924
|
+
def test_should_add_locale_to_beginning_of_load_path
|
925
|
+
@original_load_path = I18n.load_path
|
926
|
+
I18n.backend = I18n::Backend::Simple.new
|
927
|
+
|
928
|
+
app_locale = File.dirname(__FILE__) + '/../../files/en.yml'
|
929
|
+
default_locale = File.dirname(__FILE__) + '/../../../lib/state_machine/integrations/active_model/locale.rb'
|
930
|
+
I18n.load_path = [app_locale]
|
931
|
+
|
932
|
+
StateMachine::Machine.new(@model)
|
933
|
+
|
934
|
+
assert_equal [default_locale, app_locale].map {|path| File.expand_path(path)}, I18n.load_path.map {|path| File.expand_path(path)}
|
935
|
+
ensure
|
936
|
+
I18n.load_path = @original_load_path
|
937
|
+
end
|
938
|
+
|
939
|
+
def test_should_prefer_other_locales_first
|
940
|
+
@original_load_path = I18n.load_path
|
941
|
+
I18n.backend = I18n::Backend::Simple.new
|
942
|
+
I18n.load_path = [File.dirname(__FILE__) + '/../../files/en.yml']
|
943
|
+
|
944
|
+
machine = StateMachine::Machine.new(@model)
|
945
|
+
machine.state :parked, :idling
|
946
|
+
machine.event :ignite
|
947
|
+
|
948
|
+
record = @model.new(:state => 'idling')
|
949
|
+
|
950
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
|
951
|
+
assert_equal ['State cannot ignite'], record.errors.full_messages
|
952
|
+
ensure
|
953
|
+
I18n.load_path = @original_load_path
|
907
954
|
end
|
908
955
|
end
|
909
956
|
end
|