Class: Module

Inherits:
Object
  • Object
show all
Defined in:
lib/sync_attr/class_attributes.rb

Instance Method Summary collapse

Instance Method Details

#sync_mattr_accessor(*attributes, &block) ⇒ Object Also known as: sync_cattr_accessor

Generate a class reader and writer for the attribute



79
80
81
82
# File 'lib/sync_attr/class_attributes.rb', line 79

def sync_mattr_accessor(*attributes, &block)
  sync_cattr_writer(*attributes)
  sync_cattr_reader(*attributes, &block)
end

#sync_mattr_reader(*attributes, &block) ⇒ Object Also known as: sync_cattr_reader

Lazy load the specific class attribute by calling the supplied block when the attribute is first read and then return the same value for all subsequent calls to the class variable

An optional block can be supplied to initialize the synchronized class attribute when first read. Acts as a thread safe lazy initializer. The block will only be called once even if several threads call the reader at the same time

Example:

require 'sync_attr'
class MyClass
  # Generates a reader for the class attribute 'hello'
  # and Lazy initializes the value to 'hello world' only on the first
  # call to the reader
  sync_cattr_reader :hello do
    'hello world'
  end
end


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/sync_attr/class_attributes.rb', line 20

def sync_mattr_reader(*attributes, &block)
  attributes.each do |attribute|
    raise NameError.new("invalid attribute name: #{attribute}") unless attribute =~ /^[_A-Za-z]\w*$/
    mutex_var_name = "@@sync_attr_#{attribute}".to_sym
    class_variable_set(mutex_var_name, Mutex.new) unless class_variable_defined?(mutex_var_name)
    # Class reader with lazy initialization for the first thread that calls this method
    # Use metaclass/eigenclass to dynamically generate class methods
    (class << self; self; end).instance_eval do
      define_method(attribute.to_sym) do
        var_name = "@@#{attribute}".to_sym
        if class_variable_defined?(var_name)
          # If there is no writer then it is not necessary to protect reads
          if self.respond_to?("#{attribute}=".to_sym, true)
            class_variable_get(mutex_var_name).synchronize { class_variable_get(var_name) }
          else
            class_variable_get(var_name)
          end
        else
          return nil unless block
          class_variable_get(mutex_var_name).synchronize do
            # Now that we have exclusive access make sure that another thread has
            # not just initialized this attribute
            if class_variable_defined?(var_name)
              class_variable_get(var_name)
            else
              class_variable_set(var_name, class_eval(&block))
            end
          end
        end
      end
    end
  end
end

#sync_mattr_writer(*attributes) ⇒ Object Also known as: sync_cattr_writer

Generates a writer to set a synchronized attribute Supply a Proc ensure an attribute is not being updated by another thread:

MyClass.count = Proc.new {|count| (count||0) + 1}


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/sync_attr/class_attributes.rb', line 58

def sync_mattr_writer(*attributes)
  attributes.each do |attribute|
    mutex_var_name = "@@sync_attr_#{attribute}".to_sym
    class_variable_set(mutex_var_name, Mutex.new) unless class_variable_defined?(mutex_var_name)
    class_eval(<<-EOS, __FILE__, __LINE__ + 1)
      def self.#{attribute}=(value)
        #{mutex_var_name}.synchronize do
          if value.is_a?(Proc)
            current_value = @@#{attribute} if defined?(@@#{attribute})
            @@#{attribute} = value.call(current_value)
          else
            @@#{attribute} = value
          end
        end
      end
    EOS
  end
end