lawnchair 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +23 -5
- data/VERSION +1 -1
- data/lawnchair.gemspec +3 -4
- data/lib/lawnchair.rb +30 -6
- data/spec/lawnchair_spec.rb +41 -23
- data/spec/speed.rb +14 -5
- metadata +2 -3
- data/.document +0 -5
data/README.rdoc
CHANGED
@@ -3,16 +3,21 @@
|
|
3
3
|
Very simple caching mechanism for arbitrary pieces of ruby code using Redis as the distributed (or local) cache
|
4
4
|
|
5
5
|
== Prerequisites
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
git clone git://github.com/ezmobius/redis-rb.git
|
7
|
+
cd redis-rb
|
8
|
+
rake redis:install
|
9
9
|
|
10
|
-
==
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
sudo gem install lawnchair
|
11
13
|
|
14
|
+
== Usage Examples
|
12
15
|
|
13
16
|
All you really need to do is wrap some expensive piece of Ruby code in the Lawnchair::Cache.me method as a block and it will be evaluated and the return value will cached in the given cache key.
|
14
17
|
|
15
|
-
|
18
|
+
MAKE SURE REDIS SERVER IS RUNNING PRIOR TO TRYING ANYTHING BELOW!!!
|
19
|
+
|
20
|
+
First, connect to the Redis database. This would most likely go into an environment.rb.
|
16
21
|
|
17
22
|
Lawnchair.connectdb
|
18
23
|
|
@@ -42,6 +47,19 @@ If an hour is too long, or short for the cache key expiration you can set that t
|
|
42
47
|
Lawnchair::Cache.me(:key => "contrived_example", :expires_in => 1.day) do
|
43
48
|
# expensive code to be cached for 24 hours
|
44
49
|
end
|
50
|
+
|
51
|
+
== In Process Caching
|
52
|
+
|
53
|
+
If you want to get really fancy you can cache the values in process as well as in Redis. This can be a fairly significant win
|
54
|
+
if you are running the Redis server on a different physical machine as all network latency is taken out of the equation, especially if you are hitting a cache key many times on the same request. Also, it's probably best not to store TONS of keys in there, as your ruby process can bloat fairly quickly if you put everything in there. Also, these will persist as long as the process is running, unless you manually expire it.
|
55
|
+
|
56
|
+
Lawnchair::Cache.me(:key => "contrived_example", :in_process => true) do
|
57
|
+
# expensive code to be cached in process AND in redis
|
58
|
+
end
|
59
|
+
|
60
|
+
This code will get cached in redis as well, so each different process that runs the expensive code in the block will get the value from redis, instead of having to run it to get the value.
|
61
|
+
|
62
|
+
== Odds and Ends
|
45
63
|
|
46
64
|
If you need to manually expire a key just call:
|
47
65
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.2
|
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.3.
|
8
|
+
s.version = "0.3.2"
|
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 = [
|
@@ -17,8 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
"README.rdoc"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
|
-
".
|
21
|
-
".gitignore",
|
20
|
+
".gitignore",
|
22
21
|
"LICENSE",
|
23
22
|
"README.rdoc",
|
24
23
|
"Rakefile",
|
data/lib/lawnchair.rb
CHANGED
@@ -7,7 +7,7 @@ module Lawnchair
|
|
7
7
|
attr_reader :redis
|
8
8
|
|
9
9
|
def connectdb(redis=nil)
|
10
|
-
@redis
|
10
|
+
@redis = (redis || Redis.new(:db => 11))
|
11
11
|
end
|
12
12
|
|
13
13
|
def flushdb
|
@@ -16,15 +16,39 @@ module Lawnchair
|
|
16
16
|
end
|
17
17
|
|
18
18
|
class Cache
|
19
|
+
@@in_process_store = {}
|
20
|
+
|
21
|
+
def self.in_process_store
|
22
|
+
@@in_process_store
|
23
|
+
end
|
24
|
+
|
19
25
|
def self.me(options = {}, &block)
|
20
26
|
raise "Cache key please!" unless options.has_key?(:key)
|
21
27
|
|
22
|
-
if
|
23
|
-
|
28
|
+
if options[:in_process]
|
29
|
+
key_exists = @@in_process_store.has_key?([compute_key(options[:key])])
|
30
|
+
else
|
31
|
+
key_exists = exists?(options[:key])
|
32
|
+
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
|
24
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
|
+
|
25
47
|
val = block.call
|
26
48
|
expires_in = compute_expiry(options[:expires_in])
|
27
|
-
|
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)
|
28
52
|
return val
|
29
53
|
end
|
30
54
|
end
|
@@ -34,11 +58,11 @@ module Lawnchair
|
|
34
58
|
end
|
35
59
|
|
36
60
|
def self.expire(key)
|
37
|
-
Lawnchair.redis.del
|
61
|
+
Lawnchair.redis.del(compute_key(key))
|
38
62
|
end
|
39
63
|
|
40
64
|
def self.exists?(key)
|
41
|
-
return
|
65
|
+
return Lawnchair.redis.exists(compute_key(key))
|
42
66
|
end
|
43
67
|
|
44
68
|
def self.compute_expiry(seconds)
|
data/spec/lawnchair_spec.rb
CHANGED
@@ -8,35 +8,53 @@ describe "Lawnchair::Cache" do
|
|
8
8
|
end.should raise_error("Cache key please!")
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Lawnchair.redis["Lawnchair:yogurt"] = Marshal.dump("FROM THE CACHE")
|
16
|
-
x = Lawnchair::Cache.me(:key => "yogurt") { "strawberry/banana" }
|
17
|
-
x.should == "FROM THE CACHE"
|
18
|
-
end
|
11
|
+
it "returns the value if it exists" do
|
12
|
+
expected_object = [1,2,3,4]
|
13
|
+
Lawnchair::Cache.me(:key => "marshalled_array") { expected_object }
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
x = Lawnchair::Cache.me(:key => "marshalled_array") { "JUNK DATA" }
|
16
|
+
x.should == expected_object
|
17
|
+
end
|
18
|
+
|
19
|
+
it "marshalls the object into redis" do
|
20
|
+
expected_object = [1,2,3,4]
|
21
|
+
Lawnchair::Cache.me(:key => "marshalled_array") { expected_object }
|
22
|
+
|
23
|
+
Marshal.load(Lawnchair.redis["Lawnchair:marshalled_array"]).should == [1,2,3,4]
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
it "
|
28
|
-
|
29
|
-
|
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]
|
30
32
|
|
31
|
-
|
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
|
+
describe "when in_process => true" do
|
39
|
+
describe "when the value does not exist in redis" do
|
40
|
+
it "should set the value to the key in process as well as in redis" do
|
41
|
+
expected_object = [1,2,3,4]
|
42
|
+
Lawnchair::Cache.me(:key => "marshalled_array", :in_process => true) { expected_object }
|
43
|
+
Marshal.load(Lawnchair.redis["Lawnchair:marshalled_array"]).should == [1,2,3,4]
|
44
|
+
Marshal.load(Lawnchair::Cache.in_process_store["Lawnchair:marshalled_array"]).should == [1,2,3,4]
|
45
|
+
end
|
33
46
|
end
|
34
47
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
40
58
|
end
|
41
59
|
end
|
42
60
|
|
data/spec/speed.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'benchmark'
|
2
2
|
require "#{File.dirname(__FILE__)}/../lib/lawnchair"
|
3
3
|
|
4
|
-
Lawnchair.
|
4
|
+
Lawnchair.connectdb
|
5
|
+
Lawnchair.flushdb
|
5
6
|
|
6
7
|
# Totally contrived and fairly useless example... just wanted to make sure the overhead of
|
7
8
|
# reading and marshalling the data isn't obscene
|
@@ -23,17 +24,25 @@ def expensive_stuff
|
|
23
24
|
end
|
24
25
|
|
25
26
|
Benchmark.bm(7) do |x|
|
26
|
-
x.report("cached
|
27
|
+
x.report("cached:\t\t") do
|
27
28
|
(1..n).each do |i|
|
28
|
-
Lawnchair::Cache.me(:key => "
|
29
|
+
Lawnchair::Cache.me(:key => "redis_cache") do
|
29
30
|
expensive_stuff
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
|
-
x.report("
|
35
|
+
x.report("in process cached:") do
|
36
|
+
(1..n).each do |i|
|
37
|
+
Lawnchair::Cache.me(:key => "in_process_cache") do
|
38
|
+
expensive_stuff
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
x.report("not cached:\t\t") do
|
35
44
|
(1..n).each do |i|
|
36
45
|
expensive_stuff
|
37
46
|
end
|
38
47
|
end
|
39
|
-
end
|
48
|
+
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.3.
|
4
|
+
version: 0.3.2
|
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
|
|
@@ -23,7 +23,6 @@ extra_rdoc_files:
|
|
23
23
|
- LICENSE
|
24
24
|
- README.rdoc
|
25
25
|
files:
|
26
|
-
- .document
|
27
26
|
- .gitignore
|
28
27
|
- LICENSE
|
29
28
|
- README.rdoc
|