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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f82de01738a5f173a174e6ccafb88d4b068ddf0
|
4
|
+
data.tar.gz: 2a53957a4121e365e978f093bfafca9f50b9dafd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8ba8dc5b7b59fc95208faaa2735476e89e669509037f8dfb1a3a7c799fafd53ca9964822694228d3ed1afccd8af035e7ab53cea989854fbd099ba078afcda5e
|
7
|
+
data.tar.gz: fbbb9bb8aa367b9b9a37a55f8b8b2fdafcec6cf914f417fc3135c63ae8f80e2c482df959886cd9ef6a5fc3be559805ca5764d872c11551809edd83586a00b7a3
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
script: bundle exec rake
|
2
|
+
sudo: false
|
3
|
+
cache: bundler
|
4
|
+
|
6
5
|
rvm:
|
7
|
-
- 1
|
8
|
-
- 2.
|
9
|
-
- 2.1.2
|
6
|
+
- 2.1
|
7
|
+
- 2.2
|
10
8
|
- jruby-19mode
|
9
|
+
- jruby
|
11
10
|
- rbx-2
|
11
|
+
|
12
12
|
gemfile:
|
13
|
-
- gemfiles/active_model_3.2.gemfile
|
14
|
-
- gemfiles/active_model_4.0.gemfile
|
15
13
|
- gemfiles/active_model_4.1.gemfile
|
16
|
-
- gemfiles/
|
14
|
+
- gemfiles/active_model_4.2.gemfile
|
15
|
+
|
16
|
+
matrix:
|
17
|
+
allow_failures:
|
18
|
+
- rvm: jruby
|
19
|
+
- rvm: jruby-19mode
|
20
|
+
- rvm: rbx-2
|
data/Appraisals
CHANGED
@@ -1,19 +1,8 @@
|
|
1
1
|
# ActiveModel integrations
|
2
|
-
appraise
|
3
|
-
gem
|
2
|
+
appraise 'active_model_4.1' do
|
3
|
+
gem 'activemodel', github: 'rails/rails', branch: '4-2-stable'
|
4
4
|
end
|
5
5
|
|
6
|
-
appraise
|
7
|
-
gem
|
6
|
+
appraise 'active_model_4.2' do
|
7
|
+
gem 'activemodel', github: 'rails/rails', branch: '4-2-stable'
|
8
8
|
end
|
9
|
-
|
10
|
-
|
11
|
-
appraise 'active_model_4.1' do
|
12
|
-
gem 'activemodel', '~> 4.1.0'
|
13
|
-
end
|
14
|
-
|
15
|
-
appraise 'active_model_edge' do
|
16
|
-
gem 'activemodel', github: 'rails/rails'
|
17
|
-
end
|
18
|
-
|
19
|
-
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
[](https://travis-ci.org/state-machines/state_machines-activemodel)
|
2
|
+
[](https://codeclimate.com/github/state-machines/state_machines-activemodel)
|
3
3
|
|
4
4
|
# StateMachines ActiveModel Integration
|
5
5
|
|
6
6
|
The ActiveModel integration is useful for both standalone usage and for providing
|
7
7
|
the base implementation for ORMs which implement the ActiveModel API. This
|
8
|
-
integration adds support for validation errors
|
9
|
-
observers.
|
8
|
+
integration adds support for validation errors and dirty attribute tracking.
|
10
9
|
|
11
10
|
## Installation
|
12
11
|
|
@@ -29,7 +28,6 @@ Or install it yourself as:
|
|
29
28
|
class Vehicle
|
30
29
|
include ActiveModel::Dirty
|
31
30
|
include ActiveModel::Validations
|
32
|
-
include ActiveModel::Observing
|
33
31
|
|
34
32
|
attr_accessor :state
|
35
33
|
define_attribute_methods [:state]
|
@@ -80,9 +78,14 @@ end
|
|
80
78
|
|
81
79
|
```
|
82
80
|
|
81
|
+
Dependencies
|
82
|
+
|
83
|
+
Active Model 4.1+
|
84
|
+
|
85
|
+
|
83
86
|
## Contributing
|
84
87
|
|
85
|
-
1. Fork it ( https://github.com/
|
88
|
+
1. Fork it ( https://github.com/state-machines/state_machines-activemodel/fork )
|
86
89
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
87
90
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
88
91
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
t.pattern = 'spec/**/*_spec.rb'
|
4
|
+
Rake::TestTask.new do |t|
|
5
|
+
t.test_files = FileList['test/*_test.rb']
|
7
6
|
end
|
8
7
|
|
9
8
|
desc 'Default: run all tests.'
|
10
|
-
task :
|
9
|
+
task default: :test
|
@@ -1,70 +1,72 @@
|
|
1
1
|
require 'active_model'
|
2
|
-
require 'active_support/
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
require 'active_support/core_ext/module/attribute_accessors.rb'
|
3
4
|
require 'state_machines'
|
4
|
-
require 'state_machines/integrations/
|
5
|
+
require 'state_machines/integrations/base'
|
6
|
+
require 'state_machines/integrations/active_model/version'
|
5
7
|
|
6
8
|
module StateMachines
|
7
9
|
module Integrations #:nodoc:
|
8
10
|
# Adds support for integrating state machines with ActiveModel classes.
|
9
|
-
#
|
11
|
+
#
|
10
12
|
# == Examples
|
11
|
-
#
|
13
|
+
#
|
12
14
|
# If using ActiveModel directly within your class, then any one of the
|
13
15
|
# following features need to be included in order for the integration to be
|
14
16
|
# detected:
|
15
17
|
# * ActiveModel::Observing
|
16
18
|
# * ActiveModel::Validations
|
17
|
-
#
|
19
|
+
#
|
18
20
|
# Below is an example of a simple state machine defined within an
|
19
21
|
# ActiveModel class:
|
20
|
-
#
|
22
|
+
#
|
21
23
|
# class Vehicle
|
22
24
|
# include ActiveModel::Observing
|
23
25
|
# include ActiveModel::Validations
|
24
|
-
#
|
26
|
+
#
|
25
27
|
# attr_accessor :state
|
26
28
|
# define_attribute_methods [:state]
|
27
|
-
#
|
29
|
+
#
|
28
30
|
# state_machine :initial => :parked do
|
29
31
|
# event :ignite do
|
30
32
|
# transition :parked => :idling
|
31
33
|
# end
|
32
34
|
# end
|
33
35
|
# end
|
34
|
-
#
|
36
|
+
#
|
35
37
|
# The examples in the sections below will use the above class as a
|
36
38
|
# reference.
|
37
|
-
#
|
39
|
+
#
|
38
40
|
# == Actions
|
39
|
-
#
|
41
|
+
#
|
40
42
|
# By default, no action will be invoked when a state is transitioned. This
|
41
43
|
# means that if you want to save changes when transitioning, you must
|
42
44
|
# define the action yourself like so:
|
43
|
-
#
|
45
|
+
#
|
44
46
|
# class Vehicle
|
45
47
|
# include ActiveModel::Validations
|
46
48
|
# attr_accessor :state
|
47
|
-
#
|
49
|
+
#
|
48
50
|
# state_machine :action => :save do
|
49
51
|
# ...
|
50
52
|
# end
|
51
|
-
#
|
53
|
+
#
|
52
54
|
# def save
|
53
55
|
# # Save changes
|
54
56
|
# end
|
55
57
|
# end
|
56
|
-
#
|
58
|
+
#
|
57
59
|
# == Validations
|
58
|
-
#
|
60
|
+
#
|
59
61
|
# As mentioned in StateMachine::Machine#state, you can define behaviors,
|
60
62
|
# like validations, that only execute for certain states. One *important*
|
61
63
|
# caveat here is that, due to a constraint in ActiveModel's validation
|
62
64
|
# framework, custom validators will not work as expected when defined to run
|
63
65
|
# in multiple states. For example:
|
64
|
-
#
|
66
|
+
#
|
65
67
|
# class Vehicle
|
66
68
|
# include ActiveModel::Validations
|
67
|
-
#
|
69
|
+
#
|
68
70
|
# state_machine do
|
69
71
|
# ...
|
70
72
|
# state :first_gear, :second_gear do
|
@@ -72,14 +74,14 @@ module StateMachines
|
|
72
74
|
# end
|
73
75
|
# end
|
74
76
|
# end
|
75
|
-
#
|
77
|
+
#
|
76
78
|
# In this case, the <tt>:speed_is_legal</tt> validation will only get run
|
77
79
|
# for the <tt>:second_gear</tt> state. To avoid this, you can define your
|
78
80
|
# custom validation like so:
|
79
|
-
#
|
81
|
+
#
|
80
82
|
# class Vehicle
|
81
83
|
# include ActiveModel::Validations
|
82
|
-
#
|
84
|
+
#
|
83
85
|
# state_machine do
|
84
86
|
# ...
|
85
87
|
# state :first_gear, :second_gear do
|
@@ -87,103 +89,103 @@ module StateMachines
|
|
87
89
|
# end
|
88
90
|
# end
|
89
91
|
# end
|
90
|
-
#
|
92
|
+
#
|
91
93
|
# == Validation errors
|
92
|
-
#
|
94
|
+
#
|
93
95
|
# In order to hook in validation support for your model, the
|
94
96
|
# ActiveModel::Validations feature must be included. If this is included
|
95
97
|
# and an event fails to successfully fire because there are no matching
|
96
98
|
# transitions for the object, a validation error is added to the object's
|
97
99
|
# state attribute to help in determining why it failed.
|
98
|
-
#
|
100
|
+
#
|
99
101
|
# For example,
|
100
|
-
#
|
102
|
+
#
|
101
103
|
# vehicle = Vehicle.new
|
102
104
|
# vehicle.ignite # => false
|
103
105
|
# vehicle.errors.full_messages # => ["State cannot transition via \"ignite\""]
|
104
|
-
#
|
106
|
+
#
|
105
107
|
# In addition, if you're using the <tt>ignite!</tt> version of the event,
|
106
108
|
# then the failure reason (such as the current validation errors) will be
|
107
109
|
# included in the exception that gets raised when the event fails. For
|
108
110
|
# example, assuming there's a validation on a field called +name+ on the class:
|
109
|
-
#
|
111
|
+
#
|
110
112
|
# vehicle = Vehicle.new
|
111
113
|
# vehicle.ignite! # => StateMachine::InvalidTransition: Cannot transition state via :ignite from :parked (Reason(s): Name cannot be blank)
|
112
|
-
#
|
114
|
+
#
|
113
115
|
# === Security implications
|
114
|
-
#
|
116
|
+
#
|
115
117
|
# Beware that public event attributes mean that events can be fired
|
116
118
|
# whenever mass-assignment is being used. If you want to prevent malicious
|
117
119
|
# users from tampering with events through URLs / forms, the attribute
|
118
120
|
# should be protected like so:
|
119
|
-
#
|
121
|
+
#
|
120
122
|
# class Vehicle
|
121
123
|
# include ActiveModel::MassAssignmentSecurity
|
122
124
|
# attr_accessor :state
|
123
|
-
#
|
125
|
+
#
|
124
126
|
# attr_protected :state_event
|
125
127
|
# # attr_accessible ... # Alternative technique
|
126
|
-
#
|
128
|
+
#
|
127
129
|
# state_machine do
|
128
130
|
# ...
|
129
131
|
# end
|
130
132
|
# end
|
131
|
-
#
|
133
|
+
#
|
132
134
|
# If you want to only have *some* events be able to fire via mass-assignment,
|
133
135
|
# you can build two state machines (one public and one protected) like so:
|
134
|
-
#
|
136
|
+
#
|
135
137
|
# class Vehicle
|
136
138
|
# include ActiveModel::MassAssignmentSecurity
|
137
139
|
# attr_accessor :state
|
138
|
-
#
|
140
|
+
#
|
139
141
|
# attr_protected :state_event # Prevent access to events in the first machine
|
140
|
-
#
|
142
|
+
#
|
141
143
|
# state_machine do
|
142
144
|
# # Define private events here
|
143
145
|
# end
|
144
|
-
#
|
146
|
+
#
|
145
147
|
# # Public machine targets the same state as the private machine
|
146
148
|
# state_machine :public_state, :attribute => :state do
|
147
149
|
# # Define public events here
|
148
150
|
# end
|
149
151
|
# end
|
150
|
-
#
|
152
|
+
#
|
151
153
|
# == Callbacks
|
152
|
-
#
|
154
|
+
#
|
153
155
|
# All before/after transition callbacks defined for ActiveModel models
|
154
156
|
# behave in the same way that other ActiveSupport callbacks behave. The
|
155
157
|
# object involved in the transition is passed in as an argument.
|
156
|
-
#
|
158
|
+
#
|
157
159
|
# For example,
|
158
|
-
#
|
160
|
+
#
|
159
161
|
# class Vehicle
|
160
162
|
# include ActiveModel::Validations
|
161
163
|
# attr_accessor :state
|
162
|
-
#
|
164
|
+
#
|
163
165
|
# state_machine :initial => :parked do
|
164
166
|
# before_transition any => :idling do |vehicle|
|
165
167
|
# vehicle.put_on_seatbelt
|
166
168
|
# end
|
167
|
-
#
|
169
|
+
#
|
168
170
|
# before_transition do |vehicle, transition|
|
169
171
|
# # log message
|
170
172
|
# end
|
171
|
-
#
|
173
|
+
#
|
172
174
|
# event :ignite do
|
173
175
|
# transition :parked => :idling
|
174
176
|
# end
|
175
177
|
# end
|
176
|
-
#
|
178
|
+
#
|
177
179
|
# def put_on_seatbelt
|
178
180
|
# ...
|
179
181
|
# end
|
180
182
|
# end
|
181
|
-
#
|
183
|
+
#
|
182
184
|
# Note, also, that the transition can be accessed by simply defining
|
183
185
|
# additional arguments in the callback block.
|
184
|
-
#
|
186
|
+
#
|
185
187
|
# == Observers
|
186
|
-
#
|
188
|
+
#
|
187
189
|
# In order to hook in observer support for your application, the
|
188
190
|
# ActiveModel::Observing feature must be included. Because of the way
|
189
191
|
# ActiveModel observers are designed, there is less flexibility around the
|
@@ -200,49 +202,49 @@ module StateMachines
|
|
200
202
|
# * before/after/after_failure_to-_transition_state_to_idling
|
201
203
|
# * before/after/after_failure_to-_transition_state
|
202
204
|
# * before/after/after_failure_to-_transition
|
203
|
-
#
|
205
|
+
#
|
204
206
|
# The following class shows an example of some of these hooks:
|
205
|
-
#
|
207
|
+
#
|
206
208
|
# class VehicleObserver < ActiveModel::Observer
|
207
209
|
# # Callback for :ignite event *before* the transition is performed
|
208
210
|
# def before_ignite(vehicle, transition)
|
209
211
|
# # log message
|
210
212
|
# end
|
211
|
-
#
|
213
|
+
#
|
212
214
|
# # Callback for :ignite event *after* the transition has been performed
|
213
215
|
# def after_ignite(vehicle, transition)
|
214
216
|
# # put on seatbelt
|
215
217
|
# end
|
216
|
-
#
|
218
|
+
#
|
217
219
|
# # Generic transition callback *before* the transition is performed
|
218
220
|
# def after_transition(vehicle, transition)
|
219
221
|
# Audit.log(vehicle, transition)
|
220
222
|
# end
|
221
|
-
#
|
223
|
+
#
|
222
224
|
# def after_failure_to_transition(vehicle, transition)
|
223
225
|
# Audit.error(vehicle, transition)
|
224
226
|
# end
|
225
227
|
# end
|
226
|
-
#
|
228
|
+
#
|
227
229
|
# More flexible transition callbacks can be defined directly within the
|
228
230
|
# model as described in StateMachine::Machine#before_transition
|
229
231
|
# and StateMachine::Machine#after_transition.
|
230
|
-
#
|
232
|
+
#
|
231
233
|
# To define a single observer for multiple state machines:
|
232
|
-
#
|
234
|
+
#
|
233
235
|
# class StateMachineObserver < ActiveModel::Observer
|
234
236
|
# observe Vehicle, Switch, Project
|
235
|
-
#
|
237
|
+
#
|
236
238
|
# def after_transition(object, transition)
|
237
239
|
# Audit.log(object, transition)
|
238
240
|
# end
|
239
241
|
# end
|
240
|
-
#
|
242
|
+
#
|
241
243
|
# == Internationalization
|
242
|
-
#
|
244
|
+
#
|
243
245
|
# Any error message that is generated from performing invalid transitions
|
244
246
|
# can be localized. The following default translations are used:
|
245
|
-
#
|
247
|
+
#
|
246
248
|
# en:
|
247
249
|
# activemodel:
|
248
250
|
# errors:
|
@@ -252,16 +254,16 @@ module StateMachines
|
|
252
254
|
# invalid_event: "cannot transition when %{state}"
|
253
255
|
# # %{value} = attribute value, %{event} = Human event name, %{state} = Human current state name
|
254
256
|
# invalid_transition: "cannot transition via %{event}"
|
255
|
-
#
|
257
|
+
#
|
256
258
|
# You can override these for a specific model like so:
|
257
|
-
#
|
259
|
+
#
|
258
260
|
# en:
|
259
261
|
# activemodel:
|
260
262
|
# errors:
|
261
263
|
# models:
|
262
264
|
# user:
|
263
265
|
# invalid: "is not valid"
|
264
|
-
#
|
266
|
+
#
|
265
267
|
# In addition to the above, you can also provide translations for the
|
266
268
|
# various states / events in each state machine. Using the Vehicle example,
|
267
269
|
# state translations will be looked for using the following keys, where
|
@@ -270,16 +272,16 @@ module StateMachines
|
|
270
272
|
# * <tt>activemodel.state_machines.#{model_name}.states.#{state_name}</tt>
|
271
273
|
# * <tt>activemodel.state_machines.#{machine_name}.states.#{state_name}</tt>
|
272
274
|
# * <tt>activemodel.state_machines.states.#{state_name}</tt>
|
273
|
-
#
|
275
|
+
#
|
274
276
|
# Event translations will be looked for using the following keys, where
|
275
277
|
# +model_name+ = "vehicle", +machine_name+ = "state" and +event_name+ = "ignite":
|
276
278
|
# * <tt>activemodel.state_machines.#{model_name}.#{machine_name}.events.#{event_name}</tt>
|
277
279
|
# * <tt>activemodel.state_machines.#{model_name}.events.#{event_name}</tt>
|
278
280
|
# * <tt>activemodel.state_machines.#{machine_name}.events.#{event_name}</tt>
|
279
281
|
# * <tt>activemodel.state_machines.events.#{event_name}</tt>
|
280
|
-
#
|
282
|
+
#
|
281
283
|
# An example translation configuration might look like so:
|
282
|
-
#
|
284
|
+
#
|
283
285
|
# es:
|
284
286
|
# activemodel:
|
285
287
|
# state_machines:
|
@@ -287,83 +289,81 @@ module StateMachines
|
|
287
289
|
# parked: 'estacionado'
|
288
290
|
# events:
|
289
291
|
# park: 'estacionarse'
|
290
|
-
#
|
292
|
+
#
|
291
293
|
# == Dirty Attribute Tracking
|
292
|
-
#
|
294
|
+
#
|
293
295
|
# When using the ActiveModel::Dirty extension, your model will keep track of
|
294
296
|
# any changes that are made to attributes. Depending on your ORM, an object
|
295
297
|
# will only be saved when there are attributes that have changed on the
|
296
298
|
# object. When integrating with state_machine, typically the +state+ field
|
297
299
|
# will be marked as dirty after a transition occurs. In some situations,
|
298
300
|
# however, this isn't the case.
|
299
|
-
#
|
301
|
+
#
|
300
302
|
# If you define loopback transitions in your state machine, the value for
|
301
303
|
# the machine's attribute (e.g. state) will not change. Unless you explicitly
|
302
304
|
# indicate so, this means that your object won't persist anything on a
|
303
305
|
# loopback. For example:
|
304
|
-
#
|
306
|
+
#
|
305
307
|
# class Vehicle
|
306
308
|
# include ActiveModel::Validations
|
307
309
|
# include ActiveModel::Dirty
|
308
310
|
# attr_accessor :state
|
309
|
-
#
|
311
|
+
#
|
310
312
|
# state_machine :initial => :parked do
|
311
313
|
# event :park do
|
312
314
|
# transition :parked => :parked, ...
|
313
315
|
# end
|
314
316
|
# end
|
315
317
|
# end
|
316
|
-
#
|
318
|
+
#
|
317
319
|
# If, instead, you'd like your object to always persist regardless of
|
318
320
|
# whether the value actually changed, you can do so by using the
|
319
321
|
# <tt>#{attribute}_will_change!</tt> helpers or defining a +before_transition+
|
320
322
|
# callback that actually changes an attribute on the model. For example:
|
321
|
-
#
|
323
|
+
#
|
322
324
|
# class Vehicle
|
323
325
|
# ...
|
324
326
|
# state_machine :initial => :parked do
|
325
327
|
# before_transition all => same do |vehicle|
|
326
328
|
# vehicle.state_will_change!
|
327
|
-
#
|
329
|
+
#
|
328
330
|
# # Alternative solution, updating timestamp
|
329
|
-
# # vehicle.updated_at = Time.
|
331
|
+
# # vehicle.updated_at = Time.current
|
330
332
|
# end
|
331
333
|
# end
|
332
334
|
# end
|
333
|
-
#
|
335
|
+
#
|
334
336
|
# == Creating new integrations
|
335
|
-
#
|
337
|
+
#
|
336
338
|
# If you want to integrate state_machine with an ORM that implements parts
|
337
339
|
# or all of the ActiveModel API, only the machine defaults need to be
|
338
340
|
# specified. Otherwise, the implementation is similar to any other
|
339
341
|
# integration.
|
340
|
-
#
|
342
|
+
#
|
341
343
|
# For example,
|
342
|
-
#
|
344
|
+
#
|
343
345
|
# module StateMachine::Integrations::MyORM
|
344
|
-
# include
|
345
|
-
#
|
346
|
-
#
|
347
|
-
#
|
346
|
+
# include ActiveModel
|
347
|
+
#
|
348
|
+
# mattr_accessor(:defaults) { :action => :persist }
|
349
|
+
#
|
348
350
|
# def self.matches?(klass)
|
349
351
|
# defined?(::MyORM::Base) && klass <= ::MyORM::Base
|
350
352
|
# end
|
351
|
-
#
|
353
|
+
#
|
352
354
|
# protected
|
353
|
-
#
|
354
|
-
#
|
355
|
-
#
|
355
|
+
#
|
356
|
+
# def runs_validations_on_action?
|
357
|
+
# action == :persist
|
358
|
+
# end
|
356
359
|
# end
|
357
|
-
#
|
360
|
+
#
|
358
361
|
# If you wish to implement other features, such as attribute initialization
|
359
362
|
# with protected attributes, named scopes, or database transactions, you
|
360
363
|
# must add these independent of the ActiveModel integration. See the
|
361
364
|
# ActiveRecord implementation for examples of these customizations.
|
362
365
|
module ActiveModel
|
363
|
-
|
364
|
-
include StateMachines::Integrations::Base
|
365
|
-
extend ClassMethods
|
366
|
-
|
366
|
+
include Base
|
367
367
|
|
368
368
|
@defaults = {}
|
369
369
|
|
@@ -373,11 +373,11 @@ module StateMachines
|
|
373
373
|
%w(ActiveModel ActiveModel::Validations)
|
374
374
|
end
|
375
375
|
|
376
|
-
# Adds a validation error to the given object
|
376
|
+
# Adds a validation error to the given object
|
377
377
|
def invalidate(object, attribute, message, values = [])
|
378
378
|
if supports_validations?
|
379
379
|
attribute = self.attribute(attribute)
|
380
|
-
options = values.
|
380
|
+
options = values.reduce({}) do |h, (key, value)|
|
381
381
|
h[key] = value
|
382
382
|
h
|
383
383
|
end
|
@@ -400,7 +400,7 @@ module StateMachines
|
|
400
400
|
|
401
401
|
# Runs state events around the object's validation process
|
402
402
|
def around_validation(object)
|
403
|
-
object.class.state_machines.transitions(object, action, :
|
403
|
+
object.class.state_machines.transitions(object, action, after: false).perform { yield }
|
404
404
|
end
|
405
405
|
|
406
406
|
protected
|
@@ -420,7 +420,7 @@ module StateMachines
|
|
420
420
|
|
421
421
|
# Gets the terminator to use for callbacks
|
422
422
|
def callback_terminator
|
423
|
-
@terminator ||=
|
423
|
+
@terminator ||= ->(result) { result == false }
|
424
424
|
end
|
425
425
|
|
426
426
|
# Determines the base scope to use when looking up translations
|
@@ -430,8 +430,8 @@ module StateMachines
|
|
430
430
|
|
431
431
|
# The default options to use when generating messages for validation
|
432
432
|
# errors
|
433
|
-
def default_error_message_options(
|
434
|
-
{:
|
433
|
+
def default_error_message_options(_object, _attribute, message)
|
434
|
+
{ message: @messages[message] }
|
435
435
|
end
|
436
436
|
|
437
437
|
# Translates the given key / value combo. Translation keys are looked
|
@@ -451,7 +451,7 @@ module StateMachines
|
|
451
451
|
translations = ancestors.map { |ancestor| :"#{ancestor.model_name.to_s.underscore}.#{name}.#{group}.#{value}" }
|
452
452
|
translations.concat(ancestors.map { |ancestor| :"#{ancestor.model_name.to_s.underscore}.#{group}.#{value}" })
|
453
453
|
translations.concat([:"#{name}.#{group}.#{value}", :"#{group}.#{value}", value.humanize.downcase])
|
454
|
-
I18n.translate(translations.shift, :
|
454
|
+
I18n.translate(translations.shift, default: translations, scope: [i18n_scope(klass), :state_machines])
|
455
455
|
end
|
456
456
|
|
457
457
|
# Build a list of ancestors for the given class to use when
|
@@ -475,12 +475,11 @@ module StateMachines
|
|
475
475
|
"#{File.dirname(__FILE__)}/active_model/locale.rb"
|
476
476
|
end
|
477
477
|
|
478
|
-
|
479
478
|
# Skips defining reader/writer methods since this is done automatically
|
480
479
|
def define_state_accessor
|
481
480
|
name = self.name
|
482
481
|
|
483
|
-
owner_class.validates_each(attribute) do |object
|
482
|
+
owner_class.validates_each(attribute) do |object|
|
484
483
|
machine = object.class.state_machine(name)
|
485
484
|
machine.invalidate(object, :state, :invalid) unless machine.states.match(object)
|
486
485
|
end if supports_validations?
|
@@ -495,7 +494,7 @@ module StateMachines
|
|
495
494
|
# Hooks into validations by defining around callbacks for the
|
496
495
|
# :validation event
|
497
496
|
def define_validation_hook
|
498
|
-
owner_class.set_callback(:validation, :around, self, :
|
497
|
+
owner_class.set_callback(:validation, :around, self, prepend: true)
|
499
498
|
end
|
500
499
|
|
501
500
|
# Creates a new callback in the callback chain, always inserting it
|
@@ -507,20 +506,18 @@ module StateMachines
|
|
507
506
|
end
|
508
507
|
|
509
508
|
# Configures new states with the built-in humanize scheme
|
510
|
-
def add_states(
|
509
|
+
def add_states(*)
|
511
510
|
super.each do |new_state|
|
512
|
-
new_state.human_name =
|
511
|
+
new_state.human_name = ->(state, klass) { translate(klass, :state, state.name) }
|
513
512
|
end
|
514
513
|
end
|
515
514
|
|
516
515
|
# Configures new event with the built-in humanize scheme
|
517
|
-
def add_events(
|
516
|
+
def add_events(*)
|
518
517
|
super.each do |new_event|
|
519
|
-
new_event.human_name =
|
518
|
+
new_event.human_name = ->(event, klass) { translate(klass, :event, event.name) }
|
520
519
|
end
|
521
520
|
end
|
522
|
-
|
523
|
-
|
524
521
|
end
|
525
522
|
end
|
526
523
|
end
|