state_machines-activemodel 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://travis-ci.org/
|
2
|
-
[![Code Climate](https://codeclimate.com/github/
|
1
|
+
[![Build Status](https://travis-ci.org/state-machines/state_machines-activemodel.svg?branch=master)](https://travis-ci.org/state-machines/state_machines-activemodel)
|
2
|
+
[![Code Climate](https://codeclimate.com/github/state-machines/state_machines-activemodel.png)](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
|