state_machine 0.9.2 → 0.9.3
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 +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
|