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.
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