persistent-cache 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Persistent::Cache
|
2
2
|
|
3
|
-
Persistent cache behaves like a hash, with a pluggable back-end. Currently sqlite3
|
3
|
+
Persistent cache behaves like a hash, with a pluggable back-end. Currently sqlite3, file system directory and RAM back-ends are provided. The cache defaults to type STORAGE_SQLITE
|
4
4
|
|
5
5
|
Values in the cache have a default freshness period of 15465600 ms. This can be configured in the cache initializer. Setting fresh = nil indicates that data remains fresh for-ever. Each user of the cache may have his own independent freshness value. If stale data is requested from the cache, nil is returned. Data is marshalled before storage. If a key is not found in the cache, nil is returned. Setting the value of a key in the cache to nil deletes the entry. If required, creation time of an entry can be specified using set(key, value, timestamp)
|
6
6
|
|
@@ -20,6 +20,10 @@ When a StorageDirectory is used, it can be asked whether a key is present and wh
|
|
20
20
|
|
21
21
|
key_cached?(key)
|
22
22
|
|
23
|
+
## StorageRAM
|
24
|
+
|
25
|
+
Updates to the cache are stored in RAM using a hash.
|
26
|
+
|
23
27
|
## Installation
|
24
28
|
|
25
29
|
Add this line to your application's Gemfile:
|
@@ -74,10 +78,12 @@ Or install it yourself as:
|
|
74
78
|
|
75
79
|
cache = Persistent::Cache.new("/tmp/my-persistent-cache", nil) # for-ever fresh
|
76
80
|
|
77
|
-
cache = Persistent::Cache.new("/tmp/directory-cache", nil, STORAGE_DIRECTORY)
|
81
|
+
cache = Persistent::Cache.new("/tmp/directory-cache", nil, Persistent::Cache::STORAGE_DIRECTORY)
|
78
82
|
|
79
83
|
cache.set("mykey", "myvalue", Time.now) # explicitly set creation time
|
80
84
|
|
85
|
+
cache = Persistent::Cache.new("cache-name", nil, Persistent::Cache::STORAGE_RAM)
|
86
|
+
|
81
87
|
Please send feedback and comments to the authors at:
|
82
88
|
|
83
89
|
Wynand van Dyk <wynand.van.dyk@hetzner.co.za>
|
data/lib/persistent-cache.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
require "persistent-cache/version"
|
2
2
|
require "sqlite3"
|
3
3
|
require "persistent-cache/storage/storage_sq_lite"
|
4
|
+
require "persistent-cache/storage/storage_directory"
|
5
|
+
require "persistent-cache/storage/storage_ram"
|
4
6
|
|
5
7
|
module Persistent
|
6
8
|
class Cache
|
7
9
|
STORAGE_SQLITE = 'sqlite' unless defined? STORAGE_SQLITE
|
8
10
|
STORAGE_DIRECTORY = 'directory' unless defined? STORAGE_DIRECTORY
|
11
|
+
STORAGE_RAM = 'ram' unless defined? STORAGE_RAM
|
9
12
|
|
10
13
|
# Fresh is 1 day less than the bacula default job retention time. If this is configured differently, FRESH should be updated as well.
|
11
14
|
FRESH = 15465600; FRESH.freeze
|
@@ -19,6 +22,7 @@ module Persistent
|
|
19
22
|
|
20
23
|
@storage = StorageSQLite.new(storage_details) if storage == STORAGE_SQLITE
|
21
24
|
@storage = StorageDirectory.new(storage_details) if storage == STORAGE_DIRECTORY
|
25
|
+
@storage = StorageRAM.new if storage == STORAGE_RAM
|
22
26
|
@fresh = fresh
|
23
27
|
@storage_details = storage_details
|
24
28
|
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "eh/eh"
|
2
|
+
|
3
|
+
module Persistent
|
4
|
+
class StorageRAM
|
5
|
+
attr_accessor :storage
|
6
|
+
|
7
|
+
def initialize()
|
8
|
+
@storage = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def save_key_value_pair(serialized_key, serialized_value, timestamp = nil)
|
12
|
+
delete_entry(serialized_key)
|
13
|
+
time_entry = timestamp.nil? ? Time.now.to_s : timestamp.to_s
|
14
|
+
EH::retry!(:args => [serialized_key, serialized_value, time_entry]) do
|
15
|
+
@storage[serialized_key] = {:value => serialized_value, :timestamp => time_entry}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def lookup_key(serialized_key)
|
20
|
+
EH::retry!(:args => [serialized_key]) do
|
21
|
+
return [] if @storage[serialized_key].nil?
|
22
|
+
result = [[]]
|
23
|
+
result[0][0] = @storage[serialized_key][:value]
|
24
|
+
result[0][1] = @storage[serialized_key][:timestamp]
|
25
|
+
result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete_entry(serialized_key)
|
30
|
+
EH::retry!(:args => [serialized_key]) do
|
31
|
+
@storage.delete(serialized_key)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def size
|
36
|
+
EH::retry!(:args => []) do
|
37
|
+
@storage.size
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def keys
|
42
|
+
EH::retry!(:args => []) do
|
43
|
+
return [] if @storage.keys == []
|
44
|
+
[@storage.keys]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def clear
|
49
|
+
EH::retry!(:args => []) do
|
50
|
+
@storage.clear
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "persistent-cache/storage/storage_ram"
|
3
|
+
|
4
|
+
describe Persistent::StorageRAM do
|
5
|
+
before :each do
|
6
|
+
@test_key = "testkey"
|
7
|
+
@test_value = "testvalue"
|
8
|
+
@iut = Persistent::StorageRAM.new
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when constructed" do
|
12
|
+
it "should have a storage hash in RAM" do
|
13
|
+
@iut.storage.nil?.should == false
|
14
|
+
@iut.storage.instance_of?(Hash).should == true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when asked to store a key value pair" do
|
19
|
+
it "should store the key/value pair in RAM, with the current time as timestamp" do
|
20
|
+
start_time = Time.now - 1
|
21
|
+
@iut.save_key_value_pair(Marshal.dump(@test_key), Marshal.dump(@test_value))
|
22
|
+
result = @iut.lookup_key(Marshal.dump(@test_key))
|
23
|
+
result.nil?.should == false
|
24
|
+
result[0].nil?.should == false
|
25
|
+
result[0][0].should == Marshal.dump(@test_value)
|
26
|
+
test_time = Time.parse(result[0][1])
|
27
|
+
test_time.should > start_time and test_time.should < start_time + 600
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should store the key/value pair in RAM, with a timestamp specified" do
|
31
|
+
test_time = (Time.now - 2500)
|
32
|
+
@iut.save_key_value_pair(Marshal.dump(@test_key), Marshal.dump(@test_value), test_time)
|
33
|
+
result = @iut.lookup_key(Marshal.dump(@test_key))
|
34
|
+
result.nil?.should == false
|
35
|
+
result[0].nil?.should == false
|
36
|
+
result[0][0].should == Marshal.dump(@test_value)
|
37
|
+
time_retrieved = Time.parse(result[0][1])
|
38
|
+
time_retrieved.to_s.should == test_time.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should overwrite the existing key/value pair if they already exist" do
|
42
|
+
@iut.save_key_value_pair(Marshal.dump(@test_key), Marshal.dump(@test_value))
|
43
|
+
@iut.save_key_value_pair(Marshal.dump(@test_key), Marshal.dump("testvalue2"))
|
44
|
+
result = @iut.lookup_key(Marshal.dump(@test_key))
|
45
|
+
result.nil?.should == false
|
46
|
+
result[0].nil?.should == false
|
47
|
+
result.size.should == 1
|
48
|
+
result[0][0].should == Marshal.dump("testvalue2")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "When looking up a value given its key" do
|
53
|
+
it "should retrieve the value from RAM" do
|
54
|
+
@iut.save_key_value_pair(Marshal.dump(@test_key), Marshal.dump(@test_value))
|
55
|
+
result = @iut.lookup_key(Marshal.dump(@test_key))
|
56
|
+
result[0][0].should == Marshal.dump(@test_value)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should retrieve the timestamp when the value was stored from RAM" do
|
60
|
+
now = Time.now.to_s
|
61
|
+
@iut.save_key_value_pair(Marshal.dump(@test_key), Marshal.dump(@test_value))
|
62
|
+
sleep 1
|
63
|
+
result = @iut.lookup_key(Marshal.dump(@test_key))
|
64
|
+
result[0][1].should == now
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should return an empty array if a key is not in RAM" do
|
68
|
+
@iut.delete_entry(Marshal.dump(@test_key))
|
69
|
+
result = @iut.lookup_key(Marshal.dump(@test_key))
|
70
|
+
result.should == []
|
71
|
+
result[0].should == nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when asked to delete an entry" do
|
76
|
+
it "should not raise an error if the entry is not present" do
|
77
|
+
@iut.delete_entry(Marshal.dump("shouldnotbepresent"))
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should delete the entry if it is present" do
|
81
|
+
@iut.save_key_value_pair(Marshal.dump(@test_key), Marshal.dump(@test_value))
|
82
|
+
result = @iut.lookup_key(Marshal.dump(@test_key))
|
83
|
+
result[0][0].should == Marshal.dump(@test_value)
|
84
|
+
@iut.delete_entry(Marshal.dump(@test_key))
|
85
|
+
result = @iut.lookup_key(Marshal.dump(@test_key))
|
86
|
+
result.should == []
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when asked the size of the RAM database" do
|
91
|
+
it "should return 0 if the RAM database has no entries" do
|
92
|
+
@iut.size.should == 0
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should return the number of entries" do
|
96
|
+
populate_database(@iut)
|
97
|
+
@iut.size.should == 3
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when asked for the keys in the RAM database" do
|
102
|
+
it "should return an empty array if there are no entries in the RAM database" do
|
103
|
+
@iut.keys.should == []
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should return the keys in the RAM database" do
|
107
|
+
populate_database(@iut)
|
108
|
+
keys = @iut.keys.flatten
|
109
|
+
keys.include?(Marshal.dump("one")).should == true
|
110
|
+
keys.include?(Marshal.dump("two")).should == true
|
111
|
+
keys.include?(Marshal.dump("three")).should == true
|
112
|
+
@iut.size.should == 3
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should return the keys in an array, with each key in its own sub-array" do
|
116
|
+
populate_database(@iut)
|
117
|
+
found = false
|
118
|
+
test = Marshal.dump("one")
|
119
|
+
found = true if (@iut.keys[0][0] == test or @iut.keys[0][1] == test or @iut.keys[0][2] == test)
|
120
|
+
found.should == true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "when asked to clear the RAM database" do
|
125
|
+
it "should delete all entries in RAM" do
|
126
|
+
populate_database(@iut)
|
127
|
+
@iut.clear
|
128
|
+
@iut.size.should == 0
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def populate_database(iut)
|
133
|
+
iut.save_key_value_pair(Marshal.dump("one"), Marshal.dump("one"))
|
134
|
+
iut.save_key_value_pair(Marshal.dump("two"), Marshal.dump("two"))
|
135
|
+
iut.save_key_value_pair(Marshal.dump("three"), Marshal.dump("three"))
|
136
|
+
end
|
137
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: persistent-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-08-16 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -109,6 +109,7 @@ files:
|
|
109
109
|
- Rakefile
|
110
110
|
- lib/persistent-cache.rb
|
111
111
|
- lib/persistent-cache/storage/storage_directory.rb
|
112
|
+
- lib/persistent-cache/storage/storage_ram.rb
|
112
113
|
- lib/persistent-cache/storage/storage_sq_lite.rb
|
113
114
|
- lib/persistent-cache/version.rb
|
114
115
|
- multidb
|
@@ -116,6 +117,7 @@ files:
|
|
116
117
|
- spec/persistent-cache_spec.rb
|
117
118
|
- spec/spec_helper.rb
|
118
119
|
- spec/storage/storage_directory_spec.rb
|
120
|
+
- spec/storage/storage_ram_spec.rb
|
119
121
|
- spec/storage/storage_sqlite_spec.rb
|
120
122
|
homepage: ''
|
121
123
|
licenses: []
|
@@ -137,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
139
|
version: '0'
|
138
140
|
requirements: []
|
139
141
|
rubyforge_project:
|
140
|
-
rubygems_version: 1.8.
|
142
|
+
rubygems_version: 1.8.25
|
141
143
|
signing_key:
|
142
144
|
specification_version: 3
|
143
145
|
summary: Persistent Cache has a default freshness threshold of 179 days after which
|
@@ -146,4 +148,5 @@ test_files:
|
|
146
148
|
- spec/persistent-cache_spec.rb
|
147
149
|
- spec/spec_helper.rb
|
148
150
|
- spec/storage/storage_directory_spec.rb
|
151
|
+
- spec/storage/storage_ram_spec.rb
|
149
152
|
- spec/storage/storage_sqlite_spec.rb
|