Module: Shifty::Policy

Defined in:
lib/shifty/policy.rb

Overview

Handoff policies govern how a value crosses a worker boundary. Each policy responds to #call(value, worker:) and returns the value the worker's task will receive.

Defined Under Namespace

Classes: Supply

Constant Summary collapse

Frozen =

Deeply freezes the value in place (zero copies) so any mutation, anywhere in the pipeline, raises at the worker that attempted it. IO-like values are rejected proactively: Ractor.make_shareable would otherwise freeze a live handle in place — a process-wide side effect on shared resources like loggers or $stdout.

lambda do |value, worker:|
  if value.is_a?(IO)
    raise UnshareableValue.new(worker: worker, policy: :frozen, value: value)
  end
  begin
    Ractor.make_shareable(value)
  rescue Ractor::Error => e
    raise UnshareableValue.new(worker: worker, policy: :frozen, value: value, cause: e)
  end
end
Isolated =

Hands the task a private, mutable deep copy. Marshal is the mechanism because Ractor.make_shareable(copy: true) returns a frozen copy, which cannot satisfy the :isolated contract of a mutable scratch value.

lambda do |value, worker:|
  Marshal.load(Marshal.dump(value))
rescue TypeError => e
  raise UnshareableValue.new(worker: worker, policy: :isolated, value: value, cause: e)
end
Shared =

The escape hatch: the raw reference passes through untouched.

->(value, worker:) { value }
TABLE =
{
  frozen: Frozen,
  isolated: Isolated,
  shared: Shared
}.freeze
ALIASES =
{hardened: :isolated}.freeze

Class Method Summary collapse

Class Method Details

.canonical(name) ⇒ Object



88
89
90
91
92
93
94
95
96
97
# File 'lib/shifty/policy.rb', line 88

def canonical(name)
  if ALIASES.key?(name)
    replacement = ALIASES[name]
    warn "[shifty] policy :#{name} is deprecated and will be " \
         "removed in 1.0.0; use :#{replacement} instead."
    replacement
  else
    name
  end
end

.resolve(name) ⇒ Object



82
83
84
85
86
# File 'lib/shifty/policy.rb', line 82

def resolve(name)
  TABLE.fetch(canonical(name)) do
    raise ArgumentError, "unknown policy #{name.inspect}"
  end
end

.validate!(name) ⇒ Object

Canonicalizes and validates a policy name at declaration time, so a typo fails where it was written rather than at first shift.



101
102
103
104
105
106
107
# File 'lib/shifty/policy.rb', line 101

def validate!(name)
  canonical(name).tap do |canonical_name|
    unless TABLE.key?(canonical_name)
      raise ArgumentError, "unknown policy #{name.inspect}"
    end
  end
end