state_machine 0.7.5 → 0.7.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +10 -0
- data/README.rdoc +1 -1
- data/Rakefile +9 -2
- data/lib/state_machine.rb +17 -12
- data/lib/state_machine/assertions.rb +4 -5
- data/lib/state_machine/callback.rb +27 -21
- data/lib/state_machine/condition_proxy.rb +1 -1
- data/lib/state_machine/eval_helpers.rb +1 -2
- data/lib/state_machine/event.rb +3 -3
- data/lib/state_machine/event_collection.rb +5 -6
- data/lib/state_machine/integrations/active_record.rb +25 -16
- data/lib/state_machine/integrations/active_record/locale.rb +1 -0
- data/lib/state_machine/integrations/data_mapper.rb +6 -9
- data/lib/state_machine/integrations/data_mapper/observer.rb +5 -5
- data/lib/state_machine/integrations/sequel.rb +5 -7
- data/lib/state_machine/machine.rb +65 -55
- data/lib/state_machine/machine_collection.rb +15 -17
- data/lib/state_machine/matcher_helpers.rb +1 -0
- data/lib/state_machine/state.rb +6 -4
- data/lib/state_machine/state_collection.rb +3 -3
- data/lib/state_machine/transition.rb +3 -3
- data/test/unit/eval_helpers_test.rb +18 -0
- data/test/unit/event_collection_test.rb +30 -0
- data/test/unit/integrations/active_record_test.rb +78 -8
- data/test/unit/integrations/data_mapper_test.rb +77 -0
- data/test/unit/integrations/sequel_test.rb +54 -2
- data/test/unit/machine_collection_test.rb +20 -0
- data/test/unit/machine_test.rb +154 -5
- data/test/unit/state_test.rb +17 -3
- data/test/unit/transition_test.rb +27 -0
- metadata +2 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
== master
|
2
2
|
|
3
|
+
== 0.7.6 / 2009-06-17
|
4
|
+
|
5
|
+
* Allow multiple state machines on the same class to target the same attribute
|
6
|
+
* Add support for :attribute to customize the attribute target, assuming the name is the first argument of #state_machine
|
7
|
+
* Simplify reading from / writing to machine-related attributes on objects
|
8
|
+
* Fix locale for ActiveRecord getting added to the i18n load path multiple times [Reiner Dieterich]
|
9
|
+
* Fix callbacks, guards, and state-driven behaviors not always working on tainted classes [Brandon Dimcheff]
|
10
|
+
* Use Ruby 1.9's built-in Object#instance_exec for bound callbacks when it's available
|
11
|
+
* Improve performance of cached dynamic state lookups by 25%
|
12
|
+
|
3
13
|
== 0.7.5 / 2009-05-25
|
4
14
|
|
5
15
|
* Add built-in caching for dynamic state values when the value only needs to be generated once
|
data/README.rdoc
CHANGED
@@ -412,7 +412,7 @@ To generate multiple state machine graphs:
|
|
412
412
|
|
413
413
|
*Note* that this will generate a different file for every state machine defined
|
414
414
|
in the class. The generated files will use an output filename of the format
|
415
|
-
#{class_name}_#{
|
415
|
+
#{class_name}_#{machine_name}.#{format}.
|
416
416
|
|
417
417
|
For examples of actual images generated using this task, see those under the
|
418
418
|
examples folder.
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'rake/contrib/sshpublisher'
|
|
5
5
|
|
6
6
|
spec = Gem::Specification.new do |s|
|
7
7
|
s.name = 'state_machine'
|
8
|
-
s.version = '0.7.
|
8
|
+
s.version = '0.7.6'
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
10
|
s.summary = 'Adds support for creating state machines for attributes on any Ruby class'
|
11
11
|
s.description = s.summary
|
@@ -53,7 +53,14 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
53
53
|
rdoc.options << '--line-numbers' << '--inline-source'
|
54
54
|
rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb')
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
|
+
desc 'Generate a gemspec file.'
|
58
|
+
task :gemspec do
|
59
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
60
|
+
f.write spec.to_ruby
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
57
64
|
Rake::GemPackageTask.new(spec) do |p|
|
58
65
|
p.gem_spec = spec
|
59
66
|
p.need_tar = true
|
data/lib/state_machine.rb
CHANGED
@@ -5,10 +5,12 @@ require 'state_machine/machine'
|
|
5
5
|
# functionality on any Ruby class.
|
6
6
|
module StateMachine
|
7
7
|
module MacroMethods
|
8
|
-
# Creates a new state machine
|
9
|
-
#
|
8
|
+
# Creates a new state machine with the given name. The default name, if not
|
9
|
+
# specified, is <tt>:state</tt>.
|
10
10
|
#
|
11
11
|
# Configuration options:
|
12
|
+
# * <tt>:attribute</tt> - The name of the attribute to store the state value
|
13
|
+
# in. By default, this is the same as the name of the machine.
|
12
14
|
# * <tt>:initial</tt> - The initial state of the attribute. This can be a
|
13
15
|
# static state or a lambda block which will be evaluated at runtime
|
14
16
|
# (e.g. lambda {|vehicle| vehicle.speed == 0 ? :parked : :idling}).
|
@@ -17,8 +19,9 @@ module StateMachine
|
|
17
19
|
# transitions. Default is nil unless otherwise specified by the
|
18
20
|
# configured integration.
|
19
21
|
# * <tt>:namespace</tt> - The name to use for namespacing all generated
|
20
|
-
# instance methods (e.g. "heater" would generate
|
21
|
-
# :turn_off_heater for the :turn_on/:turn_off events).
|
22
|
+
# state / event instance methods (e.g. "heater" would generate
|
23
|
+
# :turn_on_heater and :turn_off_heater for the :turn_on/:turn_off events).
|
24
|
+
# Default is nil.
|
22
25
|
# * <tt>:integration</tt> - The name of the integration to use for adding
|
23
26
|
# library-specific behavior to the machine. Built-in integrations
|
24
27
|
# include :data_mapper, :active_record, and :sequel. By default, this
|
@@ -49,7 +52,7 @@ module StateMachine
|
|
49
52
|
#
|
50
53
|
# == Examples
|
51
54
|
#
|
52
|
-
# With the default attribute and no configuration:
|
55
|
+
# With the default name/attribute and no configuration:
|
53
56
|
#
|
54
57
|
# class Vehicle
|
55
58
|
# state_machine do
|
@@ -59,13 +62,14 @@ module StateMachine
|
|
59
62
|
# end
|
60
63
|
# end
|
61
64
|
#
|
62
|
-
# The above example will define a state machine
|
63
|
-
#
|
65
|
+
# The above example will define a state machine named "state" that will
|
66
|
+
# store the value in the +state+ attribute. Every vehicle will start
|
67
|
+
# without an initial state.
|
64
68
|
#
|
65
|
-
# With a custom attribute:
|
69
|
+
# With a custom name / attribute:
|
66
70
|
#
|
67
71
|
# class Vehicle
|
68
|
-
# state_machine :status do
|
72
|
+
# state_machine :status, :attribute => :status_value do
|
69
73
|
# ...
|
70
74
|
# end
|
71
75
|
# end
|
@@ -89,7 +93,8 @@ module StateMachine
|
|
89
93
|
# == Instance Methods
|
90
94
|
#
|
91
95
|
# The following instance methods will be automatically generated by the
|
92
|
-
# state machine. Any existing methods
|
96
|
+
# state machine based on the *name* of the machine. Any existing methods
|
97
|
+
# will not be overwritten.
|
93
98
|
# * <tt>state</tt> - Gets the current value for the attribute
|
94
99
|
# * <tt>state=(value)</tt> - Sets the current value for the attribute
|
95
100
|
# * <tt>state?(name)</tt> - Checks the given state name against the current
|
@@ -294,14 +299,14 @@ module StateMachine
|
|
294
299
|
#
|
295
300
|
# When a namespace is configured for a state machine, the name provided
|
296
301
|
# will be used in generating the instance methods for interacting with
|
297
|
-
# events
|
302
|
+
# states/events in the machine. This is particularly useful when a class
|
298
303
|
# has multiple state machines and it would be difficult to differentiate
|
299
304
|
# between the various states / events.
|
300
305
|
#
|
301
306
|
# For example,
|
302
307
|
#
|
303
308
|
# class Vehicle
|
304
|
-
# state_machine :heater_state, :initial => :off :namespace => 'heater' do
|
309
|
+
# state_machine :heater_state, :initial => :off, :namespace => 'heater' do
|
305
310
|
# event :turn_on do
|
306
311
|
# transition all => :on
|
307
312
|
# end
|
@@ -2,9 +2,8 @@ module StateMachine
|
|
2
2
|
# Provides a set of helper methods for making assertions about the content
|
3
3
|
# of various objects
|
4
4
|
module Assertions
|
5
|
-
# Validates that
|
6
|
-
#
|
7
|
-
# raised.
|
5
|
+
# Validates that the given hash *only* includes the specified valid keys.
|
6
|
+
# If any invalid keys are found, an ArgumentError will be raised.
|
8
7
|
#
|
9
8
|
# == Examples
|
10
9
|
#
|
@@ -18,8 +17,8 @@ module StateMachine
|
|
18
17
|
raise ArgumentError, "Invalid key(s): #{invalid_keys.join(', ')}" unless invalid_keys.empty?
|
19
18
|
end
|
20
19
|
|
21
|
-
# Validates that at *most* one of a set of
|
22
|
-
#
|
20
|
+
# Validates that the given hash only includes at *most* one of a set of
|
21
|
+
# exclusive keys. If more than one key is found, an ArgumentError will be
|
23
22
|
# raised.
|
24
23
|
#
|
25
24
|
# == Examples
|
@@ -106,9 +106,9 @@ module StateMachine
|
|
106
106
|
# * <tt>:bind_to_object</tt> - Whether to bind the callback to the object involved.
|
107
107
|
# If set to false, the object will be passed as a parameter instead.
|
108
108
|
# Default is integration-specific or set to the application default.
|
109
|
-
# * <tt>:terminator</tt> - A block/proc that determines what callback
|
110
|
-
# should cause the callback chain to halt (if not using the
|
111
|
-
# <tt>throw :halt</tt> technique).
|
109
|
+
# * <tt>:terminator</tt> - A block/proc that determines what callback
|
110
|
+
# results should cause the callback chain to halt (if not using the
|
111
|
+
# default <tt>throw :halt</tt> technique).
|
112
112
|
#
|
113
113
|
# More information about how those options affect the behavior of the
|
114
114
|
# callback can be found in their attribute definitions.
|
@@ -129,7 +129,6 @@ module StateMachine
|
|
129
129
|
end
|
130
130
|
|
131
131
|
@terminator = options.delete(:terminator)
|
132
|
-
|
133
132
|
@guard = Guard.new(options)
|
134
133
|
end
|
135
134
|
|
@@ -142,8 +141,8 @@ module StateMachine
|
|
142
141
|
# Runs the callback as long as the transition context matches the guard
|
143
142
|
# requirements configured for this callback.
|
144
143
|
#
|
145
|
-
# If a terminator has been configured and it matches the result from
|
146
|
-
#
|
144
|
+
# If a terminator has been configured and it matches the result from the
|
145
|
+
# evaluated method, then the callback chain should be halted
|
147
146
|
def call(object, context = {}, *args)
|
148
147
|
if @guard.matches?(object, context)
|
149
148
|
@methods.each do |method|
|
@@ -161,22 +160,29 @@ module StateMachine
|
|
161
160
|
# Generates a method that can be bound to the object being transitioned
|
162
161
|
# when the callback is invoked
|
163
162
|
def bound_method(block)
|
164
|
-
|
165
|
-
# This is essentially a workaround for not having Ruby 1.9's instance_exec
|
166
|
-
unbound_method = Object.class_eval do
|
167
|
-
time = Time.now
|
168
|
-
method_name = "__bind_#{time.to_i}_#{time.usec}"
|
169
|
-
define_method(method_name, &block)
|
170
|
-
method = instance_method(method_name)
|
171
|
-
remove_method(method_name)
|
172
|
-
method
|
173
|
-
end
|
174
|
-
arity = unbound_method.arity
|
163
|
+
arity = block.arity
|
175
164
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
165
|
+
if RUBY_VERSION >= '1.9'
|
166
|
+
lambda do |object, *args|
|
167
|
+
object.instance_exec(*(arity == 0 ? [] : args), &block)
|
168
|
+
end
|
169
|
+
else
|
170
|
+
# Generate a thread-safe unbound method that can be used on any object.
|
171
|
+
# This is a workaround for not having Ruby 1.9's instance_exec
|
172
|
+
unbound_method = Object.class_eval do
|
173
|
+
time = Time.now
|
174
|
+
method_name = "__bind_#{time.to_i}_#{time.usec}"
|
175
|
+
define_method(method_name, &block)
|
176
|
+
method = instance_method(method_name)
|
177
|
+
remove_method(method_name)
|
178
|
+
method
|
179
|
+
end
|
180
|
+
|
181
|
+
# Proxy calls to the method so that the method can be bound *and*
|
182
|
+
# the arguments are adjusted
|
183
|
+
lambda do |object, *args|
|
184
|
+
unbound_method.bind(object).call(*(arity == 0 ? [] : args))
|
185
|
+
end
|
180
186
|
end
|
181
187
|
end
|
182
188
|
end
|
@@ -2,7 +2,7 @@ require 'state_machine/eval_helpers'
|
|
2
2
|
|
3
3
|
module StateMachine
|
4
4
|
# Represents a type of module in which class-level methods are proxied to
|
5
|
-
# another class, injecting a custom
|
5
|
+
# another class, injecting a custom <tt>:if</tt> condition along with method.
|
6
6
|
#
|
7
7
|
# This is used for being able to automatically include conditionals which
|
8
8
|
# check the current state in class-level methods that have configuration
|
@@ -53,8 +53,7 @@ module StateMachine
|
|
53
53
|
def evaluate_method(object, method, *args)
|
54
54
|
case method
|
55
55
|
when Symbol
|
56
|
-
method
|
57
|
-
method.arity == 0 ? method.call : method.call(*args)
|
56
|
+
object.method(method).arity == 0 ? object.send(method) : object.send(method, *args)
|
58
57
|
when Proc, Method
|
59
58
|
args.unshift(object)
|
60
59
|
[0, 1].include?(method.arity) ? method.call(*args.slice(0, method.arity)) : method.call(*args)
|
data/lib/state_machine/event.rb
CHANGED
@@ -190,7 +190,7 @@ module StateMachine
|
|
190
190
|
if transition = transition_for(object)
|
191
191
|
transition.perform(*args)
|
192
192
|
else
|
193
|
-
machine.invalidate(object,
|
193
|
+
machine.invalidate(object, :state, :invalid_transition, [[:event, name]])
|
194
194
|
false
|
195
195
|
end
|
196
196
|
end
|
@@ -205,7 +205,7 @@ module StateMachine
|
|
205
205
|
guards.collect {|guard| guard.draw(graph, name, valid_states)}.flatten
|
206
206
|
end
|
207
207
|
|
208
|
-
# Generates a nicely formatted description of this
|
208
|
+
# Generates a nicely formatted description of this event's contents.
|
209
209
|
#
|
210
210
|
# For example,
|
211
211
|
#
|
@@ -244,7 +244,7 @@ module StateMachine
|
|
244
244
|
|
245
245
|
# Fires the event, raising an exception if it fails
|
246
246
|
machine.define_instance_method("#{qualified_name}!") do |machine, object, *args|
|
247
|
-
object.send(qualified_name, *args) || raise(StateMachine::InvalidTransition, "Cannot transition #{machine.
|
247
|
+
object.send(qualified_name, *args) || raise(StateMachine::InvalidTransition, "Cannot transition #{machine.name} via :#{name} from #{machine.states.match(object).name.inspect}")
|
248
248
|
end
|
249
249
|
end
|
250
250
|
end
|
@@ -92,18 +92,17 @@ module StateMachine
|
|
92
92
|
return unless machine.action
|
93
93
|
|
94
94
|
result = nil
|
95
|
-
attribute = machine.attribute
|
96
95
|
|
97
|
-
if
|
98
|
-
if event = self[
|
99
|
-
unless result =
|
96
|
+
if event_name = machine.read(object, :event)
|
97
|
+
if event = self[event_name.to_sym, :name]
|
98
|
+
unless result = machine.read(object, :event_transition) || event.transition_for(object)
|
100
99
|
# No valid transition: invalidate
|
101
|
-
machine.invalidate(object,
|
100
|
+
machine.invalidate(object, :event, :invalid_event, [[:state, machine.states.match!(object).name]]) if invalidate
|
102
101
|
result = false
|
103
102
|
end
|
104
103
|
else
|
105
104
|
# Event is unknown: invalidate
|
106
|
-
machine.invalidate(object,
|
105
|
+
machine.invalidate(object, :event, :invalid) if invalidate
|
107
106
|
result = false
|
108
107
|
end
|
109
108
|
end
|
@@ -84,14 +84,14 @@ module StateMachine
|
|
84
84
|
# you can build two state machines (one public and one protected) like so:
|
85
85
|
#
|
86
86
|
# class Vehicle < ActiveRecord::Base
|
87
|
-
# alias_attribute :public_state # Allow both machines to share the same state
|
88
87
|
# attr_protected :state_event # Prevent access to events in the first machine
|
89
88
|
#
|
90
89
|
# state_machine do
|
91
90
|
# # Define private events here
|
92
91
|
# end
|
93
92
|
#
|
94
|
-
#
|
93
|
+
# # Public machine targets the same state as the private machine
|
94
|
+
# state_machine :public_state, :attribute => :state do
|
95
95
|
# # Define public events here
|
96
96
|
# end
|
97
97
|
# end
|
@@ -274,11 +274,17 @@ module StateMachine
|
|
274
274
|
# Loads additional files specific to ActiveRecord
|
275
275
|
def self.extended(base) #:nodoc:
|
276
276
|
require 'state_machine/integrations/active_record/observer'
|
277
|
-
|
277
|
+
|
278
|
+
if Object.const_defined?(:I18n)
|
279
|
+
locale = "#{File.dirname(__FILE__)}/active_record/locale.rb"
|
280
|
+
I18n.load_path << locale unless I18n.load_path.include?(locale)
|
281
|
+
end
|
278
282
|
end
|
279
283
|
|
280
284
|
# Adds a validation error to the given object
|
281
285
|
def invalidate(object, attribute, message, values = [])
|
286
|
+
attribute = self.attribute(attribute)
|
287
|
+
|
282
288
|
if Object.const_defined?(:I18n)
|
283
289
|
options = values.inject({}) {|options, (key, value)| options[key] = value; options}
|
284
290
|
object.errors.add(attribute, message, options.merge(
|
@@ -304,8 +310,10 @@ module StateMachine
|
|
304
310
|
|
305
311
|
# Skips defining reader/writer methods since this is done automatically
|
306
312
|
def define_state_accessor
|
313
|
+
name = self.name
|
314
|
+
|
307
315
|
owner_class.validates_each(attribute) do |record, attr, value|
|
308
|
-
machine = record.class.state_machine(
|
316
|
+
machine = record.class.state_machine(name)
|
309
317
|
machine.invalidate(record, attr, :invalid) unless machine.states.match(record)
|
310
318
|
end
|
311
319
|
end
|
@@ -314,13 +322,13 @@ module StateMachine
|
|
314
322
|
# compatibility with the default predicate which determines whether
|
315
323
|
# *anything* is set for the attribute's value
|
316
324
|
def define_state_predicate
|
317
|
-
|
325
|
+
name = self.name
|
318
326
|
|
319
327
|
# Still use class_eval here instance of define_instance_method since
|
320
328
|
# we need to be able to call +super+
|
321
329
|
@instance_helper_module.class_eval do
|
322
|
-
define_method("#{
|
323
|
-
args.empty? ? super(*args) : self.class.state_machine(
|
330
|
+
define_method("#{name}?") do |*args|
|
331
|
+
args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
|
324
332
|
end
|
325
333
|
end
|
326
334
|
end
|
@@ -381,17 +389,18 @@ module StateMachine
|
|
381
389
|
# inheritance is respected properly.
|
382
390
|
def define_scope(name, scope)
|
383
391
|
name = name.to_sym
|
384
|
-
|
392
|
+
machine_name = self.name
|
385
393
|
|
386
|
-
#
|
394
|
+
# Create the scope and then override it with state translation
|
387
395
|
owner_class.named_scope(name)
|
388
396
|
owner_class.scopes[name] = lambda do |klass, *states|
|
389
|
-
machine_states = klass.state_machine(
|
397
|
+
machine_states = klass.state_machine(machine_name).states
|
390
398
|
values = states.flatten.map {|state| machine_states.fetch(state).value}
|
391
399
|
|
392
400
|
::ActiveRecord::NamedScope::Scope.new(klass, scope.call(values))
|
393
401
|
end
|
394
402
|
|
403
|
+
# Prevent the Machine class from wrapping the scope
|
395
404
|
false
|
396
405
|
end
|
397
406
|
|
@@ -402,22 +411,22 @@ module StateMachine
|
|
402
411
|
# * #{type}_#{qualified_event}_from_#{from}
|
403
412
|
# * #{type}_#{qualified_event}_to_#{to}
|
404
413
|
# * #{type}_#{qualified_event}
|
405
|
-
# * #{type}_transition_#{
|
406
|
-
# * #{type}_transition_#{
|
407
|
-
# * #{type}_transition_#{
|
408
|
-
# * #{type}_transition_#{
|
414
|
+
# * #{type}_transition_#{machine_name}_from_#{from}_to_#{to}
|
415
|
+
# * #{type}_transition_#{machine_name}_from_#{from}
|
416
|
+
# * #{type}_transition_#{machine_name}_to_#{to}
|
417
|
+
# * #{type}_transition_#{machine_name}
|
409
418
|
# * #{type}_transition
|
410
419
|
#
|
411
420
|
# This will always return true regardless of the results of the
|
412
421
|
# callbacks.
|
413
422
|
def notify(type, object, transition)
|
414
|
-
|
423
|
+
name = self.name
|
415
424
|
event = transition.qualified_event
|
416
425
|
from = transition.from_name
|
417
426
|
to = transition.to_name
|
418
427
|
|
419
428
|
# Machine-specific updates
|
420
|
-
["#{type}_#{event}", "#{type}_transition_#{
|
429
|
+
["#{type}_#{event}", "#{type}_transition_#{name}"].each do |event_segment|
|
421
430
|
["_from_#{from}", nil].each do |from_segment|
|
422
431
|
["_to_#{to}", nil].each do |to_segment|
|
423
432
|
object.class.changed
|
@@ -2,6 +2,7 @@
|
|
2
2
|
:activerecord => {
|
3
3
|
:errors => {
|
4
4
|
:messages => {
|
5
|
+
:invalid => StateMachine::Machine.default_messages[:invalid],
|
5
6
|
:invalid_event => StateMachine::Machine.default_messages[:invalid_event] % ['{{state}}'],
|
6
7
|
:invalid_transition => StateMachine::Machine.default_messages[:invalid_transition] % ['{{event}}']
|
7
8
|
}
|
@@ -94,16 +94,13 @@ module StateMachine
|
|
94
94
|
# include DataMapper::Resource
|
95
95
|
# ...
|
96
96
|
#
|
97
|
-
# # Allow both machines to share the same state
|
98
|
-
# alias_method :public_state, :state
|
99
|
-
# alias_method :public_state=, :state=
|
100
|
-
#
|
101
97
|
# state_machine do
|
102
98
|
# # Define private events here
|
103
99
|
# end
|
104
100
|
# protected :state_event= # Prevent access to events in the first machine
|
105
101
|
#
|
106
|
-
#
|
102
|
+
# # Allow both machines to share the same state
|
103
|
+
# state_machine :public_state, :attribute => :state do
|
107
104
|
# # Define public events here
|
108
105
|
# end
|
109
106
|
# end
|
@@ -112,7 +109,7 @@ module StateMachine
|
|
112
109
|
#
|
113
110
|
# By default, the use of transactions during an event transition is
|
114
111
|
# turned off to be consistent with DataMapper. This means that if
|
115
|
-
# changes are made to the database during a before callback, but the
|
112
|
+
# changes are made to the database during a before callback, but the
|
116
113
|
# transition fails to complete, those changes will *not* be rolled back.
|
117
114
|
#
|
118
115
|
# For example,
|
@@ -255,7 +252,7 @@ module StateMachine
|
|
255
252
|
|
256
253
|
# Adds a validation error to the given object
|
257
254
|
def invalidate(object, attribute, message, values = [])
|
258
|
-
object.errors.add(attribute, generate_message(message, values)) if supports_validations?
|
255
|
+
object.errors.add(self.attribute(attribute), generate_message(message, values)) if supports_validations?
|
259
256
|
end
|
260
257
|
|
261
258
|
# Resets any errors previously added when invalidating the given object
|
@@ -274,9 +271,9 @@ module StateMachine
|
|
274
271
|
owner_class.property(attribute, String) unless owner_class.properties.has_property?(attribute)
|
275
272
|
|
276
273
|
if supports_validations?
|
277
|
-
|
274
|
+
name = self.name
|
278
275
|
owner_class.validates_with_block(attribute) do
|
279
|
-
machine = self.class.state_machine(
|
276
|
+
machine = self.class.state_machine(name)
|
280
277
|
machine.states.match(self) ? true : [false, machine.generate_message(:invalid)]
|
281
278
|
end
|
282
279
|
end
|