Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
64 changes: 59 additions & 5 deletions spec/cache_hash_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
28 changes: 28 additions & 0 deletions src/cache_hash.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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