state_machine 0.7.5 → 0.7.6
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.
- 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
|