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 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
- http://code.google.com/p/redis/
7
- and
8
- http://github.com/ezmobius/redis-rb/
6
+ git clone git://github.com/ezmobius/redis-rb.git
7
+ cd redis-rb
8
+ rake redis:install
9
9
 
10
- == Usage Examples
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
- First, connect to the Redis database
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
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.1"
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-07}
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
- ".document",
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 ||= Redis.new(:db => 11)
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 exists?(options[:key])
23
- Marshal.load(Lawnchair.redis[compute_key(options[:key])])
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
- Lawnchair.redis.set(compute_key(options[:key]), Marshal.dump(val), 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)
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 compute_key(key)
61
+ Lawnchair.redis.del(compute_key(key))
38
62
  end
39
63
 
40
64
  def self.exists?(key)
41
- return !!Lawnchair.redis[compute_key(key)]
65
+ return Lawnchair.redis.exists(compute_key(key))
42
66
  end
43
67
 
44
68
  def self.compute_expiry(seconds)
@@ -8,35 +8,53 @@ describe "Lawnchair::Cache" do
8
8
  end.should raise_error("Cache key please!")
9
9
  end
10
10
 
11
- context "when the object the block returns is a string" do
12
- it "returns the item from the cache if it exists" do
13
- Lawnchair::Cache.me(:key => "yogurt") { "strawberry/banana" }
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
- it "sets the return value in the cache key given" do
21
- Lawnchair::Cache.me(:key => "pizza") { "muschroom/onion" }
22
- Lawnchair.redis["Lawnchair:pizza"].should == Marshal.dump("muschroom/onion")
23
- end
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
- context "when the object the block returns is an object" do
27
- it "returns the value if it exists" do
28
- expected_object = [1,2,3,4]
29
- Lawnchair::Cache.me(:key => "marshalled_array") { expected_object }
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
- x = Lawnchair::Cache.me(:key => "marshalled_array") { "JUNK DATA" }
32
- x.should == expected_object
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
- it "marshalls the object into redis" do
36
- expected_object = [1,2,3,4]
37
- Lawnchair::Cache.me(:key => "marshalled_array") { expected_object }
38
-
39
- Marshal.load(Lawnchair.redis["Lawnchair:marshalled_array"]).should == [1,2,3,4]
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.redis.flushdb
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:") do
27
+ x.report("cached:\t\t") do
27
28
  (1..n).each do |i|
28
- Lawnchair::Cache.me(:key => "foo") do
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("not cached:") do
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.1
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-07 00:00:00 -08:00
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
data/.document DELETED
@@ -1,5 +0,0 @@
1
- README.rdoc
2
- lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
5
- LICENSE