state_machine 0.9.3 → 0.9.4
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 +8 -0
- data/Rakefile +1 -1
- data/lib/state_machine/integrations/active_model.rb +9 -2
- data/lib/state_machine/integrations/active_record.rb +25 -11
- data/lib/state_machine/integrations/data_mapper.rb +5 -6
- data/lib/state_machine/integrations/sequel.rb +12 -2
- data/lib/state_machine/machine.rb +21 -9
- data/lib/state_machine/transition.rb +4 -2
- data/test/unit/integrations/data_mapper_test.rb +3 -2
- metadata +2 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
== master
|
2
2
|
|
3
|
+
== 0.9.4 / 2010-08-01
|
4
|
+
|
5
|
+
* Fix validation / save hooks in Sequel 3.14.0+
|
6
|
+
* Fix integration with dirty attribute tracking on DataMapper 1.0.1+
|
7
|
+
* Fix DataMapper 1.0.1+ tests producing warnings
|
8
|
+
* Fix validation error warnings in ActiveModel / ActiveRecord 3.0.0 beta5+
|
9
|
+
* Fix mass-assignment sanitization breaking in ActiveRecord 3.0.0 beta5+ [Akira Matsuda]
|
10
|
+
|
3
11
|
== 0.9.3 / 2010-06-26
|
4
12
|
|
5
13
|
* Allow access to human state / event names in transitions and for the current state
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'rake/gempackagetask'
|
|
6
6
|
|
7
7
|
spec = Gem::Specification.new do |s|
|
8
8
|
s.name = 'state_machine'
|
9
|
-
s.version = '0.9.
|
9
|
+
s.version = '0.9.4'
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.summary = 'Adds support for creating state machines for attributes on any Ruby class'
|
12
12
|
s.description = s.summary
|
@@ -222,7 +222,7 @@ module StateMachine
|
|
222
222
|
def extended(base) #:nodoc:
|
223
223
|
require 'state_machine/integrations/active_model/observer'
|
224
224
|
|
225
|
-
if
|
225
|
+
if defined?(I18n)
|
226
226
|
locale = "#{File.dirname(__FILE__)}/active_model/locale.rb"
|
227
227
|
I18n.load_path.unshift(locale) unless I18n.load_path.include?(locale)
|
228
228
|
end
|
@@ -267,7 +267,8 @@ module StateMachine
|
|
267
267
|
options
|
268
268
|
end
|
269
269
|
|
270
|
-
object
|
270
|
+
default_options = default_error_message_options(object, attribute, message)
|
271
|
+
object.errors.add(attribute, message, options.merge(default_options))
|
271
272
|
end
|
272
273
|
end
|
273
274
|
|
@@ -312,6 +313,12 @@ module StateMachine
|
|
312
313
|
owner_class.i18n_scope
|
313
314
|
end
|
314
315
|
|
316
|
+
# The default options to use when generating messages for validation
|
317
|
+
# errors
|
318
|
+
def default_error_message_options(object, attribute, message)
|
319
|
+
{:message => @messages[message]}
|
320
|
+
end
|
321
|
+
|
315
322
|
# Translates the given key / value combo. Translation keys are looked
|
316
323
|
# up in the following order:
|
317
324
|
# * <tt>#{i18n_scope}.state_machines.#{model_name}.#{machine_name}.#{plural_key}.#{value}</tt>
|
@@ -331,9 +331,9 @@ module StateMachine
|
|
331
331
|
|
332
332
|
::ActiveRecord::Observer.class_eval do
|
333
333
|
include StateMachine::Integrations::ActiveModel::Observer
|
334
|
-
end unless ::ActiveRecord::Observer
|
334
|
+
end unless ::ActiveRecord::Observer < StateMachine::Integrations::ActiveModel::Observer
|
335
335
|
|
336
|
-
if
|
336
|
+
if defined?(I18n)
|
337
337
|
locale = "#{File.dirname(__FILE__)}/active_record/locale.rb"
|
338
338
|
I18n.load_path.unshift(locale) unless I18n.load_path.include?(locale)
|
339
339
|
end
|
@@ -341,7 +341,7 @@ module StateMachine
|
|
341
341
|
|
342
342
|
# Adds a validation error to the given object
|
343
343
|
def invalidate(object, attribute, message, values = [])
|
344
|
-
if
|
344
|
+
if defined?(I18n)
|
345
345
|
super
|
346
346
|
else
|
347
347
|
object.errors.add(self.attribute(attribute), generate_message(message, values))
|
@@ -366,7 +366,7 @@ module StateMachine
|
|
366
366
|
|
367
367
|
# Only adds dirty tracking support if ActiveRecord supports it
|
368
368
|
def supports_dirty_tracking?(object)
|
369
|
-
defined?(::ActiveRecord::Dirty) && object.respond_to?("#{
|
369
|
+
defined?(::ActiveRecord::Dirty) && object.respond_to?("#{attribute}_changed?") || super
|
370
370
|
end
|
371
371
|
|
372
372
|
# Always uses the <tt>:activerecord</tt> translation scope
|
@@ -374,9 +374,19 @@ module StateMachine
|
|
374
374
|
:activerecord
|
375
375
|
end
|
376
376
|
|
377
|
+
# The default options to use when generating messages for validation
|
378
|
+
# errors
|
379
|
+
def default_error_message_options(object, attribute, message)
|
380
|
+
if ::ActiveRecord::VERSION::MAJOR >= 3
|
381
|
+
super
|
382
|
+
else
|
383
|
+
{:default => @messages[message]}
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
377
387
|
# Only allows translation of I18n is available
|
378
388
|
def translate(klass, key, value)
|
379
|
-
if
|
389
|
+
if defined?(I18n)
|
380
390
|
super
|
381
391
|
else
|
382
392
|
value ? value.to_s.humanize.downcase : 'nil'
|
@@ -384,9 +394,9 @@ module StateMachine
|
|
384
394
|
end
|
385
395
|
|
386
396
|
# Attempts to look up a class's ancestors via:
|
387
|
-
# * #lookup_ancestors
|
388
|
-
# * #self_and_descendants_from_active_record
|
389
|
-
# * #self_and_descendents_from_active_record
|
397
|
+
# * #lookup_ancestors (3.0.0+)
|
398
|
+
# * #self_and_descendants_from_active_record (2.3.2 - 2.3.x)
|
399
|
+
# * #self_and_descendents_from_active_record (2.0.0 - 2.3.1)
|
390
400
|
def ancestors_for(klass)
|
391
401
|
if ::ActiveRecord::VERSION::MAJOR >= 3
|
392
402
|
super
|
@@ -415,12 +425,16 @@ module StateMachine
|
|
415
425
|
if new_record? && !@initialized_state_machines
|
416
426
|
@initialized_state_machines = true
|
417
427
|
|
418
|
-
if new_attributes
|
428
|
+
ignore = if new_attributes
|
419
429
|
attributes = new_attributes.dup
|
420
430
|
attributes.stringify_keys!
|
421
|
-
|
431
|
+
if ::ActiveRecord::VERSION::MAJOR >= 3
|
432
|
+
sanitize_for_mass_assignment(attributes).keys
|
433
|
+
else
|
434
|
+
remove_attributes_protected_from_mass_assignment(attributes).keys
|
435
|
+
end
|
422
436
|
else
|
423
|
-
|
437
|
+
[]
|
424
438
|
end
|
425
439
|
|
426
440
|
initialize_state_machines(:dynamic => false, :ignore => ignore)
|
@@ -262,19 +262,18 @@ module StateMachine
|
|
262
262
|
# state value actually changed
|
263
263
|
def write(object, attribute, value)
|
264
264
|
if attribute == :state
|
265
|
-
# Force to Dirty state in 0.10.3+
|
266
|
-
if !(::DataMapper::VERSION =~ /^0\.(9\.|10\.[0-2]$)/)
|
267
|
-
object.persisted_state = ::DataMapper::Resource::State::Dirty.new(object) if object.persisted_state.is_a?(::DataMapper::Resource::State::Clean)
|
268
|
-
end
|
269
|
-
|
270
265
|
result = super
|
271
266
|
|
272
267
|
# Change original attributes in 0.9.4 - 0.10.2
|
273
268
|
if ::DataMapper::VERSION =~ /^0\.9\./
|
274
269
|
object.original_values[self.attribute] = "#{value}-ignored" if object.original_values[self.attribute] == value
|
275
|
-
elsif ::DataMapper::VERSION =~ /^0\.10
|
270
|
+
elsif ::DataMapper::VERSION =~ /^0\.10\./
|
276
271
|
property = owner_class.properties[self.attribute]
|
277
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)
|
278
277
|
end
|
279
278
|
else
|
280
279
|
result = super
|
@@ -297,7 +297,11 @@ module StateMachine
|
|
297
297
|
super(*args)
|
298
298
|
end
|
299
299
|
|
300
|
-
|
300
|
+
if defined?(::Sequel::MAJOR) && (::Sequel::MAJOR > 3 || ::Sequel::MAJOR == 3 && ::Sequel::MINOR > 13)
|
301
|
+
raise_on_failure?(args.first || {}) && !yielded && !result ? raise_hook_failure(:validation) : result
|
302
|
+
else
|
303
|
+
raise_on_save_failure && !yielded && !result ? save_failure(:validation) : result
|
304
|
+
end
|
301
305
|
end
|
302
306
|
|
303
307
|
define_method(defined?(::Sequel::MAJOR) && (::Sequel::MAJOR >= 3 || ::Sequel::MAJOR == 2 && ::Sequel::MINOR == 12) ? :_save : :save) do |*args|
|
@@ -307,7 +311,13 @@ module StateMachine
|
|
307
311
|
super(*args)
|
308
312
|
end
|
309
313
|
|
310
|
-
yielded || result
|
314
|
+
if yielded || result
|
315
|
+
result
|
316
|
+
elsif defined?(::Sequel::MAJOR) && (::Sequel::MAJOR > 3 || ::Sequel::MAJOR == 3 && ::Sequel::MINOR > 13)
|
317
|
+
raise_hook_failure(:save)
|
318
|
+
else
|
319
|
+
save_failure(:save)
|
320
|
+
end
|
311
321
|
end
|
312
322
|
end unless owner_class.state_machines.any? {|name, machine| machine.action == :save && machine != self}
|
313
323
|
else
|
@@ -483,7 +483,7 @@ module StateMachine
|
|
483
483
|
end
|
484
484
|
|
485
485
|
# Add class-/instance-level methods to the owner class for state initialization
|
486
|
-
unless owner_class
|
486
|
+
unless owner_class < StateMachine::InstanceMethods
|
487
487
|
owner_class.class_eval do
|
488
488
|
extend StateMachine::ClassMethods
|
489
489
|
include StateMachine::InstanceMethods
|
@@ -928,11 +928,15 @@ module StateMachine
|
|
928
928
|
# * <tt>park_transition</tt> - Gets the next transition that would be
|
929
929
|
# performed if the "park" event were to be fired now on the object or nil
|
930
930
|
# if no transitions can be performed.
|
931
|
-
# * <tt>park(run_action = true)</tt> - Fires the "park" event,
|
932
|
-
# from the current state to the next valid state.
|
933
|
-
#
|
934
|
-
#
|
935
|
-
#
|
931
|
+
# * <tt>park(..., run_action = true)</tt> - Fires the "park" event,
|
932
|
+
# transitioning from the current state to the next valid state. If the
|
933
|
+
# last argument is a boolean, it will control whether the machine's action
|
934
|
+
# gets run.
|
935
|
+
# * <tt>park!(..., run_action = true)</tt> - Fires the "park" event,
|
936
|
+
# transitioning from the current state to the next valid state. If the
|
937
|
+
# transition fails, then a StateMachine::InvalidTransition error will be
|
938
|
+
# raised. If the last argument is a boolean, it will control whether the
|
939
|
+
# machine's action gets run.
|
936
940
|
#
|
937
941
|
# With a namespace of "car", the above names map to the following methods:
|
938
942
|
# * <tt>can_park_car?</tt>
|
@@ -994,15 +998,23 @@ module StateMachine
|
|
994
998
|
# end
|
995
999
|
# end
|
996
1000
|
#
|
997
|
-
# Note that +super+ is called instead of <tt>super(*args)</tt>. This
|
998
|
-
#
|
999
|
-
#
|
1001
|
+
# Note that +super+ is called instead of <tt>super(*args)</tt>. This allows
|
1002
|
+
# the entire arguments list to be accessed by transition callbacks through
|
1003
|
+
# StateMachine::Transition#args like so:
|
1000
1004
|
#
|
1001
1005
|
# after_transition :on => :park do |vehicle, transition|
|
1002
1006
|
# kind = *transition.args
|
1003
1007
|
# ...
|
1004
1008
|
# end
|
1005
1009
|
#
|
1010
|
+
# *Remember* that if the last argument is a boolean, it will be used as the
|
1011
|
+
# +run_action+ parameter to the event action. Using the +park+ action
|
1012
|
+
# example from above, you can might call it like so:
|
1013
|
+
#
|
1014
|
+
# vehicle.park # => Uses default args and runs machine action
|
1015
|
+
# vehicle.park(:parallel) # => Specifies the +kind+ argument and runs the machine action
|
1016
|
+
# vehicle.park(:parallel, false) # => Specifies the +kind+ argument and *skips* the machine action
|
1017
|
+
#
|
1006
1018
|
# == Example
|
1007
1019
|
#
|
1008
1020
|
# class Vehicle
|
@@ -150,8 +150,10 @@ module StateMachine
|
|
150
150
|
#
|
151
151
|
# vehicle = Vehicle.new
|
152
152
|
# transition = StateMachine::Transition.new(vehicle, machine, :ignite, :parked, :idling)
|
153
|
-
# transition.perform
|
154
|
-
# transition.perform(false)
|
153
|
+
# transition.perform # => Runs the +save+ action after setting the state attribute
|
154
|
+
# transition.perform(false) # => Only sets the state attribute
|
155
|
+
# transition.perform(Time.now) # => Passes in additional arguments and runs the +save+ action
|
156
|
+
# transition.perform(Time.now, false) # => Passes in additional arguments and only sets the state attribute
|
155
157
|
def perform(*args)
|
156
158
|
run_action = [true, false].include?(args.last) ? args.pop : true
|
157
159
|
self.args = args
|
@@ -26,13 +26,14 @@ module DataMapperTest
|
|
26
26
|
def new_resource(create_table = :foo, &block)
|
27
27
|
table_name = create_table || :foo
|
28
28
|
|
29
|
-
resource = Class.new
|
29
|
+
resource = Class.new
|
30
|
+
resource.class_eval do
|
30
31
|
include DataMapper::Resource
|
31
32
|
|
32
33
|
storage_names[:default] = table_name.to_s
|
33
34
|
def self.name; "DataMapperTest::#{storage_names[:default].capitalize}"; end
|
34
35
|
|
35
|
-
property :id,
|
36
|
+
property :id, resource.class_eval('Serial')
|
36
37
|
property :state, String
|
37
38
|
|
38
39
|
auto_migrate! if create_table
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: state_machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Pfeifer
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-08-01 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|