jodosha-redis-store 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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. How to use with Rails
17
+ h2. Cache store
18
18
 
19
- In your configuration files:
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
- h2. How to use with Merb
26
-
27
- dependency "jodosha-redis-store", "0.1.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
- h2. How to use with Sinatra
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
@@ -5,7 +5,7 @@ require 'rake/testtask'
5
5
  require 'rake/rdoctask'
6
6
  require 'spec/rake/spectask'
7
7
 
8
- REDIS_STORE_VERSION = "0.1.0"
8
+ REDIS_STORE_VERSION = "0.2.0"
9
9
 
10
10
  task :default => :spec
11
11
 
@@ -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
- else # rails or ruby application
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.1.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
@@ -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
@@ -4,6 +4,9 @@ require "ostruct"
4
4
  require "spec"
5
5
  require "redis"
6
6
  require "merb"
7
+ require "rack/cache"
8
+ require "rack/cache/metastore"
9
+ require "rack/cache/entitystore"
7
10
  require "redis-store"
8
11
  require "activesupport"
9
12
  require "cache/rails/redis_store"
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.1.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