state_machines 0.100.1 → 0.100.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a87715920a8f37a43cbac61b18e9a1c91d72b4b2d2228c3dc275dc05781efe9f
4
- data.tar.gz: 7e098b96739caf0b91010ae21c626109b58fbd7325bfa309c0518055e040bcbb
3
+ metadata.gz: 7b5d883e6f3259de8981a9855e50d6f2d859bbd7e9e10a4cc5b4d91ae3fec65f
4
+ data.tar.gz: 9185125133d36eeed7ae95875a825e0e9a2e4d73b7de286c65be589965d2613d
5
5
  SHA512:
6
- metadata.gz: 7c4905db4ba8245378cb89118143432d0fd009cefbef97846bbd12608c2dbb8b55627dceb71c2a044933acb3aeae4d57c21aa16c687b32883e18789b207da088
7
- data.tar.gz: 3031f999997380d3def3b379ef794639463a2b0c82913e08f8829374fe5707ea36076e999965719db4dadcd31f8c9890fd40ff4bf6db9a3b2d081d99a771efe5
6
+ metadata.gz: 4786a92acf6011cc49c6e9f8daa0505d7bfcd26da0d244e351830aed0972ac12656ab39df4f9cf7b35fa4892c7afa02830b7ec0b33876a8624076e60fb273f21
7
+ data.tar.gz: e87573373f971cea02ebba3dcca45124f49d8ca6354c34756e55fd101deab5649ee349e62b05a3abb7d0fdde9ead78075984a089f7f3070fb4b4768c7af8fcc8
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StateMachines
4
+ # Module to extend Fiber instances for pausable state tracking
5
+ module PausableFiber
6
+ attr_accessor :state_machine_fiber_pausable
7
+ end
8
+
4
9
  # A transition represents a state change for a specific attribute.
5
10
  #
6
11
  # Transitions consist of:
@@ -39,6 +44,7 @@ module StateMachines
39
44
  @paused_fiber = nil
40
45
  @resuming = false
41
46
  @continuation_block = nil
47
+ @fiber_thread_storage = nil
42
48
 
43
49
  @event = machine.events.fetch(event)
44
50
  @from_state = machine.states.fetch(from_name)
@@ -196,7 +202,8 @@ module StateMachines
196
202
  # this is an idempotent call on an already-paused transition. Just return true.
197
203
  return true if @paused_fiber&.alive? && !options[:after]
198
204
 
199
- # Extract pausable options
205
+ # Always use fibers for compatibility with existing pause/resume functionality
206
+ # The fiber argument can still be used to explicitly control fiber usage
200
207
  pausable_options = options.key?(:fiber) ? { fiber: options[:fiber] } : {}
201
208
 
202
209
  # Check if we're resuming from a pause
@@ -283,6 +290,7 @@ module StateMachines
283
290
  @paused_fiber = nil
284
291
  @resuming = false
285
292
  @continuation_block = nil
293
+ @fiber_thread_storage = nil
286
294
  end
287
295
 
288
296
  # Determines equality of transitions by testing whether the object, states,
@@ -375,41 +383,64 @@ module StateMachines
375
383
  # Handle different result types
376
384
  case result
377
385
  when Array
378
- # Exception occurred inside the fiber
379
386
  if result[0] == :error
380
- # Clean up state before re-raising
387
+ # Exception occurred inside the fiber
381
388
  @paused_fiber = nil
382
389
  raise result[1]
383
- end
384
- else
385
- # Normal flow
386
- # Check if fiber is still alive after resume
387
- if @paused_fiber.alive?
388
- # Still paused, keep the fiber
389
- true
390
390
  else
391
- # Fiber completed
392
- @paused_fiber = nil
393
- result == :halted
391
+ # Normal completion with thread storage export
392
+ @fiber_thread_storage = result[1] if result.length == 2 && result[1].is_a?(Hash)
393
+ result_value = result[0]
394
394
  end
395
+ else
396
+ # Direct result value (paused or simple completion)
397
+ result_value = result
398
+ end
399
+
400
+ # Check if fiber is still alive after resume
401
+ if @paused_fiber.alive?
402
+ # Still paused, keep the fiber
403
+ true
404
+ else
405
+ # Fiber completed
406
+ @paused_fiber = nil
407
+ result_value == :halted
395
408
  end
396
409
  else
