diff options
author | Luke Kanies <luke@madstop.com> | 2009-05-17 15:52:52 -0500 |
---|---|---|
committer | James Turnbull <james@lovedthanlost.net> | 2009-05-20 18:29:03 +1000 |
commit | 047ab782aed5555a6812131acdba3925b1274a55 (patch) | |
tree | 6bc69963a00d694fad12f72ec6a16f8cee7ec1f3 | |
parent | 6a413d21a71da2b330f9b147532f3256fc29be3c (diff) | |
download | puppet-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.rb | 38 | ||||
-rwxr-xr-x | spec/unit/util/cacher.rb | 54 |
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 |