module ThreadAttrAccessor

`extend` this module on your class/module to get per-thread class attribute accessors. Example:

class MyClass

extend ThreadAttrAccessor

thread_attr_accessor :setting

end

MyClass.setting = :original

threads = [

Thread.new { MyClass.setting = :foo; puts MyClass.setting },
Thread.new { MyClass.setting = :bar; puts MyClass.setting },

]

threads.each(&:join) MyClass.setting == :original # true

Constants

VERSION

Public Class Methods

extended(base) click to toggle source
# File lib/thread_attr_accessor.rb, line 102
def self.extended(base)
  mod = Module.new

  unless base.const_defined?(:ThreadAttributeAccessors, false)
    base.const_set(:ThreadAttributeAccessors, mod)
    base.extend(mod)
  end
end
search_in_ancestor_threads(key) click to toggle source
# File lib/thread_attr_accessor.rb, line 75
def self.search_in_ancestor_threads(key)
  fiber  = Fiber.current
  thread = Thread.current

  until fiber.nil?
    storage = FiberStorage.new(fiber, thread)

    if storage.has_key?(key)
      return storage[key]
    else
      fiber = fiber.parent_fiber
    end
  end

  until thread.nil?
    storage = ThreadStorage.new(thread)

    if storage.has_key?(key)
      return storage[key]
    else
      thread = thread.parent_thread
    end
  end

  nil
end
thread_accessor_key(base, name) click to toggle source
# File lib/thread_attr_accessor.rb, line 27
def self.thread_accessor_key(base, name)
  "#{base.name}.#{name}"
end

Public Instance Methods

thread_attr_accessor(*names, private: false, **opts) click to toggle source
# File lib/thread_attr_accessor.rb, line 166
def thread_attr_accessor(*names, private: false, **opts)
  private_reader = private.to_s == "reader" || private == true
  private_writer = private.to_s == "writer" || private == true

  thread_attr_reader(*names, private: private_reader, **opts)
  thread_attr_writer(*names, private: private_writer, **opts)
end
thread_attr_reader(*names, default: nil, inherit: false, private: false, **opts) click to toggle source
# File lib/thread_attr_accessor.rb, line 128
def thread_attr_reader(*names, default: nil, inherit: false, private: false, **opts)
  if default && inherit
    get_default = ->(thread_key) {
      ThreadAttrAccessor.search_in_ancestor_threads(thread_key) ||
      default.call
    }
  elsif inherit
    get_default = ThreadAttrAccessor.method(:search_in_ancestor_threads)
  elsif default
    get_default = ->(*) { default.call }
  end

  if get_default
    get_value = ->(thread_key) {
      storage = FiberStorage.new
      storage[thread_key] ||= get_default.call(thread_key)
    }
  else
    get_value = ->(thread_key) {
      FiberStorage.new[thread_key]
    }
  end

  mod = const_get(:ThreadAttributeAccessors)

  names.each do |name|
    thread_key = ThreadAttrAccessor.thread_accessor_key(self, name)

    mod.send(:define_method, name) do
      get_value.call(thread_key)
    end

    if private
      mod.send :private, name
    end
  end
end
thread_attr_writer(*names, private: false, **opts) click to toggle source
# File lib/thread_attr_accessor.rb, line 111
def thread_attr_writer(*names, private: false, **opts)
  mod = const_get(:ThreadAttributeAccessors)

  names.each do |name|
    thread_key = ThreadAttrAccessor.thread_accessor_key(self, name)

    mod.send(:define_method, "#{name}=") do |value|
      FiberStorage.new[thread_key] = value
      value
    end

    if private
      mod.send :private, "#{name}="
    end
  end
end