410
+ # Capture current fiber's Thread.current storage to preserve object identity
411
+ # This is needed for compatibility but has limitations with dynamic assignments
412
+ parent_fiber_locals = Thread.current.keys.each_with_object({}) do |key, storage|
413
+ storage[key] = Thread.current[key]
414
+ end
415
+
397
416
  # Create a new fiber to run the block
398
417
  fiber = Fiber.new do
418
+ # Restore parent's Thread.current storage with exact same object references
419
+ parent_fiber_locals.each do |key, value|
420
+ Thread.current[key] = value
421
+ end
422
+
399
423
  # Mark that we're inside a pausable fiber
400
- Thread.current[:state_machine_fiber_pausable] = true
424
+ Fiber.current.extend(StateMachines::PausableFiber)
425
+ Fiber.current.state_machine_fiber_pausable = true
401
426
  begin
402
427
  halted = !catch(:halt) do
403
428
  yield
404
429
  true
405
430
  end
406
- halted ? :halted : :completed
431
+
432
+ # Export the final thread storage state along with the result
433
+ thread_storage = Thread.current.keys.each_with_object({}) do |key, storage|
434
+ storage[key] = Thread.current[key]
435
+ end
436
+
437
+ [halted ? :halted : :completed, thread_storage]
407
438
  rescue StandardError => e
408
439
  # Store the exception for re-raising
409
440
  [:error, e]
410
441
  ensure
411
442
  # Clean up the flag
412
- Thread.current[:state_machine_fiber_pausable] = false
443
+ Fiber.current.state_machine_fiber_pausable = false
413
444
  end
414
445
  end
415
446
 
@@ -419,23 +450,28 @@ module StateMachines
419
450
  # Handle different result types
420
451
  case result
421
452
  when Array
422
- # Exception occurred
423
453
  if result[0] == :error
424
- # Clean up state before re-raising
454
+ # Exception occurred
425
455
  @paused_fiber = nil
426
456
  raise result[1]
427
- end
428
- else
429
- # Normal flow
430
- # Save if paused
431
- if fiber.alive?
432
- @paused_fiber = fiber
433
- # Return true to indicate paused (treated as halted for flow control)
434
- true
435
457
  else
436
- # Fiber completed, return whether it was halted
437
- result == :halted
458
+ # Normal completion - check if we have thread storage
459
+ @fiber_thread_storage = result[1] if result.length == 2 && result[1].is_a?(Hash)
460
+ result_value = result[0]
438
461
  end
462
+ else
463
+ # Direct result value (shouldn't happen with our new code)
464
+ result_value = result
465
+ end
466
+
467
+ # Save if paused
468
+ if fiber.alive?
469
+ @paused_fiber = fiber
470
+ # Return true to indicate paused (treated as halted for flow control)
471
+ true
472
+ else
473
+ # Fiber completed, return whether it was halted
474
+ result_value == :halted
439
475
  end
440
476
  end
441
477
  end
@@ -448,8 +484,9 @@ module StateMachines
448
484
  return if @resuming
449
485
 
450
486
  # Only yield if we're actually inside a fiber created by pausable
451
- # We use a thread-local variable to track this
452
- return unless Thread.current[:state_machine_fiber_pausable]
487
+ # We use a module extension to track this
488
+ current_fiber = Fiber.current
489
+ return unless current_fiber.respond_to?(:state_machine_fiber_pausable) && current_fiber.state_machine_fiber_pausable
453
490
 
454
491
  Fiber.yield
455
492
 
@@ -532,6 +569,10 @@ module StateMachines
532
569
  def after
533
570
  return if @after_run
534
571
 
572
+ # Restore the fiber's thread storage to ensure consistency
573
+ # This preserves Thread.current state from before/around callbacks to after callbacks
574
+ @fiber_thread_storage.each { |key, value| Thread.current[key] = value } if @fiber_thread_storage
575
+
535
576
  catch(:halt) do
536
577
  type = @success ? :after : :failure
537
578
  machine.callbacks[type].each { |callback| callback.call(object, context, self) }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StateMachines
4
- VERSION = '0.100.1'
4
+ VERSION = '0.100.4'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_machines
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.100.1
4
+ version: 0.100.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -135,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
135
  - !ruby/object:Gem::Version
136
136
  version: '0'
137
137
  requirements: []
138
- rubygems_version: 3.6.7
138
+ rubygems_version: 3.6.9
139
139
  specification_version: 4
140
140
  summary: State machines for attributes
141
141
  test_files: []