state_machine 0.9.4 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +20 -0
- data/LICENSE +1 -1
- data/README.rdoc +74 -4
- data/Rakefile +3 -3
- data/lib/state_machine.rb +51 -24
- data/lib/state_machine/{guard.rb → branch.rb} +34 -40
- data/lib/state_machine/callback.rb +13 -18
- data/lib/state_machine/error.rb +13 -0
- data/lib/state_machine/eval_helpers.rb +3 -0
- data/lib/state_machine/event.rb +67 -30
- data/lib/state_machine/event_collection.rb +20 -3
- data/lib/state_machine/extensions.rb +3 -3
- data/lib/state_machine/integrations.rb +7 -0
- data/lib/state_machine/integrations/active_model.rb +149 -59
- data/lib/state_machine/integrations/active_model/versions.rb +30 -0
- data/lib/state_machine/integrations/active_record.rb +74 -148
- data/lib/state_machine/integrations/active_record/locale.rb +0 -7
- data/lib/state_machine/integrations/active_record/versions.rb +149 -0
- data/lib/state_machine/integrations/base.rb +64 -0
- data/lib/state_machine/integrations/data_mapper.rb +50 -39
- data/lib/state_machine/integrations/data_mapper/observer.rb +47 -12
- data/lib/state_machine/integrations/data_mapper/versions.rb +62 -0
- data/lib/state_machine/integrations/mongo_mapper.rb +37 -64
- data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
- data/lib/state_machine/integrations/mongo_mapper/versions.rb +102 -0
- data/lib/state_machine/integrations/mongoid.rb +297 -0
- data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
- data/lib/state_machine/integrations/mongoid/versions.rb +18 -0
- data/lib/state_machine/integrations/sequel.rb +99 -55
- data/lib/state_machine/integrations/sequel/versions.rb +40 -0
- data/lib/state_machine/machine.rb +273 -136
- data/lib/state_machine/machine_collection.rb +21 -13
- data/lib/state_machine/node_collection.rb +6 -1
- data/lib/state_machine/path.rb +120 -0
- data/lib/state_machine/path_collection.rb +90 -0
- data/lib/state_machine/state.rb +28 -9
- data/lib/state_machine/state_collection.rb +1 -1
- data/lib/state_machine/transition.rb +65 -6
- data/lib/state_machine/transition_collection.rb +1 -1
- data/test/files/en.yml +8 -0
- data/test/functional/state_machine_test.rb +15 -2
- data/test/unit/branch_test.rb +890 -0
- data/test/unit/callback_test.rb +9 -36
- data/test/unit/error_test.rb +43 -0
- data/test/unit/event_collection_test.rb +67 -33
- data/test/unit/event_test.rb +165 -38
- data/test/unit/integrations/active_model_test.rb +103 -3
- data/test/unit/integrations/active_record_test.rb +90 -43
- data/test/unit/integrations/base_test.rb +87 -0
- data/test/unit/integrations/data_mapper_test.rb +105 -44
- data/test/unit/integrations/mongo_mapper_test.rb +261 -64
- data/test/unit/integrations/mongoid_test.rb +1529 -0
- data/test/unit/integrations/sequel_test.rb +33 -49
- data/test/unit/integrations_test.rb +4 -0
- data/test/unit/invalid_event_test.rb +15 -2
- data/test/unit/invalid_parallel_transition_test.rb +18 -0
- data/test/unit/invalid_transition_test.rb +72 -2
- data/test/unit/machine_collection_test.rb +55 -61
- data/test/unit/machine_test.rb +388 -26
- data/test/unit/node_collection_test.rb +14 -4
- data/test/unit/path_collection_test.rb +266 -0
- data/test/unit/path_test.rb +485 -0
- data/test/unit/state_collection_test.rb +30 -0
- data/test/unit/state_test.rb +82 -35
- data/test/unit/transition_collection_test.rb +48 -44
- data/test/unit/transition_test.rb +198 -41
- metadata +111 -74
- data/test/unit/guard_test.rb +0 -909
@@ -0,0 +1,64 @@
|
|
1
|
+
module StateMachine
|
2
|
+
module Integrations
|
3
|
+
# Provides a set of base helpers for managing individual integrations
|
4
|
+
module Base
|
5
|
+
# Never matches
|
6
|
+
def self.matches?(klass)
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.included(base) #:nodoc:
|
11
|
+
base.class_eval do
|
12
|
+
extend ClassMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
# The default options to use for state machines using this integration
|
18
|
+
attr_reader :defaults
|
19
|
+
|
20
|
+
# Tracks the various version overrides for an integration
|
21
|
+
def versions
|
22
|
+
@versions ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
# Creates a new version override for an integration. When this
|
26
|
+
# integration is activated, each version that is marked as active will
|
27
|
+
# also extend the integration.
|
28
|
+
#
|
29
|
+
# == Example
|
30
|
+
#
|
31
|
+
# module StateMachine
|
32
|
+
# module Integrations
|
33
|
+
# module ORMLibrary
|
34
|
+
# version '0.2.x - 0.3.x' do
|
35
|
+
# def self.active?
|
36
|
+
# ::ORMLibrary::VERSION >= '0.2.0' && ::ORMLibrary::VERSION < '0.4.0'
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# def invalidate(object, attribute, message, values = [])
|
40
|
+
# # Override here...
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# In the above example, a version override is defined for the ORMLibrary
|
48
|
+
# integration when the version is between 0.2.x and 0.3.x.
|
49
|
+
def version(name, &block)
|
50
|
+
versions << mod = Module.new(&block)
|
51
|
+
mod
|
52
|
+
end
|
53
|
+
|
54
|
+
# Extends the given object with any version overrides that are currently
|
55
|
+
# active
|
56
|
+
def extended(base)
|
57
|
+
versions.each do |version|
|
58
|
+
base.extend(version) if version.active?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -241,6 +241,10 @@ module StateMachine
|
|
241
241
|
# support for DataMapper observers. See StateMachine::Integrations::DataMapper::Observer
|
242
242
|
# for more information.
|
243
243
|
module DataMapper
|
244
|
+
include Base
|
245
|
+
|
246
|
+
require 'state_machine/integrations/data_mapper/versions'
|
247
|
+
|
244
248
|
# The default options to use for state machines using this integration
|
245
249
|
class << self; attr_reader :defaults; end
|
246
250
|
@defaults = {:action => :save, :use_transactions => false}
|
@@ -255,28 +259,17 @@ module StateMachine
|
|
255
259
|
# Loads additional files specific to DataMapper
|
256
260
|
def self.extended(base) #:nodoc:
|
257
261
|
require 'dm-core/version' unless ::DataMapper.const_defined?('VERSION')
|
258
|
-
|
262
|
+
super
|
259
263
|
end
|
260
264
|
|
261
265
|
# Forces the change in state to be recognized regardless of whether the
|
262
266
|
# state value actually changed
|
263
|
-
def write(object, attribute, value)
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
object.original_values[self.attribute] = "#{value}-ignored" if object.original_values[self.attribute] == value
|
270
|
-
elsif ::DataMapper::VERSION =~ /^0\.10\./
|
271
|
-
property = owner_class.properties[self.attribute]
|
272
|
-
object.original_attributes[property] = "#{value}-ignored" unless object.original_attributes.include?(property)
|
273
|
-
else
|
274
|
-
object.persisted_state = ::DataMapper::Resource::State::Dirty.new(object) if object.persisted_state.is_a?(::DataMapper::Resource::State::Clean)
|
275
|
-
property = owner_class.properties[self.attribute]
|
276
|
-
object.persisted_state.original_attributes[property] = value unless object.persisted_state.original_attributes.include?(property)
|
277
|
-
end
|
278
|
-
else
|
279
|
-
result = super
|
267
|
+
def write(object, attribute, value, *args)
|
268
|
+
result = super
|
269
|
+
|
270
|
+
if attribute == :state || attribute == :event && value
|
271
|
+
value = read(object, :state) if attribute == :event
|
272
|
+
mark_dirty(object, value)
|
280
273
|
end
|
281
274
|
|
282
275
|
result
|
@@ -293,6 +286,16 @@ module StateMachine
|
|
293
286
|
end
|
294
287
|
|
295
288
|
protected
|
289
|
+
# Initializes class-level extensions and defaults for this machine
|
290
|
+
def after_initialize
|
291
|
+
load_observer_extensions
|
292
|
+
end
|
293
|
+
|
294
|
+
# Loads extensions to DataMapper's Observers
|
295
|
+
def load_observer_extensions
|
296
|
+
require 'state_machine/integrations/data_mapper/observer' if ::DataMapper.const_defined?('Observer')
|
297
|
+
end
|
298
|
+
|
296
299
|
# Is validation support currently loaded?
|
297
300
|
def supports_validations?
|
298
301
|
@supports_validations ||= ::DataMapper.const_defined?('Validate')
|
@@ -300,21 +303,24 @@ module StateMachine
|
|
300
303
|
|
301
304
|
# Pluralizes the name using the built-in inflector
|
302
305
|
def pluralize(word)
|
303
|
-
|
306
|
+
::DataMapper::Inflector.pluralize(word.to_s)
|
307
|
+
end
|
308
|
+
|
309
|
+
# Only allows state initialization on new records that aren't being
|
310
|
+
# created with a set of attributes that includes this machine's
|
311
|
+
# attribute.
|
312
|
+
def initialize_state?(object, options)
|
313
|
+
ignore = (options[:attributes] || {}).keys
|
314
|
+
!ignore.map {|attribute| attribute.to_sym}.include?(attribute)
|
304
315
|
end
|
305
316
|
|
306
317
|
# Defines an initialization hook into the owner class for setting the
|
307
318
|
# initial state of the machine *before* any attributes are set on the
|
308
319
|
# object
|
309
320
|
def define_state_initializer
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
initialize_state_machines(:dynamic => false, :ignore => ignore)
|
314
|
-
super
|
315
|
-
initialize_state_machines(:dynamic => true, :ignore => ignore)
|
316
|
-
end
|
317
|
-
end_eval
|
321
|
+
define_helper(:instance, :initialize) do |machine, object, _super, *args|
|
322
|
+
object.class.state_machines.initialize_states(object, :attributes => args.first) { _super.call }
|
323
|
+
end
|
318
324
|
end
|
319
325
|
|
320
326
|
# Skips defining reader/writer methods since this is done automatically
|
@@ -332,23 +338,20 @@ module StateMachine
|
|
332
338
|
|
333
339
|
# Adds hooks into validation for automatically firing events
|
334
340
|
def define_action_helpers
|
335
|
-
|
336
|
-
# enabled because of the way dm-validations integrates
|
337
|
-
return if ::DataMapper::VERSION =~ /^0\.9\.[4-6]/ && supports_validations?
|
341
|
+
super
|
338
342
|
|
339
|
-
if action == :save
|
340
|
-
|
341
|
-
|
342
|
-
define_method(:valid?) do |*args|
|
343
|
-
self.class.state_machines.transitions(self, :save, :after => false).perform { super(*args) }
|
344
|
-
end
|
345
|
-
end
|
343
|
+
if action == :save && supports_validations?
|
344
|
+
define_helper(:instance, :valid?) do |machine, object, _super, *|
|
345
|
+
object.class.state_machines.transitions(object, :save, :after => false).perform { _super.call }
|
346
346
|
end
|
347
|
-
else
|
348
|
-
super
|
349
347
|
end
|
350
348
|
end
|
351
349
|
|
350
|
+
# Uses internal save hooks if using the :save action
|
351
|
+
def action_hook
|
352
|
+
action == :save ? :save_self : super
|
353
|
+
end
|
354
|
+
|
352
355
|
# Creates a scope for finding records *with* a particular state or
|
353
356
|
# states for the attribute
|
354
357
|
def create_with_scope(name)
|
@@ -374,6 +377,14 @@ module StateMachine
|
|
374
377
|
options[:bind_to_object] = true
|
375
378
|
super
|
376
379
|
end
|
380
|
+
|
381
|
+
# Marks the object's state as dirty so that the record will be saved
|
382
|
+
# even if no actual modifications have been made to the data
|
383
|
+
def mark_dirty(object, value)
|
384
|
+
object.persisted_state = ::DataMapper::Resource::State::Dirty.new(object) if object.persisted_state.is_a?(::DataMapper::Resource::State::Clean)
|
385
|
+
property = owner_class.properties[self.attribute]
|
386
|
+
object.persisted_state.original_attributes[property] = value unless object.persisted_state.original_attributes.include?(property)
|
387
|
+
end
|
377
388
|
end
|
378
389
|
end
|
379
390
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module StateMachine
|
2
2
|
module Integrations #:nodoc:
|
3
3
|
module DataMapper
|
4
|
-
# Adds support for creating before/after transition
|
5
|
-
# DataMapper observer. These callbacks behave very
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# * Each callback can define a set of transition
|
9
|
-
#
|
4
|
+
# Adds support for creating before/after/around/failure transition
|
5
|
+
# callbacks within a DataMapper observer. These callbacks behave very
|
6
|
+
# similar to hooks during save/update/destroy/etc., but with the following
|
7
|
+
# modifications:
|
8
|
+
# * Each callback can define a set of transition requirements that must be
|
9
|
+
# met in order for the callback to get invoked.
|
10
10
|
# * An additional transition parameter is available that provides
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# contextual information about the event (see StateMachine::Transition
|
12
|
+
# for more information)
|
13
13
|
#
|
14
14
|
# To define a single observer for multiple state machines:
|
15
15
|
#
|
@@ -91,7 +91,7 @@ module StateMachine
|
|
91
91
|
# Vehicle instance being transition). This means that +self+ refers
|
92
92
|
# to the vehicle record within each callback block.
|
93
93
|
def before_transition(*args, &block)
|
94
|
-
add_transition_callback(:
|
94
|
+
add_transition_callback(:before_transition, *args, &block)
|
95
95
|
end
|
96
96
|
|
97
97
|
# Creates a callback that will be invoked *after* a transition is
|
@@ -101,7 +101,7 @@ module StateMachine
|
|
101
101
|
# See +before_transition+ for a description of the possible configurations
|
102
102
|
# for defining callbacks.
|
103
103
|
def after_transition(*args, &block)
|
104
|
-
add_transition_callback(:
|
104
|
+
add_transition_callback(:after_transition, *args, &block)
|
105
105
|
end
|
106
106
|
|
107
107
|
# Creates a callback that will be invoked *around* a transition so long
|
@@ -137,7 +137,42 @@ module StateMachine
|
|
137
137
|
# See +before_transition+ for a description of the possible configurations
|
138
138
|
# for defining callbacks.
|
139
139
|
def around_transition(*args, &block)
|
140
|
-
add_transition_callback(:
|
140
|
+
add_transition_callback(:around_transition, *args, &block)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Creates a callback that will be invoked *after* a transition failures to
|
144
|
+
# be performed so long as the given requirements match the transition.
|
145
|
+
#
|
146
|
+
# == Example
|
147
|
+
#
|
148
|
+
# class Vehicle
|
149
|
+
# include DataMapper::Resource
|
150
|
+
#
|
151
|
+
# property :id, Serial
|
152
|
+
# property :state, :String
|
153
|
+
#
|
154
|
+
# state_machine :initial => :parked do
|
155
|
+
# event :ignite do
|
156
|
+
# transition :parked => :idling
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# class VehicleObserver
|
162
|
+
# after_transition_failure do |transition|
|
163
|
+
# # log failure
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# after_transition_failure :on => :ignite do
|
167
|
+
# # log failure
|
168
|
+
# end
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# See +before_transition+ for a description of the possible configurations
|
172
|
+
# for defining callbacks. *Note* however that you cannot define the state
|
173
|
+
# requirements in these callbacks. You may only define event requirements.
|
174
|
+
def after_transition_failure(*args, &block)
|
175
|
+
add_transition_callback(:after_failure, *args, &block)
|
141
176
|
end
|
142
177
|
|
143
178
|
private
|
@@ -162,7 +197,7 @@ module StateMachine
|
|
162
197
|
klass.state_machines.values
|
163
198
|
end
|
164
199
|
|
165
|
-
state_machines.each {|machine| machine.send(
|
200
|
+
state_machines.each {|machine| machine.send(type, *args, &block)}
|
166
201
|
end if observing
|
167
202
|
end
|
168
203
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module StateMachine
|
2
|
+
module Integrations #:nodoc:
|
3
|
+
module DataMapper
|
4
|
+
version '0.9.x' do
|
5
|
+
def self.active?
|
6
|
+
::DataMapper::VERSION =~ /^0\.9\./
|
7
|
+
end
|
8
|
+
|
9
|
+
def action_hook
|
10
|
+
action
|
11
|
+
end
|
12
|
+
|
13
|
+
def mark_dirty(object, value)
|
14
|
+
object.original_values[self.attribute] = "#{value}-ignored" if object.original_values[self.attribute] == value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
version '0.9.x - 0.10.x' do
|
19
|
+
def self.active?
|
20
|
+
::DataMapper::VERSION =~ /^0\.\d\./ || ::DataMapper::VERSION =~ /^0\.10\./
|
21
|
+
end
|
22
|
+
|
23
|
+
def pluralize(word)
|
24
|
+
::Extlib::Inflection.pluralize(word.to_s)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
version '1.0.0' do
|
29
|
+
def self.active?
|
30
|
+
::DataMapper::VERSION == '1.0.0'
|
31
|
+
end
|
32
|
+
|
33
|
+
def pluralize(word)
|
34
|
+
(defined?(::ActiveSupport::Inflector) ? ::ActiveSupport::Inflector : ::Extlib::Inflection).pluralize(word.to_s)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
version '0.9.4 - 0.9.6' do
|
39
|
+
def self.active?
|
40
|
+
::DataMapper::VERSION =~ /^0\.9\.[4-6]/
|
41
|
+
end
|
42
|
+
|
43
|
+
# 0.9.4 - 0.9.6 fails to run after callbacks when validations are
|
44
|
+
# enabled because of the way dm-validations integrates
|
45
|
+
def define_action_helpers?
|
46
|
+
super if action != :save || !supports_validations?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
version '0.10.x' do
|
51
|
+
def self.active?
|
52
|
+
::DataMapper::VERSION =~ /^0\.10\./
|
53
|
+
end
|
54
|
+
|
55
|
+
def mark_dirty(object, value)
|
56
|
+
property = owner_class.properties[self.attribute]
|
57
|
+
object.original_attributes[property] = "#{value}-ignored" unless object.original_attributes.include?(property)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'state_machine/integrations/active_model'
|
2
|
+
|
1
3
|
module StateMachine
|
2
4
|
module Integrations #:nodoc:
|
3
5
|
# Adds support for integrating state machines with MongoMapper models.
|
@@ -184,8 +186,11 @@ module StateMachine
|
|
184
186
|
# Note, also, that the transition can be accessed by simply defining
|
185
187
|
# additional arguments in the callback block.
|
186
188
|
module MongoMapper
|
189
|
+
include Base
|
187
190
|
include ActiveModel
|
188
191
|
|
192
|
+
require 'state_machine/integrations/mongo_mapper/versions'
|
193
|
+
|
189
194
|
# The default options to use for state machines using this integration
|
190
195
|
@defaults = {:action => :save}
|
191
196
|
|
@@ -196,20 +201,10 @@ module StateMachine
|
|
196
201
|
defined?(::MongoMapper::Document) && klass <= ::MongoMapper::Document
|
197
202
|
end
|
198
203
|
|
199
|
-
# Adds a validation error to the given object (no i18n support)
|
200
|
-
def invalidate(object, attribute, message, values = [])
|
201
|
-
object.errors.add(self.attribute(attribute), generate_message(message, values))
|
202
|
-
end
|
203
|
-
|
204
204
|
protected
|
205
|
-
#
|
206
|
-
def
|
207
|
-
|
208
|
-
end
|
209
|
-
|
210
|
-
# Always adds validation support
|
211
|
-
def supports_validations?
|
212
|
-
true
|
205
|
+
# The name of this integration
|
206
|
+
def integration
|
207
|
+
:mongo_mapper
|
213
208
|
end
|
214
209
|
|
215
210
|
# Only runs validations on the action if using <tt>:save</tt>
|
@@ -217,71 +212,46 @@ module StateMachine
|
|
217
212
|
action == :save
|
218
213
|
end
|
219
214
|
|
220
|
-
#
|
221
|
-
|
215
|
+
# MongoMapper uses its own implementation of mass-assignment security
|
216
|
+
# instead of ActiveModel's, but still has a similar enough API that it
|
217
|
+
# can get enabled
|
218
|
+
def supports_mass_assignment_security?
|
222
219
|
true
|
223
220
|
end
|
224
221
|
|
225
|
-
#
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
# Don't allow translations
|
230
|
-
def translate(klass, key, value)
|
231
|
-
value.to_s.humanize.downcase
|
222
|
+
# Filters attributes that cannot be assigned through the initialization
|
223
|
+
# of the object
|
224
|
+
def filter_attributes(object, attributes)
|
225
|
+
object.send(:filter_protected_attrs, attributes)
|
232
226
|
end
|
233
227
|
|
234
228
|
# Defines an initialization hook into the owner class for setting the
|
235
229
|
# initial state of the machine *before* any attributes are set on the
|
236
230
|
# object
|
237
231
|
def define_state_initializer
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
if !from_database && (!attrs || !attrs.stringify_keys.key?('_id'))
|
243
|
-
filtered = respond_to?(:filter_protected_attrs) ? filter_protected_attrs(attrs) : attrs
|
244
|
-
ignore = filtered ? filtered.keys : []
|
245
|
-
|
246
|
-
initialize_state_machines(:dynamic => false, :ignore => ignore)
|
247
|
-
super
|
248
|
-
initialize_state_machines(:dynamic => true, :ignore => ignore)
|
249
|
-
else
|
250
|
-
super
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end_eval
|
232
|
+
define_helper(:instance, :initialize) do |machine, object, _super, *args|
|
233
|
+
object.class.state_machines.initialize_states(object, :attributes => args.first) { _super.call }
|
234
|
+
end
|
254
235
|
end
|
255
236
|
|
256
237
|
# Skips defining reader/writer methods since this is done automatically
|
257
238
|
def define_state_accessor
|
258
239
|
owner_class.key(attribute, String) unless owner_class.keys.include?(attribute)
|
259
|
-
|
260
|
-
name = self.name
|
261
|
-
owner_class.validates_each(attribute, :logic => lambda {|*|
|
262
|
-
machine = self.class.state_machine(name)
|
263
|
-
machine.invalidate(self, :state, :invalid) unless machine.states.match(self)
|
264
|
-
})
|
240
|
+
super
|
265
241
|
end
|
266
242
|
|
267
|
-
#
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
# Still use class_eval here instance of define_instance_method since
|
274
|
-
# we need to be able to call +super+
|
275
|
-
@instance_helper_module.class_eval do
|
276
|
-
define_method("#{name}?") do |*args|
|
277
|
-
args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
|
278
|
-
end
|
243
|
+
# Uses around callbacks to run state events if using the :save hook
|
244
|
+
def define_action_hook
|
245
|
+
if action_hook == :save
|
246
|
+
owner_class.set_callback(:save, :around, self, :prepend => true)
|
247
|
+
else
|
248
|
+
super
|
279
249
|
end
|
280
250
|
end
|
281
251
|
|
282
|
-
#
|
283
|
-
def
|
284
|
-
|
252
|
+
# Runs state events around the machine's :save action
|
253
|
+
def around_save(object)
|
254
|
+
object.class.state_machines.transitions(object, action).perform { yield }
|
285
255
|
end
|
286
256
|
|
287
257
|
# Creates a scope for finding records *with* a particular state or
|
@@ -298,11 +268,14 @@ module StateMachine
|
|
298
268
|
|
299
269
|
# Defines a new scope with the given name
|
300
270
|
def define_scope(name, scope)
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
271
|
+
lambda {|model, values| model.query.merge(model.query(scope.call(values)))}
|
272
|
+
end
|
273
|
+
|
274
|
+
# ActiveModel's use of method_missing / respond_to for attribute methods
|
275
|
+
# breaks both ancestor lookups and defined?(super). Need to special-case
|
276
|
+
# the existence of query attribute methods.
|
277
|
+
def owner_class_ancestor_has_method?(method)
|
278
|
+
method == "#{name}?" || super
|
306
279
|
end
|
307
280
|
end
|
308
281
|
end
|