summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2009-05-17 15:52:52 -0500
committerJames Turnbull <james@lovedthanlost.net>2009-05-20 18:29:03 +1000
commit047ab782aed5555a6812131acdba3925b1274a55 (patch)
tree6bc69963a00d694fad12f72ec6a16f8cee7ec1f3
parent6a413d21a71da2b330f9b147532f3256fc29be3c (diff)
downloadpuppet-047ab782aed5555a6812131acdba3925b1274a55.tar.gz
puppet-047ab782aed5555a6812131acdba3925b1274a55.tar.xz
puppet-047ab782aed5555a6812131acdba3925b1274a55.zip
Adding TTL support to attribute caching
Previously you had to have an Expirer, but now you can declare a TTL for a cached attribute and it will be expired automatically when the cached value is older than the ttl. Signed-off-by: Luke Kanies <luke@madstop.com>
-rw-r--r--lib/puppet/util/cacher.rb38
-rwxr-xr-xspec/unit/util/cacher.rb54
2 files changed, 87 insertions, 5 deletions
diff --git a/lib/puppet/util/cacher.rb b/lib/puppet/util/cacher.rb
index a9fb890c6..ef619c9a6 100644
--- a/lib/puppet/util/cacher.rb
+++ b/lib/puppet/util/cacher.rb
@@ -36,11 +36,9 @@ module Puppet::Util::Cacher
# Methods that can get added to a class.
module ClassMethods
- private
-
# 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, &block)
+ def cached_attr(name, options = {}, &block)
init_method = "init_" + name.to_s
define_method(init_method, &block)
@@ -53,6 +51,20 @@ module Puppet::Util::Cacher
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
@@ -84,9 +96,11 @@ module Puppet::Util::Cacher
def cached_value(name)
# Allow a nil expirer, in which case we regenerate the value every time.
- if expirer.nil? or expirer.expired?(cache_timestamp)
+ if expired_by_expirer?(name)
value_cache.clear
@cache_timestamp = Time.now
+ elsif expired_by_ttl?(name)
+ value_cache.delete(name)
end
unless value_cache.include?(name)
value_cache[name] = send("init_%s" % name)
@@ -94,6 +108,22 @@ module Puppet::Util::Cacher
value_cache[name]
end
+ def expired_by_expirer?(name)
+ if expirer.nil?
+ return true unless self.class.attr_ttl(name)
+ end
+ return expirer.expired?(cache_timestamp)
+ end
+
+ def expired_by_ttl?(name)
+ return false unless ttl = self.class.attr_ttl(name)
+
+ @ttl_timestamps ||= {}
+ @ttl_timestamps[name] ||= Time.now
+
+ return (Time.now - @ttl_timestamps[name]) > ttl
+ end
+
def value_cache
unless defined?(@value_cache) and @value_cache
@value_cache = {}
diff --git a/spec/unit/util/cacher.rb b/spec/unit/util/cacher.rb
index 5a867c6c1..696ef6b19 100755
--- a/spec/unit/util/cacher.rb
+++ b/spec/unit/util/cacher.rb
@@ -44,7 +44,7 @@ describe Puppet::Util::Cacher do
end
it "should support defining cached attributes" do
- CacheTest.private_methods.should be_include("cached_attr")
+ CacheTest.methods.should be_include("cached_attr")
end
it "should default to the Cacher module as its expirer" do
@@ -120,5 +120,57 @@ describe Puppet::Util::Cacher do
@expirer.expire
@object.instance_cache.should_not == "foo"
end
+
+ it "should allow specification of a ttl for cached attributes" do
+ klass = Class.new do
+ include Puppet::Util::Cacher
+ end
+
+ klass.cached_attr(:myattr, :ttl => 5) { Time.now }
+
+ klass.attr_ttl(:myattr).should == 5
+ end
+
+ it "should allow specification of a ttl as a string" do
+ klass = Class.new do
+ include Puppet::Util::Cacher
+ end
+
+ klass.cached_attr(:myattr, :ttl => "5") { Time.now }
+
+ klass.attr_ttl(:myattr).should == 5
+ end
+
+ it "should fail helpfully if the ttl cannot be converted to an integer" do
+ klass = Class.new do
+ include Puppet::Util::Cacher
+ end
+
+ lambda { klass.cached_attr(:myattr, :ttl => "yep") { Time.now } }.should raise_error(ArgumentError)
+ end
+
+ it "should automatically expire cached attributes whose ttl has expired, even if no expirer is present" do
+ klass = Class.new do
+ def self.to_s
+ "CacheTestClass"
+ end
+ include Puppet::Util::Cacher
+ attr_accessor :value
+ end
+
+ klass.cached_attr(:myattr, :ttl => 5) { self.value += 1; self.value }
+
+ now = Time.now
+ later = Time.now + 15
+
+ instance = klass.new
+ instance.value = 0
+ instance.myattr.should == 1
+
+ Time.expects(:now).returns later
+
+ # This call should get the new Time value, which should expire the old value
+ instance.myattr.should == 2
+ end
end
end