state_machine 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +7 -0
- data/Rakefile +1 -1
- data/lib/state_machine.rb +2 -2
- data/lib/state_machine/callback.rb +2 -2
- data/lib/state_machine/initializers/rails.rb +0 -2
- data/lib/state_machine/integrations/active_model.rb +2 -2
- data/lib/state_machine/integrations/active_record.rb +1 -0
- data/lib/state_machine/integrations/data_mapper.rb +14 -7
- data/lib/state_machine/transition.rb +62 -36
- data/test/unit/integrations/active_record_test.rb +2 -3
- data/test/unit/integrations/data_mapper_test.rb +6 -2
- metadata +2 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
== master
|
2
2
|
|
3
|
+
== 0.9.1 / 2010-05-02
|
4
|
+
|
5
|
+
* Fix ActiveRecord 2.0.0 - 2.2.3 integrations failing if version info isn't already loaded
|
6
|
+
* Fix integration with dirty attribute tracking on DataMapper 0.10.3
|
7
|
+
* Fix observers failing in ActiveRecord 3.0.0.beta4+ integrations
|
8
|
+
* Fix deprecation warning in Rails 3 railtie [Chris Yuan]
|
9
|
+
|
3
10
|
== 0.9.0 / 2010-04-12
|
4
11
|
|
5
12
|
* Use attribute-based event transitions whenever possible to ensure consistency
|
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.1'
|
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
|
data/lib/state_machine.rb
CHANGED
@@ -295,8 +295,8 @@ module StateMachine
|
|
295
295
|
#
|
296
296
|
# Within the +state_machine+ block, you can also define callbacks for
|
297
297
|
# transitions. For more information about defining these callbacks,
|
298
|
-
# see StateMachine::Machine#before_transition
|
299
|
-
# StateMachine::Machine#
|
298
|
+
# see StateMachine::Machine#before_transition, StateMachine::Machine#after_transition,
|
299
|
+
# and StateMachine::Machine#around_transition.
|
300
300
|
#
|
301
301
|
# == Namespaces
|
302
302
|
#
|
@@ -204,8 +204,8 @@ module StateMachine
|
|
204
204
|
def bound_method(block)
|
205
205
|
type = self.type
|
206
206
|
arity = block.arity
|
207
|
-
arity += 1 if arity >= 0
|
208
|
-
arity += 1 if arity == 1 && type == :around
|
207
|
+
arity += 1 if arity >= 0 # Make sure the object gets passed
|
208
|
+
arity += 1 if arity == 1 && type == :around # Make sure the block gets passed
|
209
209
|
|
210
210
|
method = if RUBY_VERSION >= '1.9'
|
211
211
|
lambda do |object, *args|
|
@@ -397,14 +397,14 @@ module StateMachine
|
|
397
397
|
["#{type}_#{event}", "#{type}_transition_#{name}"].each do |event_segment|
|
398
398
|
["_from_#{from}", nil].each do |from_segment|
|
399
399
|
["_to_#{to}", nil].each do |to_segment|
|
400
|
-
object.class.changed
|
400
|
+
object.class.changed if object.class.respond_to?(:changed)
|
401
401
|
object.class.notify_observers([event_segment, from_segment, to_segment].join, object, transition)
|
402
402
|
end
|
403
403
|
end
|
404
404
|
end
|
405
405
|
|
406
406
|
# Generic updates
|
407
|
-
object.class.changed
|
407
|
+
object.class.changed if object.class.respond_to?(:changed)
|
408
408
|
object.class.notify_observers("#{type}_transition", object, transition)
|
409
409
|
|
410
410
|
true
|
@@ -261,18 +261,25 @@ module StateMachine
|
|
261
261
|
# Forces the change in state to be recognized regardless of whether the
|
262
262
|
# state value actually changed
|
263
263
|
def write(object, attribute, value)
|
264
|
-
|
265
|
-
|
266
|
-
if ::DataMapper::VERSION =~ /^0
|
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
|
+
result = super
|
271
|
+
|
272
|
+
# Change original attributes in 0.9.4 - 0.10.2
|
273
|
+
if ::DataMapper::VERSION =~ /^0\.9\./
|
267
274
|
object.original_values[self.attribute] = "#{value}-ignored" if object.original_values[self.attribute] == value
|
268
|
-
|
269
|
-
# Force the resource's state to Dirty for 0.10.13+
|
270
|
-
object.persisted_state = ::DataMapper::Resource::State::Dirty.new(object) unless ::DataMapper::VERSION =~ /^0\.10\.[0-2]$/ || object.persisted_state.respond_to?(:original_attributes)
|
271
|
-
|
275
|
+
elsif ::DataMapper::VERSION =~ /^0\.10\.[0-2]$/
|
272
276
|
property = owner_class.properties[self.attribute]
|
273
277
|
object.original_attributes[property] = "#{value}-ignored" unless object.original_attributes.include?(property)
|
274
278
|
end
|
279
|
+
else
|
280
|
+
result = super
|
275
281
|
end
|
282
|
+
|
276
283
|
result
|
277
284
|
end
|
278
285
|
|
@@ -167,23 +167,12 @@ module StateMachine
|
|
167
167
|
options = {:after => true}.merge(options)
|
168
168
|
@success = false
|
169
169
|
|
170
|
-
|
171
|
-
# any around callbacks.
|
172
|
-
begin
|
173
|
-
halted = !catch(:halt) { before(options[:after], &block); true }
|
174
|
-
rescue Exception => error
|
175
|
-
raise unless @after_block
|
176
|
-
end
|
170
|
+
halted = pausable { before(options[:after], &block) }
|
177
171
|
|
178
172
|
# After callbacks are only run if:
|
179
|
-
# * There isn't an after block already running
|
180
173
|
# * An around callback didn't halt after yielding
|
181
174
|
# * They're enabled or the run didn't succeed
|
182
|
-
if @
|
183
|
-
@after_block.call(halted, error)
|
184
|
-
elsif !(@before_run && halted) && (options[:after] || !@success)
|
185
|
-
after
|
186
|
-
end
|
175
|
+
after if !(@before_run && halted) && (options[:after] || !@success)
|
187
176
|
|
188
177
|
@before_run
|
189
178
|
end
|
@@ -247,7 +236,7 @@ module StateMachine
|
|
247
236
|
# the state has already been persisted
|
248
237
|
def reset
|
249
238
|
@before_run = @persisted = @after_run = false
|
250
|
-
@
|
239
|
+
@paused_block = nil
|
251
240
|
end
|
252
241
|
|
253
242
|
# Generates a nicely formatted description of this transitions's contents.
|
@@ -261,6 +250,57 @@ module StateMachine
|
|
261
250
|
end
|
262
251
|
|
263
252
|
private
|
253
|
+
# Runs a block that may get paused. If the block doesn't pause, then
|
254
|
+
# execution will continue as normal. If the block gets paused, then it
|
255
|
+
# will take care of switching the execution context when it's resumed.
|
256
|
+
#
|
257
|
+
# This will return true if the given block halts for a reason other than
|
258
|
+
# getting paused.
|
259
|
+
def pausable
|
260
|
+
begin
|
261
|
+
halted = !catch(:halt) { yield; true }
|
262
|
+
rescue Exception => error
|
263
|
+
raise unless @resume_block
|
264
|
+
end
|
265
|
+
|
266
|
+
if @resume_block
|
267
|
+
@resume_block.call(halted, error)
|
268
|
+
else
|
269
|
+
halted
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Pauses the current callback execution. This should only occur within
|
274
|
+
# around callbacks when the remainder of the callback will be executed at
|
275
|
+
# a later point in time.
|
276
|
+
def pause
|
277
|
+
unless @resume_block
|
278
|
+
require 'continuation' unless defined?(callcc)
|
279
|
+
callcc do |block|
|
280
|
+
@paused_block = block
|
281
|
+
throw :halt, true
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# Resumes the execution of a previously paused callback execution. Once
|
287
|
+
# the paused callbacks complete, the current execution will continue.
|
288
|
+
def resume
|
289
|
+
if @paused_block
|
290
|
+
halted, error = callcc do |block|
|
291
|
+
@resume_block = block
|
292
|
+
@paused_block.call
|
293
|
+
end
|
294
|
+
|
295
|
+
@resume_block = @paused_block = nil
|
296
|
+
|
297
|
+
raise error if error
|
298
|
+
!halted
|
299
|
+
else
|
300
|
+
true
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
264
304
|
# Runs the machine's +before+ callbacks for this transition. Only
|
265
305
|
# callbacks that are configured to match the event, from state, and to
|
266
306
|
# state will be invoked.
|
@@ -278,16 +318,12 @@ module StateMachine
|
|
278
318
|
# * The block fails and the callback doesn't run on failures OR
|
279
319
|
# * The block succeeds, but after callbacks are disabled (in which
|
280
320
|
# case a continuation is stored for later execution)
|
281
|
-
return if catch(:
|
321
|
+
return if catch(:cancel) do
|
282
322
|
callback.call(object, context, self) do
|
283
323
|
before(complete, index, &block)
|
284
324
|
|
285
|
-
if @success && !complete
|
286
|
-
|
287
|
-
callcc {|block| @around_block = block}
|
288
|
-
end
|
289
|
-
|
290
|
-
throw :pause, true if @around_block && !@after_block || !callback.matches_success?(@success)
|
325
|
+
pause if @success && !complete
|
326
|
+
throw :cancel, true unless callback.matches_success?(@success)
|
291
327
|
end
|
292
328
|
end
|
293
329
|
else
|
@@ -318,22 +354,12 @@ module StateMachine
|
|
318
354
|
# should never halt the execution of a +perform+.
|
319
355
|
def after
|
320
356
|
unless @after_run
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
@around_block.call
|
327
|
-
end
|
328
|
-
|
329
|
-
@after_block = @around_block = nil
|
330
|
-
raise error if error
|
331
|
-
throw :halt if halted
|
357
|
+
# First resume previously paused callbacks
|
358
|
+
if resume
|
359
|
+
catch(:halt) do
|
360
|
+
after_context = context.merge(:success => @success)
|
361
|
+
machine.callbacks[:after].each {|callback| callback.call(object, after_context, self)}
|
332
362
|
end
|
333
|
-
|
334
|
-
# Call normal after callbacks in order
|
335
|
-
after_context = context.merge(:success => @success)
|
336
|
-
machine.callbacks[:after].each {|callback| callback.call(object, after_context, self)}
|
337
363
|
end
|
338
364
|
|
339
365
|
@after_run = true
|
@@ -12,10 +12,9 @@ FIXTURES_ROOT = File.dirname(__FILE__) + '/../../fixtures/'
|
|
12
12
|
require 'active_support/test_case'
|
13
13
|
require 'active_record/fixtures'
|
14
14
|
|
15
|
-
|
16
|
-
if ActiveRecord::VERSION::STRING >= '2.1.0'
|
15
|
+
begin
|
17
16
|
require 'active_record/test_case'
|
18
|
-
|
17
|
+
rescue LoadError
|
19
18
|
class ActiveRecord::TestCase < ActiveSupport::TestCase
|
20
19
|
self.fixture_path = FIXTURES_ROOT
|
21
20
|
self.use_instantiated_fixtures = false
|
@@ -512,7 +512,9 @@ module DataMapperTest
|
|
512
512
|
end
|
513
513
|
|
514
514
|
def test_should_track_attribute_change
|
515
|
-
if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.
|
515
|
+
if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.3')
|
516
|
+
assert_equal e = {@resource.properties[:state] => 'parked'}, @record.original_attributes
|
517
|
+
elsif Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
|
516
518
|
assert_equal e = {@resource.properties[:state] => 'parked-ignored'}, @record.original_attributes
|
517
519
|
else
|
518
520
|
assert_equal e = {:state => 'parked-ignored'}, @record.original_values
|
@@ -580,7 +582,9 @@ module DataMapperTest
|
|
580
582
|
end
|
581
583
|
|
582
584
|
def test_should_track_attribute_changes
|
583
|
-
if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.
|
585
|
+
if Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.3')
|
586
|
+
assert_equal e = {@resource.properties[:status] => 'parked'}, @record.original_attributes
|
587
|
+
elsif Gem::Version.new(::DataMapper::VERSION) >= Gem::Version.new('0.10.0')
|
584
588
|
assert_equal e = {@resource.properties[:status] => 'parked-ignored'}, @record.original_attributes
|
585
589
|
else
|
586
590
|
assert_equal e = {:status => 'parked-ignored'}, @record.original_values
|
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.1
|
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-05-02 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|