class Dry::Effects::Frame

Stack frame

@api private

Public Class Methods

spawn_fiber(stack) { || ... } click to toggle source

Spawn a new fiber with a stack of effect handlers

@param [Stack] stack @api private

# File lib/dry/effects/frame.rb, line 36
def spawn_fiber(stack)
  fiber = ::Fiber.new do
    self.stack = stack
    yield
  end
  result = fiber.resume

  loop do
    error = false
    break result unless fiber.alive?

    provided = stack.(result) do
      ::Dry::Effects.yield(result) do |_, e|
        error = true
        e
      end
    end

    result =
      if error
        raise_in_fiber(fiber, provided)
      else
        fiber.resume(provided)
      end
  end
end
stack() click to toggle source

Accessing current stack of effect handlers. It is inherently thread/fiber-local.

@return [Stack] @api private

# File lib/dry/effects/frame.rb, line 22
def stack
  ::Thread.current[:dry_effects_stack] ||= Stack.new
end
stack=(stack) click to toggle source

@param [Stack] stack @api private

# File lib/dry/effects/frame.rb, line 28
def stack=(stack)
  ::Thread.current[:dry_effects_stack] = stack
end

Private Class Methods

raise_in_fiber(fiber, error) click to toggle source

@api private

# File lib/dry/effects/frame.rb, line 67
def raise_in_fiber(fiber, error)
  fiber.raise(error)
end

Public Instance Methods

call(*args, &block) click to toggle source

Add new handler to the current stack and run the given block

@param [Array<Object>] args Handler arguments @param [Proc] block Program to run @api private

# File lib/dry/effects/frame.rb, line 90
def call(*args, &block)
  stack = Frame.stack
  prov = provider.dup
  was_empty = stack.empty?

  prov.(*args) do
    if was_empty
      stack.push(prov) do
        Frame.spawn_fiber(stack, &block)
      end
    else
      stack.push(prov, &block)
    end
  end
end