jodosha-redis-store 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +24 -10
- data/Rakefile +1 -1
- data/lib/rack/cache/redis_entitystore.rb +51 -0
- data/lib/rack/cache/redis_metastore.rb +42 -0
- data/lib/redis-store.rb +8 -2
- data/redis-store.gemspec +3 -3
- data/spec/rack/cache/entitystore/pony.jpg +0 -0
- data/spec/rack/cache/entitystore/redis_spec.rb +108 -0
- data/spec/rack/cache/metastore/redis_spec.rb +102 -0
- data/spec/spec_helper.rb +3 -0
- metadata +8 -1
data/README.textile
CHANGED
@@ -4,7 +4,7 @@ h2. Installation
|
|
4
4
|
|
5
5
|
Download and install Redis from http://code.google.com/p/redis/
|
6
6
|
|
7
|
-
wget http://redis.googlecode.com/files/redis-0.094.tar.gz
|
7
|
+
wget "http://redis.googlecode.com/files/redis-0.094.tar.gz":http://redis.googlecode.com/files/redis-0.094.tar.gz
|
8
8
|
tar -zxvf redis-0.094.tar.gz
|
9
9
|
cd redis-0.094
|
10
10
|
make
|
@@ -14,37 +14,51 @@ Install the gems
|
|
14
14
|
sudo gem install ezmobius-redis-rb -s http://gems.github.com
|
15
15
|
sudo gem install jodosha-redis-store -s http://gems.github.com
|
16
16
|
|
17
|
-
h2.
|
17
|
+
h2. Cache store
|
18
18
|
|
19
|
-
|
19
|
+
Provides a cache store for your Ruby web framework of choice.
|
20
|
+
|
21
|
+
h3. How to use with Rails
|
20
22
|
|
21
23
|
config.gem "jodosha-redis-store", :source => "http://gems.github.com", :lib => "redis-store"
|
22
24
|
require "redis-store" # HACK Rails tries to instantiate cache first, then load configured gems
|
23
25
|
config.cache_store = :redis_store
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
dependency "jodosha-redis-store", "0.
|
27
|
+
h3. How to use with Merb
|
28
|
+
|
29
|
+
dependency "jodosha-redis-store", "0.2.0"
|
28
30
|
dependency("merb-cache", merb_gems_version) do
|
29
31
|
Merb::Cache.setup do
|
30
32
|
register(:redis, Merb::Cache::RedisStore, :servers => ["127.0.0.1:6379"])
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
34
|
-
|
36
|
+
h3. How to use with Sinatra
|
35
37
|
|
36
38
|
require 'rubygems'
|
37
39
|
require 'sinatra'
|
38
40
|
require 'jodosha-redis-store'
|
39
|
-
|
40
41
|
class MyApp < Sinatra::Base
|
41
42
|
register Sinatra::Cache
|
42
|
-
|
43
43
|
get '/hi' do
|
44
44
|
cache.fetch("greet") { "Hello, World!" }
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
h2. Rack::Cache
|
49
|
+
|
50
|
+
Provides a Redis store for HTTP caching. See "http://github.com/rtomayko/rack-cache":http://github.com/rtomayko/rack-cache
|
51
|
+
|
52
|
+
require "rubygems"
|
53
|
+
require "rack"
|
54
|
+
require "rack/cache"
|
55
|
+
require "jodosha-redis-store"
|
56
|
+
require "application"
|
57
|
+
use Rack::Cache,
|
58
|
+
:metastore => 'redis://localhost:6379/0',
|
59
|
+
:entitystore => 'redis://localhost:6380/1'
|
60
|
+
run Application.new
|
61
|
+
|
48
62
|
h2. Copyright
|
49
63
|
|
50
|
-
(c) 2009 Luca Guidi - http://lucaguidi.com, released under the MIT license
|
64
|
+
(c) 2009 Luca Guidi - "http://lucaguidi.com":http://lucaguidi.com, released under the MIT license
|
data/Rakefile
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
module Rack
|
2
|
+
module Cache
|
3
|
+
class EntityStore
|
4
|
+
class RedisBase < EntityStore
|
5
|
+
# The underlying ::Redis instance used to communicate with the Redis daemon.
|
6
|
+
attr_reader :cache
|
7
|
+
|
8
|
+
extend Rack::Utils
|
9
|
+
|
10
|
+
def open(key)
|
11
|
+
data = read(key)
|
12
|
+
data && [data]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.resolve(uri)
|
16
|
+
db = uri.path.sub(/^\//, '')
|
17
|
+
db = "0" if db.empty?
|
18
|
+
server = { :host => uri.host, :port => uri.port || "6379", :db => db }
|
19
|
+
new server
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Redis < RedisBase
|
24
|
+
def initialize(server, options = {})
|
25
|
+
@cache = ::Redis.new server
|
26
|
+
end
|
27
|
+
|
28
|
+
def exist?(key)
|
29
|
+
cache.key? key
|
30
|
+
end
|
31
|
+
|
32
|
+
def read(key)
|
33
|
+
cache.get key
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(body)
|
37
|
+
buf = StringIO.new
|
38
|
+
key, size = slurp(body){|part| buf.write(part) }
|
39
|
+
[key, size] if cache.set(key, buf.string)
|
40
|
+
end
|
41
|
+
|
42
|
+
def purge(key)
|
43
|
+
cache.delete key
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
REDIS = Redis
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rack
|
2
|
+
module Cache
|
3
|
+
class MetaStore
|
4
|
+
class RedisBase < MetaStore
|
5
|
+
extend Rack::Utils
|
6
|
+
|
7
|
+
# The ::MarshaledRedis object used to communicate with the Redis daemon.
|
8
|
+
attr_reader :cache
|
9
|
+
|
10
|
+
def self.resolve(uri)
|
11
|
+
db = uri.path.sub(/^\//, '')
|
12
|
+
db = "0" if db.empty?
|
13
|
+
server = { :host => uri.host, :port => uri.port || "6379", :db => db }
|
14
|
+
new server
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Redis < RedisBase
|
19
|
+
def initialize(server, options = {})
|
20
|
+
@cache = ::MarshaledRedis.new server
|
21
|
+
end
|
22
|
+
|
23
|
+
def read(key)
|
24
|
+
key = hexdigest(key)
|
25
|
+
cache.get(key) || []
|
26
|
+
end
|
27
|
+
|
28
|
+
def write(key, entries)
|
29
|
+
key = hexdigest(key)
|
30
|
+
cache.set(key, entries)
|
31
|
+
end
|
32
|
+
|
33
|
+
def purge(key)
|
34
|
+
cache.delete(hexdigest(key))
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
REDIS = Redis
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/redis-store.rb
CHANGED
@@ -3,13 +3,19 @@ require "dist_redis"
|
|
3
3
|
require "redis/marshaled_redis"
|
4
4
|
require "redis/distributed_marshaled_redis"
|
5
5
|
|
6
|
+
# Cache store
|
6
7
|
if defined?(Sinatra)
|
7
8
|
require "cache/sinatra/redis_store"
|
8
9
|
elsif defined?(Merb)
|
9
10
|
# HACK for cyclic dependency: redis-store is required before merb-cache
|
10
11
|
module Merb; module Cache; class AbstractStore; end end end
|
11
12
|
require "cache/merb/redis_store"
|
12
|
-
|
13
|
-
require "activesupport"
|
13
|
+
elsif defined?(Rails)
|
14
14
|
require "cache/rails/redis_store"
|
15
15
|
end
|
16
|
+
|
17
|
+
# Rack::Cache
|
18
|
+
if defined?(Rack::Cache)
|
19
|
+
require "rack/cache/redis_metastore"
|
20
|
+
require "rack/cache/redis_entitystore"
|
21
|
+
end
|
data/redis-store.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "redis-store"
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "0.2.0"
|
4
4
|
s.date = "2009-04-30"
|
5
5
|
s.summary = "Redis cache and session stores for Ruby web frameworks"
|
6
6
|
s.author = "Luca Guidi"
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.homepage = "http://lucaguidi.com"
|
9
9
|
s.description = "Redis cache and session stores for Ruby web frameworks"
|
10
10
|
s.has_rdoc = true
|
11
|
-
s.files = ["MIT-LICENSE", "README.textile", "Rakefile", "lib/cache/merb/redis_store.rb", "lib/cache/rails/redis_store.rb", "lib/cache/sinatra/redis_store.rb", "lib/redis-store.rb", "lib/redis/distributed_marshaled_redis.rb", "lib/redis/marshaled_redis.rb", "redis-store.gemspec", "spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/config/master.conf", "spec/config/single.conf", "spec/config/slave.conf", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/spec_helper.rb"]
|
12
|
-
s.test_files = ["spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb"]
|
11
|
+
s.files = ["MIT-LICENSE", "README.textile", "Rakefile", "lib/cache/merb/redis_store.rb", "lib/cache/rails/redis_store.rb", "lib/cache/sinatra/redis_store.rb", "lib/rack/cache/redis_entitystore.rb", "lib/rack/cache/redis_metastore.rb", "lib/redis-store.rb", "lib/redis/distributed_marshaled_redis.rb", "lib/redis/marshaled_redis.rb", "redis-store.gemspec", "spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/config/master.conf", "spec/config/single.conf", "spec/config/slave.conf", "spec/rack/cache/entitystore/pony.jpg", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/spec_helper.rb"]
|
12
|
+
s.test_files = ["spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb"]
|
13
13
|
s.extra_rdoc_files = ["README.textile"]
|
14
14
|
end
|
Binary file
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/../../../spec_helper")
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Cache
|
5
|
+
class MetaStore
|
6
|
+
describe "Redis" do
|
7
|
+
before(:each) do
|
8
|
+
@store = Rack::Cache::EntityStore::Redis.new :host => "localhost"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have the class referenced by homonym constant" do
|
12
|
+
Rack::Cache::EntityStore::REDIS.should be(Rack::Cache::EntityStore::Redis)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should resolve the connection uri" do
|
16
|
+
cache = Redis.resolve(uri("redis://127.0.0.1")).cache
|
17
|
+
cache.should be_kind_of(::MarshaledRedis)
|
18
|
+
cache.host.should == "127.0.0.1"
|
19
|
+
cache.port.should == "6379"
|
20
|
+
cache.instance_variable_get(:@db).should == "0"
|
21
|
+
|
22
|
+
cache = Redis.resolve(uri("redis://127.0.0.1:6380")).cache
|
23
|
+
cache.port.should == 6380
|
24
|
+
|
25
|
+
cache = Redis.resolve(uri("redis://127.0.0.1/11")).cache
|
26
|
+
cache.instance_variable_get(:@db).should == "11"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'responds to all required messages' do
|
30
|
+
%w[read open write exist?].each do |message|
|
31
|
+
@store.should respond_to(message)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'stores bodies with #write' do
|
36
|
+
key, size = @store.write(['My wild love went riding,'])
|
37
|
+
key.should_not be_nil
|
38
|
+
# key.should be_sha_like TODO re-enable
|
39
|
+
|
40
|
+
data = @store.read(key)
|
41
|
+
data.should == 'My wild love went riding,'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'correctly determines whether cached body exists for key with #exist?' do
|
45
|
+
key, size = @store.write(['She rode to the devil,'])
|
46
|
+
@store.should be_exist(key)
|
47
|
+
@store.should_not be_exist('938jasddj83jasdh4438021ksdfjsdfjsdsf')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'can read data written with #write' do
|
51
|
+
key, size = @store.write(['And asked him to pay.'])
|
52
|
+
data = @store.read(key)
|
53
|
+
data.should == 'And asked him to pay.'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'gives a 40 character SHA1 hex digest from #write' do
|
57
|
+
key, size = @store.write(['she rode to the sea;'])
|
58
|
+
key.should_not be_nil
|
59
|
+
key.length.should == 40
|
60
|
+
key.should =~ /^[0-9a-z]+$/
|
61
|
+
key.should == '90a4c84d51a277f3dafc34693ca264531b9f51b6'
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns the entire body as a String from #read' do
|
65
|
+
key, size = @store.write(['She gathered together'])
|
66
|
+
@store.read(key).should == 'She gathered together'
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns nil from #read when key does not exist' do
|
70
|
+
@store.read('87fe0a1ae82a518592f6b12b0183e950b4541c62').should be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns a Rack compatible body from #open' do
|
74
|
+
key, size = @store.write(['Some shells for her hair.'])
|
75
|
+
body = @store.open(key)
|
76
|
+
body.should respond_to(:each)
|
77
|
+
buf = ''
|
78
|
+
body.each { |part| buf << part }
|
79
|
+
buf.should == 'Some shells for her hair.'
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns nil from #open when key does not exist' do
|
83
|
+
@store.open('87fe0a1ae82a518592f6b12b0183e950b4541c62').should be_nil
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'can store largish bodies with binary data' do
|
87
|
+
pony = ::File.open(::File.dirname(__FILE__) + '/pony.jpg', 'rb') { |f| f.read }
|
88
|
+
key, size = @store.write([pony])
|
89
|
+
key.should == 'd0f30d8659b4d268c5c64385d9790024c2d78deb'
|
90
|
+
data = @store.read(key)
|
91
|
+
data.length.should == pony.length
|
92
|
+
data.hash.should == pony.hash
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'deletes stored entries with #purge' do
|
96
|
+
key, size = @store.write(['My wild love went riding,'])
|
97
|
+
@store.purge(key).should be_nil
|
98
|
+
@store.read(key).should be_nil
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def uri(uri)
|
103
|
+
URI.parse uri
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/../../../spec_helper")
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Cache
|
5
|
+
class MetaStore
|
6
|
+
describe "Redis" do
|
7
|
+
before(:each) do
|
8
|
+
@store = Redis.resolve uri("redis://127.0.0.1")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have the class referenced by homonym constant" do
|
12
|
+
Rack::Cache::MetaStore::REDIS.should be(Rack::Cache::MetaStore::Redis)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should resolve the connection uri" do
|
16
|
+
cache = Redis.resolve(uri("redis://127.0.0.1")).cache
|
17
|
+
cache.should be_kind_of(::MarshaledRedis)
|
18
|
+
cache.host.should == "127.0.0.1"
|
19
|
+
cache.port.should == "6379"
|
20
|
+
cache.instance_variable_get(:@db).should == "0"
|
21
|
+
|
22
|
+
cache = Redis.resolve(uri("redis://127.0.0.1:6380")).cache
|
23
|
+
cache.port.should == 6380
|
24
|
+
|
25
|
+
cache = Redis.resolve(uri("redis://127.0.0.1/11")).cache
|
26
|
+
cache.instance_variable_get(:@db).should == "11"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'writes a list of negotation tuples with #write' do
|
30
|
+
lambda { @store.write('/test', [[{}, {}]]) }.should_not raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'reads a list of negotation tuples with #read' do
|
34
|
+
@store.write('/test', [[{},{}],[{},{}]])
|
35
|
+
tuples = @store.read('/test')
|
36
|
+
tuples.should == [ [{},{}], [{},{}] ]
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'reads an empty list with #read when nothing cached at key' do
|
40
|
+
@store.read('/nothing').should be_empty
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'removes entries for key with #purge' do
|
44
|
+
@store.write('/test', [[{},{}]])
|
45
|
+
@store.read('/test').should_not be_empty
|
46
|
+
|
47
|
+
@store.purge('/test')
|
48
|
+
@store.read('/test').should be_empty
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'succeeds when purging non-existing entries' do
|
52
|
+
@store.read('/test').should be_empty
|
53
|
+
@store.purge('/test')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns nil from #purge' do
|
57
|
+
@store.write('/test', [[{},{}]])
|
58
|
+
@store.purge('/test').should be_nil
|
59
|
+
@store.read('/test').should == []
|
60
|
+
end
|
61
|
+
|
62
|
+
%w[/test http://example.com:8080/ /test?x=y /test?x=y&p=q].each do |key|
|
63
|
+
it "can read and write key: '#{key}'" do
|
64
|
+
lambda { @store.write(key, [[{},{}]]) }.should_not raise_error
|
65
|
+
@store.read(key).should == [[{},{}]]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "can read and write fairly large keys" do
|
70
|
+
key = "b" * 4096
|
71
|
+
lambda { @store.write(key, [[{},{}]]) }.should_not raise_error
|
72
|
+
@store.read(key).should == [[{},{}]]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "allows custom cache keys from block" do
|
76
|
+
request = mock_request('/test', {})
|
77
|
+
request.env['rack-cache.cache_key'] =
|
78
|
+
lambda { |request| request.path_info.reverse }
|
79
|
+
@store.cache_key(request).should == 'tset/'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "allows custom cache keys from class" do
|
83
|
+
request = mock_request('/test', {})
|
84
|
+
request.env['rack-cache.cache_key'] = Class.new do
|
85
|
+
def self.call(request); request.path_info.reverse end
|
86
|
+
end
|
87
|
+
@store.cache_key(request).should == 'tset/'
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def mock_request(uri, opts)
|
92
|
+
env = Rack::MockRequest.env_for(uri, opts || {})
|
93
|
+
Rack::Cache::Request.new(env)
|
94
|
+
end
|
95
|
+
|
96
|
+
def uri(uri)
|
97
|
+
URI.parse uri
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jodosha-redis-store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
@@ -28,6 +28,8 @@ files:
|
|
28
28
|
- lib/cache/merb/redis_store.rb
|
29
29
|
- lib/cache/rails/redis_store.rb
|
30
30
|
- lib/cache/sinatra/redis_store.rb
|
31
|
+
- lib/rack/cache/redis_entitystore.rb
|
32
|
+
- lib/rack/cache/redis_metastore.rb
|
31
33
|
- lib/redis-store.rb
|
32
34
|
- lib/redis/distributed_marshaled_redis.rb
|
33
35
|
- lib/redis/marshaled_redis.rb
|
@@ -38,6 +40,9 @@ files:
|
|
38
40
|
- spec/config/master.conf
|
39
41
|
- spec/config/single.conf
|
40
42
|
- spec/config/slave.conf
|
43
|
+
- spec/rack/cache/entitystore/pony.jpg
|
44
|
+
- spec/rack/cache/entitystore/redis_spec.rb
|
45
|
+
- spec/rack/cache/metastore/redis_spec.rb
|
41
46
|
- spec/redis/distributed_marshaled_redis_spec.rb
|
42
47
|
- spec/redis/marshaled_redis_spec.rb
|
43
48
|
- spec/spec_helper.rb
|
@@ -71,5 +76,7 @@ test_files:
|
|
71
76
|
- spec/cache/merb/redis_store_spec.rb
|
72
77
|
- spec/cache/rails/redis_store_spec.rb
|
73
78
|
- spec/cache/sinatra/redis_store_spec.rb
|
79
|
+
- spec/rack/cache/entitystore/redis_spec.rb
|
80
|
+
- spec/rack/cache/metastore/redis_spec.rb
|
74
81
|
- spec/redis/distributed_marshaled_redis_spec.rb
|
75
82
|
- spec/redis/marshaled_redis_spec.rb
|