state_machines-activemodel 0.0.2 → 0.0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +14 -10
- data/Appraisals +4 -15
- data/Gemfile +3 -0
- data/LICENSE.txt +1 -1
- data/README.md +9 -6
- data/Rakefile +5 -6
- data/gemfiles/active_model_4.1.gemfile +5 -1
- data/gemfiles/active_model_4.2.gemfile +11 -0
- data/lib/state_machines/integrations/active_model.rb +105 -108
- data/lib/state_machines/integrations/active_model/locale.rb +8 -8
- data/lib/state_machines/integrations/{version.rb → active_model/version.rb} +1 -1
- data/state_machines-activemodel.gemspec +10 -9
- data/{spec/support → test/files}/en.yml +1 -1
- data/test/integration_test.rb +23 -0
- data/test/machine_by_default_test.rb +24 -0
- data/test/machine_errors_test.rb +19 -0
- data/test/machine_multiple_test.rb +18 -0
- data/test/machine_with_callbacks_test.rb +130 -0
- data/test/machine_with_dirty_attribute_and_custom_attributes_during_loopback_test.rb +26 -0
- data/test/machine_with_dirty_attribute_and_state_events_test.rb +23 -0
- data/test/machine_with_dirty_attributes_and_custom_attribute_test.rb +34 -0
- data/test/machine_with_dirty_attributes_during_loopback_test.rb +49 -0
- data/test/machine_with_dirty_attributes_test.rb +33 -0
- data/test/machine_with_dynamic_initial_state_test.rb +14 -0
- data/test/machine_with_events_test.rb +13 -0
- data/test/machine_with_failed_after_callbacks_test.rb +31 -0
- data/test/machine_with_failed_before_callbacks_test.rb +32 -0
- data/test/machine_with_initialized_state_test.rb +35 -0
- data/test/machine_with_internationalization_test.rb +190 -0
- data/test/machine_with_model_state_attribute_test.rb +31 -0
- data/test/machine_with_non_model_state_attribute_undefined_test.rb +26 -0
- data/test/machine_with_state_driven_validations_test.rb +31 -0
- data/test/machine_with_states_test.rb +13 -0
- data/test/machine_with_static_initial_state_test.rb +13 -0
- data/test/machine_with_validations_and_custom_attribute_test.rb +22 -0
- data/test/machine_with_validations_test.rb +46 -0
- data/test/test_helper.rb +58 -0
- metadata +83 -36
- data/gemfiles/active_model_3.2.gemfile +0 -7
- data/gemfiles/active_model_3.2.gemfile.lock +0 -50
- data/gemfiles/active_model_4.0.gemfile +0 -7
- data/gemfiles/active_model_4.0.gemfile.lock +0 -56
- data/gemfiles/active_model_4.1.gemfile.lock +0 -57
- data/gemfiles/active_model_edge.gemfile +0 -7
- data/gemfiles/active_model_edge.gemfile.lock +0 -62
- data/spec/active_model_spec.rb +0 -801
- data/spec/integration_spec.rb +0 -26
- data/spec/spec_helper.rb +0 -8
- data/spec/support/helpers.rb +0 -48
- data/spec/support/migration_helpers.rb +0 -43
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithDynamicInitialStateTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model
|
6
|
+
@machine = StateMachines::Machine.new(@model, initial: lambda { |_object| :parked }, integration: :active_model)
|
7
|
+
@machine.state :parked
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_set_initial_state_on_created_object
|
11
|
+
record = @model.new
|
12
|
+
assert_equal 'parked', record.state
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithEventsTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model
|
6
|
+
@machine = StateMachines::Machine.new(@model)
|
7
|
+
@machine.event :shift_up
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_humanize_name
|
11
|
+
assert_equal 'shift up', @machine.event(:shift_up).human_name
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithFailedAfterCallbacksTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@callbacks = []
|
6
|
+
|
7
|
+
@model = new_model
|
8
|
+
@machine = StateMachines::Machine.new(@model, integration: :active_model)
|
9
|
+
@machine.state :parked, :idling
|
10
|
+
@machine.event :ignite
|
11
|
+
@machine.after_transition { @callbacks << :after_1; false }
|
12
|
+
@machine.after_transition { @callbacks << :after_2 }
|
13
|
+
@machine.around_transition { |block| @callbacks << :around_before; block.call; @callbacks << :around_after }
|
14
|
+
|
15
|
+
@record = @model.new(state: 'parked')
|
16
|
+
@transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
17
|
+
@result = @transition.perform
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_be_successful
|
21
|
+
assert @result
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_should_change_current_state
|
25
|
+
assert_equal 'idling', @record.state
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_not_run_further_after_callbacks
|
29
|
+
assert_equal [:around_before, :around_after, :after_1], @callbacks
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithFailedBeforeCallbacksTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@callbacks = []
|
6
|
+
|
7
|
+
@model = new_model
|
8
|
+
@machine = StateMachines::Machine.new(@model, integration: :active_model)
|
9
|
+
@machine.state :parked, :idling
|
10
|
+
@machine.event :ignite
|
11
|
+
@machine.before_transition { @callbacks << :before_1; false }
|
12
|
+
@machine.before_transition { @callbacks << :before_2 }
|
13
|
+
@machine.after_transition { @callbacks << :after }
|
14
|
+
@machine.around_transition { |block| @callbacks << :around_before; block.call; @callbacks << :around_after }
|
15
|
+
|
16
|
+
@record = @model.new(state: 'parked')
|
17
|
+
@transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
18
|
+
@result = @transition.perform
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_should_not_be_successful
|
22
|
+
assert !@result
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_not_change_current_state
|
26
|
+
assert_equal 'parked', @record.state
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_should_not_run_further_callbacks
|
30
|
+
assert_equal [:before_1], @callbacks
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithInitializedStateTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model
|
6
|
+
@machine = StateMachines::Machine.new(@model, initial: :parked, integration: :active_model)
|
7
|
+
@machine.state :idling
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_allow_nil_initial_state_when_static
|
11
|
+
@machine.state nil
|
12
|
+
|
13
|
+
record = @model.new(state: nil)
|
14
|
+
assert_nil record.state
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_should_allow_nil_initial_state_when_dynamic
|
18
|
+
@machine.state nil
|
19
|
+
|
20
|
+
@machine.initial_state = lambda { :parked }
|
21
|
+
record = @model.new(state: nil)
|
22
|
+
assert_nil record.state
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_allow_different_initial_state_when_static
|
26
|
+
record = @model.new(state: 'idling')
|
27
|
+
assert_equal 'idling', record.state
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_allow_different_initial_state_when_dynamic
|
31
|
+
@machine.initial_state = lambda { :parked }
|
32
|
+
record = @model.new(state: 'idling')
|
33
|
+
assert_equal 'idling', record.state
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'i18n'
|
3
|
+
|
4
|
+
class MachineWithInternationalizationTest < BaseTestCase
|
5
|
+
def setup
|
6
|
+
I18n.backend = I18n::Backend::Simple.new
|
7
|
+
# Initialize the backend
|
8
|
+
I18n.backend.translate(:en, 'activemodel.errors.messages.invalid_transition', event: 'ignite', value: 'idling')
|
9
|
+
|
10
|
+
@model = new_model { include ActiveModel::Validations }
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_should_use_defaults
|
14
|
+
I18n.backend.store_translations(:en,
|
15
|
+
activemodel: { errors: { messages: { invalid_transition: 'cannot %{event}' } } }
|
16
|
+
)
|
17
|
+
|
18
|
+
machine = StateMachines::Machine.new(@model, action: :save)
|
19
|
+
machine.state :parked, :idling
|
20
|
+
machine.event :ignite
|
21
|
+
|
22
|
+
record = @model.new(state: 'idling')
|
23
|
+
|
24
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
|
25
|
+
assert_equal ['State cannot ignite'], record.errors.full_messages
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_allow_customized_error_key
|
29
|
+
I18n.backend.store_translations(:en,
|
30
|
+
activemodel: { errors: { messages: { bad_transition: 'cannot %{event}' } } }
|
31
|
+
)
|
32
|
+
|
33
|
+
machine = StateMachines::Machine.new(@model, action: :save, messages: { invalid_transition: :bad_transition })
|
34
|
+
machine.state :parked, :idling
|
35
|
+
|
36
|
+
record = @model.new
|
37
|
+
record.state = 'idling'
|
38
|
+
|
39
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
|
40
|
+
assert_equal ['State cannot ignite'], record.errors.full_messages
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_should_allow_customized_error_string
|
44
|
+
machine = StateMachines::Machine.new(@model, action: :save, messages: { invalid_transition: 'cannot %{event}' })
|
45
|
+
machine.state :parked, :idling
|
46
|
+
|
47
|
+
record = @model.new(state: 'idling')
|
48
|
+
|
49
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
|
50
|
+
assert_equal ['State cannot ignite'], record.errors.full_messages
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_should_allow_customized_state_key_scoped_to_class_and_machine
|
54
|
+
I18n.backend.store_translations(:en,
|
55
|
+
activemodel: { state_machines: { :'foo' => { state: { states: { parked: 'shutdown' } } } } }
|
56
|
+
)
|
57
|
+
|
58
|
+
machine = StateMachines::Machine.new(@model)
|
59
|
+
machine.state :parked
|
60
|
+
assert_equal 'shutdown', machine.state(:parked).human_name
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_should_allow_customized_state_key_scoped_to_class
|
64
|
+
I18n.backend.store_translations(:en,
|
65
|
+
activemodel: { state_machines: { :'foo' => { states: { parked: 'shutdown' } } } }
|
66
|
+
)
|
67
|
+
|
68
|
+
machine = StateMachines::Machine.new(@model)
|
69
|
+
machine.state :parked
|
70
|
+
|
71
|
+
assert_equal 'shutdown', machine.state(:parked).human_name
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_should_allow_customized_state_key_scoped_to_machine
|
75
|
+
I18n.backend.store_translations(:en,
|
76
|
+
activemodel: { state_machines: { state: { states: { parked: 'shutdown' } } } }
|
77
|
+
)
|
78
|
+
|
79
|
+
machine = StateMachines::Machine.new(@model)
|
80
|
+
machine.state :parked
|
81
|
+
|
82
|
+
assert_equal 'shutdown', machine.state(:parked).human_name
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_should_allow_customized_state_key_unscoped
|
86
|
+
I18n.backend.store_translations(:en,
|
87
|
+
activemodel: { state_machines: { states: { parked: 'shutdown' } } }
|
88
|
+
)
|
89
|
+
|
90
|
+
machine = StateMachines::Machine.new(@model)
|
91
|
+
machine.state :parked
|
92
|
+
|
93
|
+
assert_equal 'shutdown', machine.state(:parked).human_name
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_should_support_nil_state_key
|
97
|
+
I18n.backend.store_translations(:en,
|
98
|
+
activemodel: { state_machines: { states: { nil: 'empty' } } }
|
99
|
+
)
|
100
|
+
|
101
|
+
machine = StateMachines::Machine.new(@model)
|
102
|
+
|
103
|
+
assert_equal 'empty', machine.state(nil).human_name
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_should_allow_customized_event_key_scoped_to_class_and_machine
|
107
|
+
I18n.backend.store_translations(:en,
|
108
|
+
activemodel: { state_machines: { :'foo' => { state: { events: { park: 'stop' } } } } }
|
109
|
+
)
|
110
|
+
|
111
|
+
machine = StateMachines::Machine.new(@model)
|
112
|
+
machine.event :park
|
113
|
+
|
114
|
+
assert_equal 'stop', machine.event(:park).human_name
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_should_allow_customized_event_key_scoped_to_class
|
118
|
+
I18n.backend.store_translations(:en,
|
119
|
+
activemodel: { state_machines: { :'foo' => { events: { park: 'stop' } } } }
|
120
|
+
)
|
121
|
+
|
122
|
+
machine = StateMachines::Machine.new(@model)
|
123
|
+
machine.event :park
|
124
|
+
|
125
|
+
assert_equal 'stop', machine.event(:park).human_name
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_should_allow_customized_event_key_scoped_to_machine
|
129
|
+
I18n.backend.store_translations(:en,
|
130
|
+
activemodel: { state_machines: { state: { events: { park: 'stop' } } } }
|
131
|
+
)
|
132
|
+
|
133
|
+
machine = StateMachines::Machine.new(@model)
|
134
|
+
machine.event :park
|
135
|
+
|
136
|
+
assert_equal 'stop', machine.event(:park).human_name
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_should_allow_customized_event_key_unscoped
|
140
|
+
I18n.backend.store_translations(:en,
|
141
|
+
activemodel: { state_machines: { events: { park: 'stop' } } }
|
142
|
+
)
|
143
|
+
|
144
|
+
machine = StateMachines::Machine.new(@model)
|
145
|
+
machine.event :park
|
146
|
+
|
147
|
+
assert_equal 'stop', machine.event(:park).human_name
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_should_only_add_locale_once_in_load_path
|
151
|
+
assert_equal 1, I18n.load_path.select { |path| path =~ %r{active_model/locale\.rb$} }.length
|
152
|
+
|
153
|
+
# Create another ActiveModel model that will triger the i18n feature
|
154
|
+
new_model
|
155
|
+
|
156
|
+
assert_equal 1, I18n.load_path.select { |path| path =~ %r{active_model/locale\.rb$} }.length
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_should_add_locale_to_beginning_of_load_path
|
160
|
+
@original_load_path = I18n.load_path
|
161
|
+
I18n.backend = I18n::Backend::Simple.new
|
162
|
+
|
163
|
+
app_locale = File.dirname(__FILE__) + '/files/en.yml'
|
164
|
+
default_locale = File.dirname(__FILE__) + '/../lib/state_machines/integrations/active_model/locale.rb'
|
165
|
+
I18n.load_path = [app_locale]
|
166
|
+
|
167
|
+
StateMachines::Machine.new(@model)
|
168
|
+
|
169
|
+
assert_equal [default_locale, app_locale].map { |path| File.expand_path(path) }, I18n.load_path.map { |path| File.expand_path(path) }
|
170
|
+
ensure
|
171
|
+
I18n.load_path = @original_load_path
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_should_prefer_other_locales_first
|
175
|
+
@original_load_path = I18n.load_path
|
176
|
+
I18n.backend = I18n::Backend::Simple.new
|
177
|
+
I18n.load_path = [File.dirname(__FILE__) + '/files/en.yml']
|
178
|
+
|
179
|
+
machine = StateMachines::Machine.new(@model)
|
180
|
+
machine.state :parked, :idling
|
181
|
+
machine.event :ignite
|
182
|
+
|
183
|
+
record = @model.new(state: 'idling')
|
184
|
+
|
185
|
+
machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
|
186
|
+
assert_equal ['State cannot ignite'], record.errors.full_messages
|
187
|
+
ensure
|
188
|
+
I18n.load_path = @original_load_path
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithModelStateAttributeTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model
|
6
|
+
@machine = StateMachines::Machine.new(@model, initial: :parked, integration: :active_model)
|
7
|
+
@machine.other_states(:idling)
|
8
|
+
|
9
|
+
@record = @model.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_should_have_an_attribute_predicate
|
13
|
+
assert @record.respond_to?(:state?)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_should_raise_exception_for_predicate_without_parameters
|
17
|
+
assert_raises(ArgumentError) { @record.state? }
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_return_false_for_predicate_if_does_not_match_current_value
|
21
|
+
assert !@record.state?(:idling)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_should_return_true_for_predicate_if_matches_current_value
|
25
|
+
assert @record.state?(:parked)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_raise_exception_for_predicate_if_invalid_state_specified
|
29
|
+
assert_raises(IndexError) { @record.state?(:invalid) }
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithNonModelStateAttributeUndefinedTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model do
|
6
|
+
def initialize
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
@machine = StateMachines::Machine.new(@model, :status, initial: :parked, integration: :active_model)
|
11
|
+
@machine.other_states(:idling)
|
12
|
+
@record = @model.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_should_not_define_a_reader_attribute_for_the_attribute
|
16
|
+
assert !@record.respond_to?(:status)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_should_not_define_a_writer_attribute_for_the_attribute
|
20
|
+
assert !@record.respond_to?(:status=)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_define_an_attribute_predicate
|
24
|
+
assert @record.respond_to?(:status?)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithStateDrivenValidationsTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model do
|
6
|
+
include ActiveModel::Validations
|
7
|
+
attr_accessor :seatbelt
|
8
|
+
end
|
9
|
+
|
10
|
+
@machine = StateMachines::Machine.new(@model)
|
11
|
+
@machine.state :first_gear, :second_gear do
|
12
|
+
validates_presence_of :seatbelt
|
13
|
+
end
|
14
|
+
@machine.other_states :parked
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_should_be_valid_if_validation_fails_outside_state_scope
|
18
|
+
record = @model.new(state: 'parked', seatbelt: nil)
|
19
|
+
assert record.valid?
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_should_be_invalid_if_validation_fails_within_state_scope
|
23
|
+
record = @model.new(state: 'first_gear', seatbelt: nil)
|
24
|
+
assert !record.valid?
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_should_be_valid_if_validation_succeeds_within_state_scope
|
28
|
+
record = @model.new(state: 'second_gear', seatbelt: true)
|
29
|
+
assert record.valid?
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithStatesTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model
|
6
|
+
@machine = StateMachines::Machine.new(@model)
|
7
|
+
@machine.state :first_gear
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_humanize_name
|
11
|
+
assert_equal 'first gear', @machine.state(:first_gear).human_name
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithStaticInitialStateTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model
|
6
|
+
@machine = StateMachines::Machine.new(@model, initial: :parked, integration: :active_model)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_should_set_initial_state_on_created_object
|
10
|
+
record = @model.new
|
11
|
+
assert_equal 'parked', record.state
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
|
4
|
+
def setup
|
5
|
+
@model = new_model { include ActiveModel::Validations }
|
6
|
+
|
7
|
+
@machine = StateMachines::Machine.new(@model, :status, attribute: :state)
|
8
|
+
@machine.state :parked
|
9
|
+
|
10
|
+
@record = @model.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_should_add_validation_errors_to_custom_attribute
|
14
|
+
@record.state = 'invalid'
|
15
|
+
|
16
|
+
assert !@record.valid?
|
17
|
+
assert_equal ['State is invalid'], @record.errors.full_messages
|
18
|
+
|
19
|
+
@record.state = 'parked'
|
20
|
+
assert @record.valid?
|
21
|
+
end
|
22
|
+
end
|