1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
module Puppet::Util::Cacher
module Expirer
attr_reader :timestamp
# Cause all cached values to be considered expired.
def expire
@timestamp = Time.now
end
# Is the provided timestamp earlier than our expiration timestamp?
# If it is, then the associated value is expired.
def dependent_data_expired?(ts)
return false unless timestamp
timestamp > ts
end
end
extend Expirer
# Our module has been extended in a class; we can only add the Instance methods,
# which become *class* methods in the class.
def self.extended(other)
class << other
extend ClassMethods
include InstanceMethods
end
end
# Our module has been included in a class, which means the class gets the class methods
# and all of its instances get the instance methods.
def self.included(other)
other.extend(ClassMethods)
other.send(:include, InstanceMethods)
end
# Methods that can get added to a class.
module ClassMethods
# Provide a means of defining an attribute whose value will be cached.
# Must provide a block capable of defining the value if it's flushed..
def cached_attr(name, options = {}, &block)
init_method = "init_#{name}"
define_method(init_method, &block)
define_method(name) do
cached_value(name)
end
define_method(name.to_s + "=") do |value|
# Make sure the cache timestamp is set
cache_timestamp
value_cache[name] = value
end
if ttl = options[:ttl]
set_attr_ttl(name, ttl)
end
end
def attr_ttl(name)
return nil unless @attr_ttls
@attr_ttls[name]
end
def set_attr_ttl(name, value)
@attr_ttls ||= {}
@attr_ttls[name] = Integer(value)
end
end
# Methods that get added to instances.
module InstanceMethods
def expire
# Only expire if we have an expirer. This is
# mostly so that we can comfortably handle cases
# like Puppet::Type instances, which use their
# catalog as their expirer, and they often don't
# have a catalog.
if e = expirer
e.expire
end
end
def expirer
Puppet::Util::Cacher
end
private
def cache_timestamp
@cache_timestamp ||= Time.now
end
def cached_value(name)
# Allow a nil expirer, in which case we regenerate the value every time.
if expired_by_expirer?(name)
value_cache.clear
@cache_timestamp = Time.now
elsif expired_by_ttl?(name)
value_cache.delete(name)
end
value_cache[name] = send("init_#{name}") unless value_cache.include?(name)
value_cache[name]
end
def expired_by_expirer?(name)
if expirer.nil?
return true unless self.class.attr_ttl(name)
end
expirer.dependent_data_expired?(cache_timestamp)
end
def expired_by_ttl?(name)
return false unless self.class.respond_to?(:attr_ttl)
return false unless ttl = self.class.attr_ttl(name)
@ttl_timestamps ||= {}
@ttl_timestamps[name] ||= Time.now
(Time.now - @ttl_timestamps[name]) > ttl
end
def value_cache
@value_cache ||= {}
end
end
end
|