lawnchair 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Shane Wolf
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,59 @@
1
+ = Lawnchair
2
+
3
+ Very simple caching mechanism for arbitrary pieces of ruby code using Redis as the distributed (or local) cache
4
+
5
+ == Usage Examples
6
+
7
+
8
+ 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.
9
+
10
+ First, connect to the Redis database
11
+
12
+ Lawnchair.connectdb
13
+
14
+ This will connect to a default database on localhost, if you want to connect to a particular database you can do:
15
+
16
+ Lawnchair.connectdb(Redis.new(:database => 11, :host => "127.0.0.1", :port => 6379))
17
+
18
+ Obligatory example:
19
+
20
+ Lawnchair::Cache.me(:key => "contrived_example") do
21
+ # ideally this would be something a little more computationally expensive, but sleep will have to do
22
+ (1..3).inject([]) do
23
+ |set, i| set << Time.now.strftime("%H:%M:%S")
24
+ sleep 1
25
+ set
26
+ end
27
+ end
28
+
29
+ The return value is exactly what you think it should be
30
+
31
+ ["12:26:08", "12:26:09", "12:26:10"]
32
+
33
+ Now, since it is cached, any time this block method is called (for the next 60 minute) it will return those values. also, you will note it comes back instantly, instead of waiting on those sleeps.
34
+
35
+ If an hour is too long, or short for the cache key expiration you can set that to anything you want using the :expires_in hash key and entering a time in milliseconds
36
+
37
+ Lawnchair::Cache.me(:key => "contrived_example", :expires_in => 1.day) do
38
+ # expensive code to be cached for 24 hours
39
+ end
40
+
41
+ If you need to manually expire a key just call:
42
+
43
+ Lawnchair::Cache.expire("contrived_example")
44
+
45
+ If you need to flush all the values in the database
46
+
47
+ Lawnchair.flushdb
48
+
49
+
50
+ == Note on Patches/Pull Requests
51
+
52
+ * Fork the project.
53
+ * Make your feature addition or bug fix.
54
+ * I will promply ignore anything that is not a refactor that does not have associated specs :)
55
+ * Have fun
56
+
57
+ == Copyright
58
+
59
+ Copyright (c) 2010 Shane Wolf. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "lawnchair"
8
+ gem.summary = "Enclose resource expensive Ruby code in a block and cache it in redis"
9
+ gem.description = "Very simple caching mechanism for arbitrary pieces of resoucre ruby code using Redis as the distributed (or local) cache"
10
+ gem.email = "shanewolf@gmail.com"
11
+ gem.homepage = "http://github.com/gizm0duck/lawnchair"
12
+ gem.authors = ["Shane Wolf"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'spec/rake/spectask'
21
+ Spec::Rake::SpecTask.new(:spec) do |spec|
22
+ spec.libs << 'lib' << 'spec'
23
+ spec.spec_files = FileList['spec/**/*_spec.rb']
24
+ end
25
+
26
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.pattern = 'spec/**/*_spec.rb'
29
+ spec.rcov = true
30
+ end
31
+
32
+ task :spec => :check_dependencies
33
+
34
+ task :default => :spec
35
+
36
+ require 'rake/rdoctask'
37
+ Rake::RDocTask.new do |rdoc|
38
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
39
+
40
+ rdoc.rdoc_dir = 'rdoc'
41
+ rdoc.title = "lawnchair #{version}"
42
+ rdoc.rdoc_files.include('README*')
43
+ rdoc.rdoc_files.include('lib/**/*.rb')
44
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
data/lawnchair.gemspec ADDED
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{lawnchair}
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Shane Wolf"]
12
+ s.date = %q{2010-02-07}
13
+ s.description = %q{Very simple caching mechanism for arbitrary pieces of resoucre ruby code using Redis as the distributed (or local) cache}
14
+ s.email = %q{shanewolf@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lawnchair.gemspec",
27
+ "lib/lawnchair.rb",
28
+ "spec/lawnchair_spec.rb",
29
+ "spec/spec.opts",
30
+ "spec/spec_helper.rb",
31
+ "spec/speed.rb",
32
+ "spec/speed_results.png"
33
+ ]
34
+ s.homepage = %q{http://github.com/gizm0duck/lawnchair}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.5}
38
+ s.summary = %q{Enclose resource expensive Ruby code in a block and cache it in redis}
39
+ s.test_files = [
40
+ "spec/lawnchair_spec.rb",
41
+ "spec/spec_helper.rb",
42
+ "spec/speed.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ else
51
+ end
52
+ else
53
+ end
54
+ end
55
+
data/lib/lawnchair.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'redis'
3
+
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
+ class Cache
19
+ def self.me(options = {}, &block)
20
+ raise "Cache key please!" unless options.has_key?(:key)
21
+
22
+ if exists?(options[:key])
23
+ Marshal.load(Lawnchair.redis[compute_key(options[:key])])
24
+ else
25
+ val = block.call
26
+ expires_in = compute_expiry(options[:expires_in])
27
+ Lawnchair.redis.set(compute_key(options[:key]), Marshal.dump(val), expires_in)
28
+ return val
29
+ end
30
+ end
31
+
32
+ def self.compute_key(key)
33
+ "Lawnchair:#{key}"
34
+ end
35
+
36
+ def self.expire(key)
37
+ Lawnchair.redis.del compute_key(key)
38
+ end
39
+
40
+ def self.exists?(key)
41
+ return !!Lawnchair.redis[compute_key(key)]
42
+ end
43
+
44
+ def self.compute_expiry(ms)
45
+ ms ||= 3600000
46
+ ms/1000
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,77 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Lawnchair::Cache" do
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
+ 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
19
+
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
24
+ end
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 }
30
+
31
+ x = Lawnchair::Cache.me(:key => "marshalled_array") { "JUNK DATA" }
32
+ x.should == expected_object
33
+ end
34
+
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]
40
+ end
41
+ end
42
+
43
+ it "sets a default ttl of 60 minutes" do
44
+ Lawnchair::Cache.me(:key => "pizza") { "muschroom/onion" }
45
+ Lawnchair.redis.ttl("Lawnchair:pizza").should == 3600 # seconds
46
+ end
47
+
48
+ it "allows you to override the default ttl" do
49
+ Lawnchair::Cache.me(:key => "pizza", :expires_in => 1000) { "muschroom/onion" }
50
+ Lawnchair.redis.ttl("Lawnchair:pizza").should == 1 # seconds
51
+ end
52
+ end
53
+
54
+ describe ".exists?" do
55
+ it "returns false when the key does not exist" do
56
+ Lawnchair.redis.keys('*').should_not include("Lawnchair:mu")
57
+ Lawnchair::Cache.exists?("mu").should be_false
58
+ end
59
+
60
+ it "returns true when the key exists" do
61
+ Lawnchair.redis["Lawnchair:mu"] = "fasa"
62
+ Lawnchair.redis.keys('*').should include("Lawnchair:mu")
63
+ Lawnchair::Cache.exists?("mu").should be_true
64
+ end
65
+ end
66
+
67
+ describe ".expire" do
68
+ it "should only expire the key specified" do
69
+ Lawnchair.redis["Lawnchair:mu"] = "fasa"
70
+ Lawnchair.redis["Lawnchair:sim"] = "ba"
71
+
72
+ Lawnchair::Cache.expire("mu")
73
+ Lawnchair.redis["Lawnchair:mu"].should be_nil
74
+ Lawnchair.redis["Lawnchair:sim"].should == "ba"
75
+ end
76
+ end
77
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rubygems'
4
+ require 'lawnchair'
5
+ require 'spec'
6
+ require 'spec/autorun'
7
+ require 'redis'
8
+
9
+ Spec::Runner.configure do |config|
10
+ config.before(:all) { Lawnchair.connectdb(Redis.new(:db => 11)) }
11
+ config.before(:each) { Lawnchair.flushdb }
12
+ end
data/spec/speed.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'benchmark'
2
+ require "#{File.dirname(__FILE__)}/../lib/lawnchair"
3
+
4
+ Lawnchair.redis.flushdb
5
+
6
+ # Totally contrived and fairly useless example... just wanted to make sure the overhead of
7
+ # reading and marshalling the data isn't obscene
8
+
9
+ # *** Performing 1000 iterations ***
10
+ # user system total real
11
+ # cached: 0.140000 0.040000 0.180000 ( 0.292324)
12
+ # not cached: 26.070000 0.620000 26.690000 ( 27.156388)
13
+
14
+ n = (ARGV.shift || 1000).to_i
15
+
16
+ puts "*** Performing #{n} iterations ***"
17
+
18
+ def expensive_stuff
19
+ a = []
20
+ 100.times do
21
+ a << Date.parse("Dec 3. 1981")
22
+ end
23
+ end
24
+
25
+ Benchmark.bm(7) do |x|
26
+ x.report("cached:") do
27
+ (1..n).each do |i|
28
+ Lawnchair::Cache.me(:key => "foo") do
29
+ expensive_stuff
30
+ end
31
+ end
32
+ end
33
+
34
+ x.report("not cached:") do
35
+ (1..n).each do |i|
36
+ expensive_stuff
37
+ end
38
+ end
39
+ end
Binary file
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lawnchair
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Shane Wolf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-07 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Very simple caching mechanism for arbitrary pieces of resoucre ruby code using Redis as the distributed (or local) cache
17
+ email: shanewolf@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - .document
27
+ - .gitignore
28
+ - LICENSE
29
+ - README.rdoc
30
+ - Rakefile
31
+ - VERSION
32
+ - lawnchair.gemspec
33
+ - lib/lawnchair.rb
34
+ - spec/lawnchair_spec.rb
35
+ - spec/spec.opts
36
+ - spec/spec_helper.rb
37
+ - spec/speed.rb
38
+ - spec/speed_results.png
39
+ has_rdoc: true
40
+ homepage: http://github.com/gizm0duck/lawnchair
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --charset=UTF-8
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Enclose resource expensive Ruby code in a block and cache it in redis
67
+ test_files:
68
+ - spec/lawnchair_spec.rb
69
+ - spec/spec_helper.rb
70
+ - spec/speed.rb