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 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.0'
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 and
299
- # StateMachine::Machine#after_transition.
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|
@@ -1,6 +1,4 @@
1
1
  class StateMachine::Railtie < Rails::Railtie
2
- railtie_name :state_machine
3
-
4
2
  rake_tasks do
5
3
  load 'tasks/state_machine.rb'
6
4
  end
@@ -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
@@ -323,6 +323,7 @@ module StateMachine
323
323
  end
324
324
 
325
325
  def self.extended(base) #:nodoc:
326
+ require 'active_record/version'
326
327
  require 'state_machine/integrations/active_model/observer'
327
328
 
328
329
  ::ActiveRecord::Observer.class_eval do
@@ -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
- result = super
265
- if attribute == :state && owner_class.properties.detect {|property| property.name == self.attribute}
266
- if ::DataMapper::VERSION =~ /^0\.\d\./ # Match anything < 0.10
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
- else
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
- # Run before callbacks. :halt is caught here so that it rolls up through
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 @after_block
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
- @around_block = nil
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(:pause) do
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 && !@around_block && !@after_block
286
- require 'continuation' unless defined?(callcc)
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
- catch(:halt) do
322
- # First call any yielded around blocks
323
- if @around_block
324
- halted, error = callcc do |block|
325
- @after_block = block
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
- require 'active_record/version'
16
- if ActiveRecord::VERSION::STRING >= '2.1.0'
15
+ begin
17
16
  require 'active_record/test_case'
18
- else
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.0')
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.0')
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.0
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-04-12 00:00:00 -04:00
12
+ date: 2010-05-02 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15