Module: ExpirationDate::ClassMethods

Defined in:
lib/expiration-date.rb

Overview

Container for the class methods to add to including modules.

Instance Method Summary collapse

Instance Method Details

#_class_expiration_labelsObject



175
176
177
178
179
# File 'lib/expiration-date.rb', line 175

def _class_expiration_labels
  @_class_expiration_labels ||= Hash.new do |h,k|
    h[k] = ExpirationLabel.new(Mutex.new)
  end
end

#_class_managers_specialsObject



171
172
173
# File 'lib/expiration-date.rb', line 171

def _class_managers_specials
  @_class_managers_specials ||= Hash.new
end

#_managers_specialsObject

:stopdoc: Class-level hash used to hold the initialization blocks for the expiring attributes.



167
168
169
# File 'lib/expiration-date.rb', line 167

def _managers_specials
  @_managers_specials ||= Hash.new
end

#expiring_attr(name, age, &block) ⇒ Object

Declares a new instance attribute that will expire after age seconds and be replaced by the results of running the block. The block is lazily evaluated when the attribute is accessed after the expiration time. The block is evaluated in the context of the instance (as opposed to being evaluated in the context of the class where the block is declared).

Obviously this scheme will only work if the attribute is only accessed using the setter and getter methods defined by this function.

class A
  include ExpirationDate
  expiring_attr( :foo, 60 ) { 'foo' }
end

a = A.new
a.foo              #=> 'foo'
a.foo = 'bar'
a.foo              #=> 'bar'
sleep 61
a.foo              #=> 'foo'

Raises:

  • (ArgumentError)


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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/expiration-date.rb', line 50

def expiring_attr( name, age, &block )
  raise ArgumentError, "a block must be given" if block.nil?

  name = name.to_sym
  age = Float(age)
  _managers_specials[name] = block

  self.class_eval <<-CODE, __FILE__, __LINE__
    def #{name}
      now = Time.now
      label = _expiration_labels[#{name.inspect}]
      if label.expires_on.nil? || now >= label.expires_on
        label.mutex.synchronize {
          break unless label.expires_on.nil? || now >= label.expires_on
          block = ::#{self.name}._managers_specials[#{name.inspect}]
          @#{name} = instance_eval(&block)
          label.age ||= #{age}
          label.expires_on = now + label.age
        }
      end
      @#{name}
    end

    def #{name}=( val )
      now = Time.now
      label = _expiration_labels[#{name.inspect}]
      label.mutex.synchronize {
        @#{name} = val
        label.age ||= #{age}
        label.expires_on = now + label.age
      }
      @#{name}
    end

    def expire_#{name}_now
      label = _expiration_labels[#{name.inspect}]
      label.mutex.synchronize {label.expires_on = Time.now - 1}
      @#{name}
    end

    def alter_#{name}_age( age )
      label = _expiration_labels[#{name.inspect}]
      label.mutex.synchronize {label.age = Float(age)}
    end
  CODE
end

#expiring_class_attr(name, age, &block) ⇒ Object

Declares a new class attribute that will expire after age seconds and be replaced by the results of running the block. The block is lazily evaluated when the attribute is accessed after the expiration time. The block is evaluated in the context of the class.

Obviously this scheme will only work if the attribute is only accessed using the setter and getter methods defined by this function.

class A
  include ExpirationDate
  expiring_class_attr( :foo, 60 ) { 'foo' }
end

A.foo              #=> 'foo'
A.foo = 'bar'
A.foo              #=> 'bar'
sleep 61
A.foo              #=> 'foo'

Raises:

  • (ArgumentError)


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/expiration-date.rb', line 116

def expiring_class_attr( name, age, &block )
  raise ArgumentError, "a block must be given" if block.nil?

  name = name.to_sym
  age = Float(age)
  _class_managers_specials[name] = block

  self.class_eval <<-CODE, __FILE__, __LINE__
    def self.#{name}
      now = Time.now
      label = _class_expiration_labels[#{name.inspect}]
      if label.expires_on.nil? || now >= label.expires_on
        label.mutex.synchronize {
          break unless label.expires_on.nil? || now >= label.expires_on
          block = ::#{self.name}._class_managers_specials[#{name.inspect}]
          @#{name} = instance_eval(&block)
          label.age ||= #{age}
          label.expires_on = now + label.age
        }
      end
      @#{name}
    end

    def self.#{name}=( val )
      now = Time.now
      label = _class_expiration_labels[#{name.inspect}]
      label.mutex.synchronize {
        @#{name} = val
        label.age ||= #{age}
        label.expires_on = now + label.age
      }
      @#{name}
    end

    def self.expire_#{name}_now
      label = _class_expiration_labels[#{name.inspect}]
      label.mutex.synchronize {label.expires_on = Time.now - 1}
      @#{name}
    end

    def self.alter_#{name}_age( age )
      label = _class_expiration_labels[#{name.inspect}]
      label.mutex.synchronize {label.age = Float(age)}
    end
  CODE
end