Method: Module#thread_mattr_reader

Defined in:
lib/active_support/core_ext/module/attribute_accessors_per_thread.rb

#thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) ⇒ Object Also known as: thread_cattr_reader

Defines a per-thread class attribute and creates class and instance reader methods. The underlying per-thread class variable is set to nil, if it is not previously defined.

module Current
  thread_mattr_reader :user
end

Current.user = "DHH"
Current.user # => "DHH"
Thread.new { Current.user }.value # => nil

The attribute name must be a valid method name in Ruby.

module Foo
  thread_mattr_reader :"1_Badname"
end
# => NameError: invalid attribute name: 1_Badname

To omit the instance reader method, pass instance_reader: false or instance_accessor: false.

class Current
  thread_mattr_reader :user, instance_reader: false
end

Current.new.user # => NoMethodError


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/active_support/core_ext/module/attribute_accessors_per_thread.rb', line 41

def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
  syms.each do |sym|
    raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)

    # The following generated method concatenates `object_id` because we want
    # subclasses to maintain independent values.
    if default.nil?
      class_eval(<<-EOS, __FILE__, __LINE__ + 1)
        def self.#{sym}
          @__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
          ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
        end
      EOS
    else
      default = default.dup.freeze unless default.frozen?
      singleton_class.define_method("#{sym}_default_value") { default }

      class_eval(<<-EOS, __FILE__, __LINE__ + 1)
        def self.#{sym}
          @__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
          value = ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]

          if value.nil? && !::ActiveSupport::IsolatedExecutionState.key?(@__thread_mattr_#{sym})
            ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = #{sym}_default_value
          else
            value
          end
        end
      EOS
    end

    if instance_reader && instance_accessor
      class_eval(<<-EOS, __FILE__, __LINE__ + 1)
        def #{sym}
          self.class.#{sym}
        end
      EOS
    end
  end
end