lawnchair 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lawnchair.gemspec +17 -4
- data/lib/lawnchair.rb +21 -59
- data/lib/storage_engine/abstract.rb +37 -0
- data/lib/storage_engine/composite.rb +45 -0
- data/lib/storage_engine/in_process.rb +29 -0
- data/lib/storage_engine/redis.rb +28 -0
- data/lib/view/helper.rb +15 -0
- data/spec/lawnchair_spec.rb +9 -74
- data/spec/spec_helper.rb +8 -1
- data/spec/storage_engine/abstract_spec.rb +103 -0
- data/spec/storage_engine/composite_spec.rb +108 -0
- data/spec/storage_engine/in_process_spec.rb +61 -0
- data/spec/storage_engine/redis_spec.rb +72 -0
- metadata +15 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lawnchair.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{lawnchair}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Shane Wolf"]
|
12
|
-
s.date = %q{2010-02-
|
12
|
+
s.date = %q{2010-02-12}
|
13
13
|
s.description = %q{Very simple caching mechanism for arbitrary pieces of resoucre ruby code using Redis as the distributed (or local) cache}
|
14
14
|
s.email = %q{shanewolf@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -24,11 +24,20 @@ Gem::Specification.new do |s|
|
|
24
24
|
"VERSION",
|
25
25
|
"lawnchair.gemspec",
|
26
26
|
"lib/lawnchair.rb",
|
27
|
+
"lib/storage_engine/abstract.rb",
|
28
|
+
"lib/storage_engine/composite.rb",
|
29
|
+
"lib/storage_engine/in_process.rb",
|
30
|
+
"lib/storage_engine/redis.rb",
|
31
|
+
"lib/view/helper.rb",
|
27
32
|
"spec/lawnchair_spec.rb",
|
28
33
|
"spec/spec.opts",
|
29
34
|
"spec/spec_helper.rb",
|
30
35
|
"spec/speed.rb",
|
31
|
-
"spec/speed_results.png"
|
36
|
+
"spec/speed_results.png",
|
37
|
+
"spec/storage_engine/abstract_spec.rb",
|
38
|
+
"spec/storage_engine/composite_spec.rb",
|
39
|
+
"spec/storage_engine/in_process_spec.rb",
|
40
|
+
"spec/storage_engine/redis_spec.rb"
|
32
41
|
]
|
33
42
|
s.homepage = %q{http://github.com/gizm0duck/lawnchair}
|
34
43
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -38,7 +47,11 @@ Gem::Specification.new do |s|
|
|
38
47
|
s.test_files = [
|
39
48
|
"spec/lawnchair_spec.rb",
|
40
49
|
"spec/spec_helper.rb",
|
41
|
-
"spec/speed.rb"
|
50
|
+
"spec/speed.rb",
|
51
|
+
"spec/storage_engine/abstract_spec.rb",
|
52
|
+
"spec/storage_engine/composite_spec.rb",
|
53
|
+
"spec/storage_engine/in_process_spec.rb",
|
54
|
+
"spec/storage_engine/redis_spec.rb"
|
42
55
|
]
|
43
56
|
|
44
57
|
if s.respond_to? :specification_version then
|
data/lib/lawnchair.rb
CHANGED
@@ -1,72 +1,34 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
Dir[File.dirname(__FILE__) + '/storage_engine/*.rb'].each {|file| require file }
|
2
|
+
require 'view/helper'
|
3
3
|
|
4
4
|
module Lawnchair
|
5
|
-
|
6
|
-
class << self
|
7
|
-
attr_reader :redis
|
8
|
-
|
9
|
-
def connectdb(redis=nil)
|
10
|
-
@redis ||= Redis.new(:db => 11)
|
11
|
-
end
|
12
|
-
|
13
|
-
def flushdb
|
14
|
-
redis.flushdb
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
5
|
class Cache
|
19
|
-
|
20
|
-
|
21
|
-
def self.in_process_store
|
22
|
-
@@in_process_store
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.me(options = {}, &block)
|
26
|
-
raise "Cache key please!" unless options.has_key?(:key)
|
27
|
-
|
6
|
+
def self.me(key, options={}, &block)
|
28
7
|
if options[:in_process]
|
29
|
-
|
8
|
+
store = initialize_composite_store
|
30
9
|
else
|
31
|
-
|
10
|
+
store = Lawnchair::StorageEngine::Redis
|
32
11
|
end
|
33
|
-
|
34
|
-
if key_exists && !options[:force]
|
35
|
-
if options[:in_process]
|
36
|
-
Marshal.load(@@in_process_store[compute_key(options[:key])])
|
37
|
-
else
|
38
|
-
Marshal.load(Lawnchair.redis[compute_key(options[:key])])
|
39
|
-
end
|
40
|
-
else
|
41
|
-
if options[:in_process] && exists?(options[:key])
|
42
|
-
cached_val = Lawnchair.redis[compute_key(options[:key])]
|
43
|
-
@@in_process_store[compute_key(options[:key])] = cached_val
|
44
|
-
return Marshal.dump(cached_val)
|
45
|
-
end
|
46
|
-
|
47
|
-
val = block.call
|
48
|
-
expires_in = compute_expiry(options[:expires_in])
|
49
|
-
dumped_val = Marshal.dump(val)
|
50
|
-
@@in_process_store[compute_key(options[:key])] = dumped_val if options[:in_process]
|
51
|
-
Lawnchair.redis.set(compute_key(options[:key]), dumped_val, expires_in)
|
52
|
-
return val
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.compute_key(key)
|
57
|
-
"Lawnchair:#{key}"
|
12
|
+
store.fetch(key, options, &block)
|
58
13
|
end
|
59
14
|
|
60
|
-
def self.
|
61
|
-
Lawnchair.
|
15
|
+
def self.initialize_composite_store
|
16
|
+
composite_store = Lawnchair::StorageEngine::Composite.new
|
17
|
+
composite_store.register_storage_engine(Lawnchair::StorageEngine::InProcess)
|
18
|
+
composite_store.register_storage_engine(Lawnchair::StorageEngine::Redis)
|
19
|
+
composite_store
|
62
20
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
attr_reader :redis
|
25
|
+
|
26
|
+
def connectdb(redis=nil)
|
27
|
+
@redis = (redis || Redis.new(:db => 11))
|
66
28
|
end
|
67
|
-
|
68
|
-
def
|
69
|
-
|
29
|
+
|
30
|
+
def flushdb
|
31
|
+
redis.flushdb
|
70
32
|
end
|
71
33
|
end
|
72
34
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Lawnchair
|
2
|
+
module StorageEngine
|
3
|
+
class Abstract
|
4
|
+
class << self
|
5
|
+
attr_reader :cache_container
|
6
|
+
|
7
|
+
def cache_container
|
8
|
+
@cache_container ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch(key, options, &block)
|
12
|
+
if exists?(key)
|
13
|
+
value = get(key, options)
|
14
|
+
else
|
15
|
+
value = block.call
|
16
|
+
set(key, value, options)
|
17
|
+
end
|
18
|
+
value
|
19
|
+
end
|
20
|
+
|
21
|
+
def get(key, options={})
|
22
|
+
if options[:raw]
|
23
|
+
cache_container[computed_key(key)]
|
24
|
+
else
|
25
|
+
exists?(key) ? Marshal.load(cache_container[computed_key(key)]) : nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def computed_key(key)
|
30
|
+
raise "Missiing key" if key.nil? || key.empty?
|
31
|
+
prefix = "Lawnchair"
|
32
|
+
"#{prefix}:#{key}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Lawnchair
|
2
|
+
module StorageEngine
|
3
|
+
class Composite
|
4
|
+
attr_reader :storage_engines
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@storage_engines = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def register_storage_engine(storage_engine)
|
11
|
+
storage_engines << storage_engine
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch(key, options, &block)
|
15
|
+
raise "No Storage Engines Configured" if storage_engines.empty?
|
16
|
+
|
17
|
+
value, index = find_in_storage(key, options)
|
18
|
+
value ||= yield
|
19
|
+
place_in_storage(key, value, options, index)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def find_in_storage(key, options)
|
25
|
+
value, index = nil, nil
|
26
|
+
storage_engines.each_with_index do |storage_engine, i|
|
27
|
+
if storage_engine.exists?(key)
|
28
|
+
value = storage_engine.get(key, options)
|
29
|
+
index = i
|
30
|
+
break
|
31
|
+
end
|
32
|
+
end
|
33
|
+
return value, index
|
34
|
+
end
|
35
|
+
|
36
|
+
def place_in_storage(key, value, options, index)
|
37
|
+
storage_engines.each_with_index do |storage_engine, i|
|
38
|
+
break if i == index
|
39
|
+
storage_engine.set(key, value, options)
|
40
|
+
end
|
41
|
+
value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Lawnchair
|
2
|
+
module StorageEngine
|
3
|
+
class InProcess < Abstract
|
4
|
+
@@cache_container = {}
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def cache_container
|
8
|
+
@@cache_container
|
9
|
+
end
|
10
|
+
|
11
|
+
def set(key, value, options={})
|
12
|
+
if options[:raw]
|
13
|
+
cache_container[computed_key(key)] = value
|
14
|
+
else
|
15
|
+
cache_container[computed_key(key)] = Marshal.dump(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def exists?(key)
|
20
|
+
cache_container.has_key?(computed_key(key))
|
21
|
+
end
|
22
|
+
|
23
|
+
def expire!(key)
|
24
|
+
cache_container.delete(computed_key(key))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Lawnchair
|
2
|
+
module StorageEngine
|
3
|
+
class Redis < Abstract
|
4
|
+
class << self
|
5
|
+
def cache_container
|
6
|
+
Lawnchair.redis
|
7
|
+
end
|
8
|
+
|
9
|
+
def set(key, value, options={})
|
10
|
+
ttl = options[:expires_in] || 3600
|
11
|
+
if options[:raw]
|
12
|
+
cache_container.set(computed_key(key), value, ttl)
|
13
|
+
else
|
14
|
+
cache_container.set(computed_key(key), Marshal.dump(value), ttl)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def exists?(key)
|
19
|
+
cache_container.exists(computed_key(key))
|
20
|
+
end
|
21
|
+
|
22
|
+
def expire!(key)
|
23
|
+
cache_container.del(computed_key(key))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/view/helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
module Lawnchair
|
3
|
+
module View
|
4
|
+
module Helper
|
5
|
+
def lawnchair_cache(key, &block)
|
6
|
+
rendered_value = Lawnchair::Cache.me({:key => key, :raw => true}) do
|
7
|
+
capture(&block)
|
8
|
+
end
|
9
|
+
concat(rendered_value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
ActionView::Base.send(:include, Lawnchair::View::Helper)
|
data/spec/lawnchair_spec.rb
CHANGED
@@ -2,94 +2,29 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
2
|
|
3
3
|
describe "Lawnchair::Cache" do
|
4
4
|
describe ".me" do
|
5
|
-
it "raises an exception if no key is given" do
|
6
|
-
lambda do
|
7
|
-
Lawnchair::Cache.me { 1 }
|
8
|
-
end.should raise_error("Cache key please!")
|
9
|
-
end
|
10
|
-
|
11
5
|
it "returns the value if it exists" do
|
12
6
|
expected_object = [1,2,3,4]
|
13
|
-
Lawnchair::Cache.me(
|
7
|
+
Lawnchair::Cache.me("marshalled_array") { expected_object }
|
14
8
|
|
15
|
-
x = Lawnchair::Cache.me(
|
9
|
+
x = Lawnchair::Cache.me("marshalled_array") { "JUNK DATA" }
|
16
10
|
x.should == expected_object
|
17
11
|
end
|
18
12
|
|
19
13
|
it "marshalls the object into redis" do
|
20
14
|
expected_object = [1,2,3,4]
|
21
|
-
Lawnchair::Cache.me(
|
15
|
+
Lawnchair::Cache.me("marshalled_array") { expected_object }
|
22
16
|
|
23
17
|
Marshal.load(Lawnchair.redis["Lawnchair:marshalled_array"]).should == [1,2,3,4]
|
24
18
|
end
|
25
19
|
|
26
|
-
describe "when passed :force => true" do
|
27
|
-
it "expires the key and sets it to the new return value" do
|
28
|
-
initial_expected_object = [1,2,3,4]
|
29
|
-
new_expected_object = [1,2,3]
|
30
|
-
Lawnchair::Cache.me(:key => "marshalled_array") { initial_expected_object }
|
31
|
-
Marshal.load(Lawnchair.redis["Lawnchair:marshalled_array"]).should == [1,2,3,4]
|
32
|
-
|
33
|
-
Lawnchair::Cache.me(:key => "marshalled_array", :force => true) { new_expected_object }
|
34
|
-
Marshal.load(Lawnchair.redis["Lawnchair:marshalled_array"]).should == [1,2,3]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
20
|
describe "when in_process => true" do
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
describe "when the value exists in redis" do
|
49
|
-
it "takes the value from redis and places it in the in process cache" do
|
50
|
-
Lawnchair::Cache.in_process_store.delete("Lawnchair:marshalled_array")
|
51
|
-
expected_object = [1,2,3,4]
|
52
|
-
unexpected_object = [1,2,3,4,5,6,7,8]
|
53
|
-
Lawnchair::Cache.me(:key => "marshalled_array") { expected_object }
|
54
|
-
Lawnchair::Cache.in_process_store["Lawnchair:marshalled_array"].should be_nil
|
55
|
-
Lawnchair::Cache.me(:key => "marshalled_array", :in_process => true) { unexpected_object }
|
56
|
-
Marshal.load(Lawnchair::Cache.in_process_store["Lawnchair:marshalled_array"]).should == expected_object
|
57
|
-
end
|
21
|
+
it "fetches the value to/from the composite store" do
|
22
|
+
mock_composite_engine = Lawnchair::StorageEngine::Composite.new
|
23
|
+
Lawnchair::StorageEngine::Composite.stub!(:new).and_return(mock_composite_engine)
|
24
|
+
|
25
|
+
mock_composite_engine.should_receive(:fetch)
|
26
|
+
Lawnchair::Cache.me("mu", :in_process => true, :raw => true) { "fasa" }
|
58
27
|
end
|
59
28
|
end
|
60
|
-
|
61
|
-
it "sets a default ttl of 60 minutes" do
|
62
|
-
Lawnchair::Cache.me(:key => "pizza") { "muschroom/onion" }
|
63
|
-
Lawnchair.redis.ttl("Lawnchair:pizza").should == 3600 # seconds
|
64
|
-
end
|
65
|
-
|
66
|
-
it "allows you to override the default ttl" do
|
67
|
-
Lawnchair::Cache.me(:key => "pizza", :expires_in => 1) { "muschroom/onion" }
|
68
|
-
Lawnchair.redis.ttl("Lawnchair:pizza").should == 1 # seconds
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe ".exists?" do
|
73
|
-
it "returns false when the key does not exist" do
|
74
|
-
Lawnchair.redis.keys('*').should_not include("Lawnchair:mu")
|
75
|
-
Lawnchair::Cache.exists?("mu").should be_false
|
76
|
-
end
|
77
|
-
|
78
|
-
it "returns true when the key exists" do
|
79
|
-
Lawnchair.redis["Lawnchair:mu"] = "fasa"
|
80
|
-
Lawnchair.redis.keys('*').should include("Lawnchair:mu")
|
81
|
-
Lawnchair::Cache.exists?("mu").should be_true
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
describe ".expire" do
|
86
|
-
it "should only expire the key specified" do
|
87
|
-
Lawnchair.redis["Lawnchair:mu"] = "fasa"
|
88
|
-
Lawnchair.redis["Lawnchair:sim"] = "ba"
|
89
|
-
|
90
|
-
Lawnchair::Cache.expire("mu")
|
91
|
-
Lawnchair.redis["Lawnchair:mu"].should be_nil
|
92
|
-
Lawnchair.redis["Lawnchair:sim"].should == "ba"
|
93
|
-
end
|
94
29
|
end
|
95
30
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,5 +8,12 @@ require 'redis'
|
|
8
8
|
|
9
9
|
Spec::Runner.configure do |config|
|
10
10
|
config.before(:all) { Lawnchair.connectdb(Redis.new(:db => 11)) }
|
11
|
-
config.before(:each)
|
11
|
+
config.before(:each) do
|
12
|
+
Lawnchair.flushdb
|
13
|
+
abstract_store = Lawnchair::StorageEngine::Abstract
|
14
|
+
abstract_store.cache_container.keys.each {|k| abstract_store.cache_container.delete(k)}
|
15
|
+
|
16
|
+
in_process = Lawnchair::StorageEngine::InProcess
|
17
|
+
in_process.cache_container.keys.each {|k| in_process.cache_container.delete(k)}
|
18
|
+
end
|
12
19
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Lawnchair::StorageEngine::Abstract" do
|
4
|
+
attr_reader :abstract_store
|
5
|
+
|
6
|
+
before do
|
7
|
+
@abstract_store = Lawnchair::StorageEngine::Abstract
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".cache_container" do
|
11
|
+
it "should be an empty hash" do
|
12
|
+
abstract_store.cache_container.should == {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".fetch" do
|
17
|
+
context "when key exists" do
|
18
|
+
before do
|
19
|
+
abstract_store.stub!(:exists?).and_return(true)
|
20
|
+
abstract_store.cache_container["Lawnchair:sim"] = "ba"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns the value from the cache" do
|
24
|
+
value = abstract_store.fetch("sim", :raw => true) { "DOESNT MATTER" }
|
25
|
+
value.should == "ba"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when key does not exist" do
|
30
|
+
before do
|
31
|
+
abstract_store.stub!(:exists?).and_return(false)
|
32
|
+
abstract_store.cache_container["Lawnchair:sim"].should be_nil
|
33
|
+
|
34
|
+
class Lawnchair::StorageEngine::Abstract
|
35
|
+
def self.set(key, value, options={})
|
36
|
+
cache_container["Lawnchair:#{key}"] = value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "computes the value and saves it to the cache" do
|
42
|
+
value = abstract_store.fetch("sim", :raw => true) { "ba" }
|
43
|
+
abstract_store.cache_container["Lawnchair:sim"].should == "ba"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe ".get" do
|
49
|
+
context "when raw is true" do
|
50
|
+
it "gets the value in key if it exists" do
|
51
|
+
abstract_store.cache_container["Lawnchair:mu"] = "fasa"
|
52
|
+
abstract_store.get("mu", :raw => true).should == "fasa"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "returns nil if the key does not exist" do
|
56
|
+
abstract_store.cache_container["mu"].should be_nil
|
57
|
+
abstract_store.get("mu", :raw => true).should be_nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when raw is false" do
|
62
|
+
context "when they key exists" do
|
63
|
+
before do
|
64
|
+
abstract_store.stub!(:exists?).and_return(true)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "gets the value in key if it exists and unmarshalls it" do
|
68
|
+
expected_value = "fasa"
|
69
|
+
value = Marshal.dump(expected_value)
|
70
|
+
|
71
|
+
abstract_store.cache_container["Lawnchair:mu"] = value
|
72
|
+
abstract_store.get("mu", :raw => false).should == expected_value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when the key does not exist" do
|
77
|
+
before do
|
78
|
+
abstract_store.stub!(:exists?).and_return(false)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns nil if the key does not exist" do
|
82
|
+
abstract_store.cache_container["Lawnchair:mu"].should be_nil
|
83
|
+
abstract_store.get("mu", :raw => false).should be_nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe ".computed_key" do
|
90
|
+
it "should prepend keys with Lawnchair:" do
|
91
|
+
abstract_store.computed_key("hooo").should == "Lawnchair:hooo"
|
92
|
+
end
|
93
|
+
it "raises an exception if no key is given" do
|
94
|
+
lambda do
|
95
|
+
abstract_store.computed_key("") { 1 }
|
96
|
+
end.should raise_error
|
97
|
+
|
98
|
+
lambda do
|
99
|
+
abstract_store.computed_key(nil)
|
100
|
+
end.should raise_error
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Lawnchair::StorageEngine::Composite" do
|
4
|
+
attr_reader :composite_store
|
5
|
+
|
6
|
+
before do
|
7
|
+
@composite_store = Lawnchair::StorageEngine::Composite.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "initialization" do
|
11
|
+
it "has a collection of storage_engines" do
|
12
|
+
composite_store.storage_engines == []
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#register_storage_engine" do
|
17
|
+
before do
|
18
|
+
composite_store.storage_engines.should be_empty
|
19
|
+
end
|
20
|
+
|
21
|
+
it "adds a storage engine to the collection" do
|
22
|
+
composite_store.register_storage_engine Lawnchair::StorageEngine::Redis
|
23
|
+
composite_store.storage_engines.should == [Lawnchair::StorageEngine::Redis]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#fetch" do
|
28
|
+
context "when no storage engines have been configured" do
|
29
|
+
it "raises an exception" do
|
30
|
+
lambda do
|
31
|
+
composite_store.fetch("key", {}) {x=1}
|
32
|
+
end.should raise_error("No Storage Engines Configured")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when there is only 1 storage engine" do
|
37
|
+
before do
|
38
|
+
composite_store.register_storage_engine Lawnchair::StorageEngine::Redis
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when the key does not exist" do
|
42
|
+
it "sets the value on the key in the storage engine specified" do
|
43
|
+
Lawnchair.redis["Lawnchair:mu"].should be_nil
|
44
|
+
composite_store.fetch("mu", {:raw => true}) { "fasa" }
|
45
|
+
Lawnchair.redis["Lawnchair:mu"].should == "fasa"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when the key exists" do
|
50
|
+
before do
|
51
|
+
Lawnchair.redis["Lawnchair:sim"] = "ba"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "returns the value in the key from the storage engine specified" do
|
55
|
+
value = composite_store.fetch("sim", {:raw => true}) { "DOESNT MATTER" }
|
56
|
+
value.should == "ba"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when there are two storage engines" do
|
62
|
+
before do
|
63
|
+
composite_store.register_storage_engine Lawnchair::StorageEngine::InProcess
|
64
|
+
composite_store.register_storage_engine Lawnchair::StorageEngine::Redis
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when the key exists in the first storage engine" do
|
68
|
+
before do
|
69
|
+
Lawnchair::StorageEngine::InProcess.set("mu", "fasa", :raw => true)
|
70
|
+
Lawnchair::StorageEngine::Redis.get("mu").should be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns the value from the first storage engine" do
|
74
|
+
value = composite_store.fetch("mu", {:raw => true}) { "DOESNT MATTER" }
|
75
|
+
value.should == "fasa"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when the key exists in the second storage engine but not the first" do
|
80
|
+
before do
|
81
|
+
Lawnchair::StorageEngine::InProcess.get("mu").should be_nil
|
82
|
+
Lawnchair::StorageEngine::Redis.set("mu", "fasa", :raw => true)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "places the value in the first storage engine" do
|
86
|
+
composite_store.fetch("mu", {:raw => true}) { "DOESNT MATTER" }.should == "fasa"
|
87
|
+
Lawnchair::StorageEngine::InProcess.get("mu", :raw => true).should == "fasa"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when the key doesnt exist in either storage engine" do
|
92
|
+
before do
|
93
|
+
Lawnchair::StorageEngine::InProcess.get("mu").should be_nil
|
94
|
+
Lawnchair::StorageEngine::Redis.get("mu").should be_nil
|
95
|
+
end
|
96
|
+
|
97
|
+
it "sets the value in both storage engines" do
|
98
|
+
composite_store.fetch("mu", {:raw => true}) { sleep 0.2; "fasa" }
|
99
|
+
Lawnchair::StorageEngine::InProcess.get("mu", :raw => true).should == "fasa"
|
100
|
+
Lawnchair::StorageEngine::Redis.get("mu", :raw => true).should == "fasa"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "only calls the block 1 time for both engines" # spec should take less than .4 seconds to run
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Lawnchair::StorageEngine::InProcessStore" do
|
4
|
+
attr_reader :in_process_store
|
5
|
+
|
6
|
+
before do
|
7
|
+
@in_process_store = Lawnchair::StorageEngine::InProcess
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#cache_container" do
|
11
|
+
it "returns the redis cache object" do
|
12
|
+
Lawnchair::StorageEngine::InProcess.send(:class_variable_set, "@@cache_container", {"Lawnchair:mu" => "fasa"})
|
13
|
+
in_process_store.cache_container["Lawnchair:mu"].should == "fasa"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#set" do
|
18
|
+
context "when raw is true" do
|
19
|
+
it "sets the value" do
|
20
|
+
in_process_store.cache_container["Lawnchair:mu"].should be_nil
|
21
|
+
in_process_store.set("mu", "fasa", :raw => true)
|
22
|
+
in_process_store.cache_container["Lawnchair:mu"].should == "fasa"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when raw is false" do
|
27
|
+
it "sets the value" do
|
28
|
+
value = "fasa"
|
29
|
+
expected_value = Marshal.dump(value)
|
30
|
+
|
31
|
+
in_process_store.cache_container["Lawnchair:mu"].should be_nil
|
32
|
+
in_process_store.set("mu", value, :raw => false)
|
33
|
+
in_process_store.cache_container["Lawnchair:mu"].should == expected_value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "exists?" do
|
39
|
+
it "returns false when the key does not exist" do
|
40
|
+
in_process_store.cache_container.keys.should_not include("Lawnchair:mu")
|
41
|
+
in_process_store.exists?("mu").should be_false
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns true when the key exists" do
|
45
|
+
in_process_store.cache_container["Lawnchair:mu"] = "fasa"
|
46
|
+
in_process_store.cache_container.keys.should include("Lawnchair:mu")
|
47
|
+
in_process_store.exists?("mu").should be_true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#expire!" do
|
52
|
+
it "should only expire the key specified" do
|
53
|
+
in_process_store.cache_container["Lawnchair:mu"] = "fasa"
|
54
|
+
in_process_store.cache_container["Lawnchair:sim"] = "ba"
|
55
|
+
|
56
|
+
in_process_store.expire!("mu")
|
57
|
+
in_process_store.cache_container["Lawnchair:mu"].should be_nil
|
58
|
+
in_process_store.cache_container["Lawnchair:sim"].should == "ba"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Lawnchair::StorageEngine::RedisStore" do
|
4
|
+
attr_reader :redis_store
|
5
|
+
|
6
|
+
before do
|
7
|
+
@redis_store = Lawnchair::StorageEngine::Redis
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#cache_container" do
|
11
|
+
it "returns the redis cache object" do
|
12
|
+
Lawnchair.redis["Lawnchair:mu"] = "fasa"
|
13
|
+
redis_store.cache_container["Lawnchair:mu"].should == "fasa"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#set" do
|
18
|
+
it "sets a default ttl of 60 minutes" do
|
19
|
+
redis_store.set("mu", "fasa")
|
20
|
+
Lawnchair.redis.ttl("Lawnchair:mu").should == 3600 # seconds
|
21
|
+
end
|
22
|
+
|
23
|
+
it "allows you to override the default ttl" do
|
24
|
+
redis_store.set("mu", "fasa", :expires_in => 600)
|
25
|
+
|
26
|
+
Lawnchair.redis.ttl("Lawnchair:mu").should == 600 # seconds
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when raw is true" do
|
30
|
+
it "sets the value in redis" do
|
31
|
+
Lawnchair.redis["Lawnchair:mu"].should be_nil
|
32
|
+
redis_store.set("mu", "fasa", :raw => true)
|
33
|
+
Lawnchair.redis["Lawnchair:mu"].should == "fasa"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when raw is false" do
|
38
|
+
it "sets the value in redis" do
|
39
|
+
value = "fasa"
|
40
|
+
expected_value = Marshal.dump(value)
|
41
|
+
|
42
|
+
Lawnchair.redis["Lawnchair:mu"].should be_nil
|
43
|
+
redis_store.set("mu", value, :raw => false)
|
44
|
+
Lawnchair.redis["Lawnchair:mu"].should == expected_value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "exists?" do
|
50
|
+
it "returns false when the key does not exist" do
|
51
|
+
Lawnchair.redis.keys('*').should_not include("Lawnchair:mu")
|
52
|
+
redis_store.exists?("mu").should be_false
|
53
|
+
end
|
54
|
+
|
55
|
+
it "returns true when the key exists" do
|
56
|
+
Lawnchair.redis["Lawnchair:mu"] = "fasa"
|
57
|
+
Lawnchair.redis.keys('*').should include("Lawnchair:mu")
|
58
|
+
redis_store.exists?("mu").should be_true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#expire!" do
|
63
|
+
it "should only expire the key specified" do
|
64
|
+
Lawnchair.redis["Lawnchair:mu"] = "fasa"
|
65
|
+
Lawnchair.redis["Lawnchair:sim"] = "ba"
|
66
|
+
|
67
|
+
redis_store.expire!("mu")
|
68
|
+
Lawnchair.redis["Lawnchair:mu"].should be_nil
|
69
|
+
Lawnchair.redis["Lawnchair:sim"].should == "ba"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lawnchair
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Wolf
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-12 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -30,11 +30,20 @@ files:
|
|
30
30
|
- VERSION
|
31
31
|
- lawnchair.gemspec
|
32
32
|
- lib/lawnchair.rb
|
33
|
+
- lib/storage_engine/abstract.rb
|
34
|
+
- lib/storage_engine/composite.rb
|
35
|
+
- lib/storage_engine/in_process.rb
|
36
|
+
- lib/storage_engine/redis.rb
|
37
|
+
- lib/view/helper.rb
|
33
38
|
- spec/lawnchair_spec.rb
|
34
39
|
- spec/spec.opts
|
35
40
|
- spec/spec_helper.rb
|
36
41
|
- spec/speed.rb
|
37
42
|
- spec/speed_results.png
|
43
|
+
- spec/storage_engine/abstract_spec.rb
|
44
|
+
- spec/storage_engine/composite_spec.rb
|
45
|
+
- spec/storage_engine/in_process_spec.rb
|
46
|
+
- spec/storage_engine/redis_spec.rb
|
38
47
|
has_rdoc: true
|
39
48
|
homepage: http://github.com/gizm0duck/lawnchair
|
40
49
|
licenses: []
|
@@ -67,3 +76,7 @@ test_files:
|
|
67
76
|
- spec/lawnchair_spec.rb
|
68
77
|
- spec/spec_helper.rb
|
69
78
|
- spec/speed.rb
|
79
|
+
- spec/storage_engine/abstract_spec.rb
|
80
|
+
- spec/storage_engine/composite_spec.rb
|
81
|
+
- spec/storage_engine/in_process_spec.rb
|
82
|
+
- spec/storage_engine/redis_spec.rb
|