diff --git a/README.md b/README.md index ae41c92..37daa7f 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,22 @@ Removes all key/value pairs from the hash. Removes all stale key/value pairs from the hash. +### `.set_purge_interval(interval : Time::Span, stale_only : Bool = true)` + +Sets an interval where key/value pairs will automatically be purged. + +**Example:** + +```ruby +cache_hash = CacheHash(String).new(1.minute) +cache_hash.set_purge_interval(10.minutes) # stale_only defaults to true +``` + +```ruby +cache_hash = CacheHash(String).new(1.minute) +cache_hash.set_purge_interval(10.minutes, stale_only: false) # deletes all values, not just stale ones +``` + ### `.keys() : Array(String)` Runs `purge_stale` and returns an array of all the the non-stale keys. diff --git a/spec/cache_hash_spec.cr b/spec/cache_hash_spec.cr index 8b2c475..c8ed086 100644 --- a/spec/cache_hash_spec.cr +++ b/spec/cache_hash_spec.cr @@ -165,14 +165,17 @@ describe CacheHash do describe "#time" do it "returns a time if the kv pair is not stale" do - hash = CacheHash(String).new(Time::Span.new(0, 0, 3)) - - t = Time.now + time_before = Time.now + sleep 1 + hash = CacheHash(String).new(3.seconds) hash.set "city_1", "Seattle" - hash.time("city_1").class.should eq(Time) + sleep 1 + time_after = Time.now city_1_time = hash.time("city_1").not_nil! - (city_1_time > t && city_1_time < Time.now).should be_true + city_1_time.class.should eq(Time) + (city_1_time > time_before).should be_true + (city_1_time < time_after).should be_true end it "delete the kv pair if it is stale" do @@ -278,4 +281,55 @@ describe CacheHash do hash.raw.empty?.should be_true end end + + describe "#set_purge_interval" do + it "purges stale values from hash" do + hash = CacheHash(String).new(4.seconds) + hash.set_purge_interval(3.seconds) + hash.set "city_1", "Seattle" + hash.set "city_2", "Hong Kong" + hash.set "city_3", "Sacramento" + + hash.get("city_1").should eq("Seattle") + hash.get("city_2").should eq("Hong Kong") + hash.get("city_3").should eq("Sacramento") + hash.raw.empty?.should be_false + sleep 4 + hash.get("city_1").should be_nil + hash.get("city_2").should be_nil + hash.get("city_3").should be_nil + hash.raw.empty?.should be_true + + hash.set "city_1", "Seattle" + sleep 2 + hash.set "city_2", "Hong Kong" + hash.get("city_1").should eq("Seattle") + hash.get("city_2").should eq("Hong Kong") + sleep 2 + hash.get("city_1").should be_nil + hash.get("city_2").should eq("Hong Kong") + sleep 2 + hash.get("city_1").should be_nil + hash.get("city_2").should be_nil + hash.raw.empty?.should be_true + end + + it "purges all values from hash if specified" do + hash = CacheHash(String).new(5.seconds) + hash.set_purge_interval(3.seconds, stale_only: false) + hash.set "city_1", "Seattle" + hash.set "city_2", "Hong Kong" + hash.set "city_3", "Sacramento" + + hash.get("city_1").should eq("Seattle") + hash.get("city_2").should eq("Hong Kong") + hash.get("city_3").should eq("Sacramento") + hash.raw.empty?.should be_false + sleep 4 + hash.get("city_1").should be_nil + hash.get("city_2").should be_nil + hash.get("city_3").should be_nil + hash.raw.empty?.should be_true + end + end end diff --git a/src/cache_hash.cr b/src/cache_hash.cr index f8e32fd..dc8f024 100644 --- a/src/cache_hash.cr +++ b/src/cache_hash.cr @@ -2,6 +2,7 @@ class CacheHash(V) def initialize(@cache_time_span : Time::Span) @kv_hash = {} of String => V @time_hash = {} of String => Time + @is_purge_interval_running = false end def get(key : String) @@ -83,4 +84,31 @@ class CacheHash(V) end end end + + def set_purge_interval(interval : Time::Span, stale_only = true) + @purge_interval = interval + @purge_interval_stale_only = stale_only + unless @is_purge_interval_running + spawn do + run_purge_interval_loop + end + end + end + + private def run_purge_interval_loop + return if @is_purge_interval_running + + @is_purge_interval_running = true + loop do + stale_only = @purge_interval_stale_only + if (interval = @purge_interval).is_a?(Time::Span) + sleep interval.as(Time::Span) + if stale_only + purge_stale + else + purge + end + end + end + end end