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.
Files changed (36) hide show
  1. data/CHANGELOG.rdoc +10 -0
  2. data/README.rdoc +8 -0
  3. data/Rakefile +1 -1
  4. data/examples/merb-rest/view_edit.html.erb +2 -2
  5. data/examples/merb-rest/view_index.html.erb +2 -2
  6. data/examples/merb-rest/view_show.html.erb +2 -2
  7. data/examples/rails-rest/view_edit.html.erb +2 -2
  8. data/examples/rails-rest/view_index.html.erb +2 -2
  9. data/examples/rails-rest/view_show.html.erb +2 -2
  10. data/lib/state_machine.rb +34 -0
  11. data/lib/state_machine/event.rb +17 -2
  12. data/lib/state_machine/event_collection.rb +1 -1
  13. data/lib/state_machine/integrations/active_model.rb +39 -15
  14. data/lib/state_machine/integrations/active_model/locale.rb +2 -2
  15. data/lib/state_machine/integrations/active_record.rb +15 -3
  16. data/lib/state_machine/integrations/active_record/locale.rb +16 -0
  17. data/lib/state_machine/integrations/mongo_mapper.rb +16 -2
  18. data/lib/state_machine/machine.rb +53 -10
  19. data/lib/state_machine/machine_collection.rb +1 -1
  20. data/lib/state_machine/state.rb +12 -1
  21. data/lib/state_machine/transition.rb +50 -34
  22. data/test/files/en.yml +9 -0
  23. data/test/{classes → files}/switch.rb +0 -0
  24. data/test/functional/state_machine_test.rb +9 -0
  25. data/test/unit/event_collection_test.rb +5 -7
  26. data/test/unit/event_test.rb +51 -0
  27. data/test/unit/integrations/active_model_test.rb +80 -33
  28. data/test/unit/integrations/active_record_test.rb +89 -30
  29. data/test/unit/integrations/data_mapper_test.rb +25 -1
  30. data/test/unit/integrations/mongo_mapper_test.rb +40 -7
  31. data/test/unit/integrations/sequel_test.rb +25 -1
  32. data/test/unit/machine_collection_test.rb +1 -1
  33. data/test/unit/machine_test.rb +123 -4
  34. data/test/unit/state_test.rb +53 -0
  35. data/test/unit/transition_test.rb +20 -0
  36. 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
- events = names.collect do |name|
1018
- unless event = self.events[name]
1019
- self.events << event = Event.new(self, name)
1020
- end
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, event_name]])
36
+ machine.invalidate(object, :state, :invalid_transition, [[:event, event.human_name]])
37
37
  end
38
38
 
39
39
  transition
@@ -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
- # Event information
63
- event = machine.events.fetch(event)
64
- @event = event.name
65
- @qualified_event = event.qualified_name
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
@@ -0,0 +1,9 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ invalid_transition: "cannot transition"
6
+ activemodel:
7
+ errors:
8
+ messages:
9
+ invalid_transition: "cannot %{event}"
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 test_should_invalidate_with_friendly_name_if_invalid_event_specified
275
- # Add a valid nil state
276
- @machine.state nil
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 nil'], @object.errors
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
@@ -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, :park]])
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 {{event}}'}}}
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, :ignite]])
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 {{event}}'}}}
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, :ignite]])
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 {{event}}'})
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, :ignite]])
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, :initial => :parked, :action => :save)
831
- record = @model.new
854
+ machine = StateMachine::Machine.new(@model)
855
+ machine.state :parked
832
856
 
833
- machine.invalidate(record, :event, :invalid_event, [[:state, :parked]])
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, :initial => :parked, :action => :save)
843
- record = @model.new
865
+ machine = StateMachine::Machine.new(@model)
866
+ machine.state :parked
844
867
 
845
- machine.invalidate(record, :event, :invalid_event, [[:state, :parked]])
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, :initial => :parked, :action => :save)
855
- record = @model.new
876
+ machine = StateMachine::Machine.new(@model)
877
+ machine.state :parked
856
878
 
857
- machine.invalidate(record, :event, :invalid_event, [[:state, :parked]])
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, :action => :save)
887
+ machine = StateMachine::Machine.new(@model)
867
888
  machine.event :park
868
- record = @model.new
869
889
 
870
- machine.invalidate(record, :state, :invalid_transition, [[:event, :park]])
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, :action => :save)
898
+ machine = StateMachine::Machine.new(@model)
880
899
  machine.event :park
881
- record = @model.new
882
900
 
883
- machine.invalidate(record, :state, :invalid_transition, [[:event, :park]])
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, :action => :save)
909
+ machine = StateMachine::Machine.new(@model)
893
910
  machine.event :park
894
- record = @model.new
895
911
 
896
- machine.invalidate(record, :state, :invalid_transition, [[:event, :park]])
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{state_machine/integrations/active_model/locale\.rb$}}.length
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{state_machine/integrations/active_model/locale\.rb$}}.length
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