jodosha-redis-store 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +15 -1
- data/Rakefile +1 -1
- data/lib/cache/sinatra/redis_store.rb +131 -0
- data/lib/redis-store.rb +3 -1
- data/redis-store.gemspec +3 -3
- data/spec/cache/rails/redis_store_spec.rb +1 -0
- data/spec/cache/sinatra/redis_store_spec.rb +192 -0
- data/spec/spec_helper.rb +1 -0
- metadata +4 -1
data/README.textile
CHANGED
@@ -24,13 +24,27 @@ In your configuration files:
|
|
24
24
|
|
25
25
|
h2. How to use with Merb
|
26
26
|
|
27
|
-
dependency "redis-store", "0.0
|
27
|
+
dependency "jodosha-redis-store", "0.1.0"
|
28
28
|
dependency("merb-cache", merb_gems_version) do
|
29
29
|
Merb::Cache.setup do
|
30
30
|
register(:redis, Merb::Cache::RedisStore, :servers => ["127.0.0.1:6379"])
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
h2. How to use with Sinatra
|
35
|
+
|
36
|
+
require 'rubygems'
|
37
|
+
require 'sinatra'
|
38
|
+
require 'jodosha-redis-store'
|
39
|
+
|
40
|
+
class MyApp < Sinatra::Base
|
41
|
+
register Sinatra::Cache
|
42
|
+
|
43
|
+
get '/hi' do
|
44
|
+
cache.fetch("greet") { "Hello, World!" }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
34
48
|
h2. Copyright
|
35
49
|
|
36
50
|
(c) 2009 Luca Guidi - http://lucaguidi.com, released under the MIT license
|
data/Rakefile
CHANGED
@@ -0,0 +1,131 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Cache
|
3
|
+
class << self
|
4
|
+
def register(app)
|
5
|
+
app.set :cache, RedisStore.new
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class RedisStore
|
10
|
+
# Instantiate the store.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# RedisStore.new # => host: localhost, port: 6379, db: 0
|
14
|
+
# RedisStore.new "example.com" # => host: example.com, port: 6379, db: 0
|
15
|
+
# RedisStore.new "example.com:23682" # => host: example.com, port: 23682, db: 0
|
16
|
+
# RedisStore.new "example.com:23682/1" # => host: example.com, port: 23682, db: 1
|
17
|
+
# RedisStore.new "localhost:6379/0", "localhost:6380/0" # => instantiate a cluster
|
18
|
+
def initialize(*addresses)
|
19
|
+
addresses = extract_addresses(addresses)
|
20
|
+
@data = if addresses.size > 1
|
21
|
+
DistributedMarshaledRedis.new addresses
|
22
|
+
else
|
23
|
+
MarshaledRedis.new addresses.first || {}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def write(key, value, options = nil)
|
28
|
+
method = options && options[:unless_exist] ? :set_unless_exists : :set
|
29
|
+
@data.send method, key, value, options
|
30
|
+
end
|
31
|
+
|
32
|
+
def read(key, options = nil)
|
33
|
+
@data.get key, options
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete(key, options = nil)
|
37
|
+
@data.delete key
|
38
|
+
end
|
39
|
+
|
40
|
+
def exist?(key, options = nil)
|
41
|
+
@data.key? key
|
42
|
+
end
|
43
|
+
|
44
|
+
# Increment a key in the store.
|
45
|
+
#
|
46
|
+
# If the key doesn't exist it will be initialized on 0.
|
47
|
+
# If the key exist but it isn't a Fixnum it will be initialized on 0.
|
48
|
+
#
|
49
|
+
# Example:
|
50
|
+
# We have two objects in cache:
|
51
|
+
# counter # => 23
|
52
|
+
# rabbit # => #<Rabbit:0x5eee6c>
|
53
|
+
#
|
54
|
+
# cache.increment "counter"
|
55
|
+
# cache.read "counter", :raw => true # => "24"
|
56
|
+
#
|
57
|
+
# cache.increment "counter", 6
|
58
|
+
# cache.read "counter", :raw => true # => "30"
|
59
|
+
#
|
60
|
+
# cache.increment "a counter"
|
61
|
+
# cache.read "a counter", :raw => true # => "1"
|
62
|
+
#
|
63
|
+
# cache.increment "rabbit"
|
64
|
+
# cache.read "rabbit", :raw => true # => "1"
|
65
|
+
def increment(key, amount = 1)
|
66
|
+
@data.incr key, amount
|
67
|
+
end
|
68
|
+
|
69
|
+
# Decrement a key in the store
|
70
|
+
#
|
71
|
+
# If the key doesn't exist it will be initialized on 0.
|
72
|
+
# If the key exist but it isn't a Fixnum it will be initialized on 0.
|
73
|
+
#
|
74
|
+
# Example:
|
75
|
+
# We have two objects in cache:
|
76
|
+
# counter # => 23
|
77
|
+
# rabbit # => #<Rabbit:0x5eee6c>
|
78
|
+
#
|
79
|
+
# cache.decrement "counter"
|
80
|
+
# cache.read "counter", :raw => true # => "22"
|
81
|
+
#
|
82
|
+
# cache.decrement "counter", 2
|
83
|
+
# cache.read "counter", :raw => true # => "20"
|
84
|
+
#
|
85
|
+
# cache.decrement "a counter"
|
86
|
+
# cache.read "a counter", :raw => true # => "-1"
|
87
|
+
#
|
88
|
+
# cache.decrement "rabbit"
|
89
|
+
# cache.read "rabbit", :raw => true # => "-1"
|
90
|
+
def decrement(key, amount = 1)
|
91
|
+
@data.decr key, amount
|
92
|
+
end
|
93
|
+
|
94
|
+
# Delete objects for matched keys.
|
95
|
+
#
|
96
|
+
# Example:
|
97
|
+
# cache.delete_matched "rab*"
|
98
|
+
def delete_matched(matcher, options = nil)
|
99
|
+
@data.keys(matcher).each { |key| @data.delete key }
|
100
|
+
end
|
101
|
+
|
102
|
+
def fetch(key, options = {})
|
103
|
+
(!options[:force] && data = read(key, options)) || (write key, yield, options if block_given?)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Clear all the data from the store.
|
107
|
+
def clear
|
108
|
+
@data.flush_db
|
109
|
+
end
|
110
|
+
|
111
|
+
def stats
|
112
|
+
@data.info
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
def extract_addresses(addresses) # TODO extract in a module or a class
|
117
|
+
addresses = addresses.flatten.compact
|
118
|
+
addresses.inject([]) do |result, address|
|
119
|
+
host, port = address.split /\:/
|
120
|
+
port, db = port.split /\// if port
|
121
|
+
address = {}
|
122
|
+
address[:host] = host if host
|
123
|
+
address[:port] = port if port
|
124
|
+
address[:db] = db.to_i if db
|
125
|
+
result << address
|
126
|
+
result
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/redis-store.rb
CHANGED
@@ -3,7 +3,9 @@ require "dist_redis"
|
|
3
3
|
require "redis/marshaled_redis"
|
4
4
|
require "redis/distributed_marshaled_redis"
|
5
5
|
|
6
|
-
if defined?(
|
6
|
+
if defined?(Sinatra)
|
7
|
+
require "cache/sinatra/redis_store"
|
8
|
+
elsif defined?(Merb)
|
7
9
|
# HACK for cyclic dependency: redis-store is required before merb-cache
|
8
10
|
module Merb; module Cache; class AbstractStore; end end end
|
9
11
|
require "cache/merb/redis_store"
|
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.0
|
3
|
+
s.version = "0.1.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/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/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/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/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"]
|
13
13
|
s.extra_rdoc_files = ["README.textile"]
|
14
14
|
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/../../spec_helper")
|
2
|
+
|
3
|
+
class App
|
4
|
+
def initialize
|
5
|
+
@values = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def set(key, value)
|
9
|
+
@values[key] = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(key)
|
13
|
+
@values[key]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Sinatra
|
18
|
+
module Cache
|
19
|
+
describe "RedisStore" do
|
20
|
+
before(:each) do
|
21
|
+
@store = RedisStore.new
|
22
|
+
@dstore = RedisStore.new "localhost:6380/1", "localhost:6381/1"
|
23
|
+
@rabbit = OpenStruct.new :name => "bunny"
|
24
|
+
@white_rabbit = OpenStruct.new :color => "white"
|
25
|
+
with_store_management do |store|
|
26
|
+
store.write "rabbit", @rabbit
|
27
|
+
store.delete "counter"
|
28
|
+
store.delete "rub-a-dub"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should register as extension" do
|
33
|
+
app = App.new
|
34
|
+
Sinatra::Cache.register(app)
|
35
|
+
store = app.get(:cache)
|
36
|
+
store.should be_kind_of(RedisStore)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should accept connection params" do
|
40
|
+
redis = instantiate_store
|
41
|
+
redis.instance_variable_get(:@db).should == 0
|
42
|
+
redis.host.should == "localhost"
|
43
|
+
redis.port.should == "6379"
|
44
|
+
|
45
|
+
redis = instantiate_store "redis.com"
|
46
|
+
redis.host.should == "redis.com"
|
47
|
+
|
48
|
+
redis = instantiate_store "redis.com:6380"
|
49
|
+
redis.host.should == "redis.com"
|
50
|
+
redis.port.should == "6380"
|
51
|
+
|
52
|
+
redis = instantiate_store "redis.com:6380/23"
|
53
|
+
redis.instance_variable_get(:@db).should == 23
|
54
|
+
redis.host.should == "redis.com"
|
55
|
+
redis.port.should == "6380"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should instantiate a ring" do
|
59
|
+
store = instantiate_store
|
60
|
+
store.should be_kind_of(MarshaledRedis)
|
61
|
+
store = instantiate_store ["localhost:6379/0", "localhost:6379/1"]
|
62
|
+
store.should be_kind_of(DistributedMarshaledRedis)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should read the data" do
|
66
|
+
with_store_management do |store|
|
67
|
+
store.read("rabbit").should === @rabbit
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should write the data" do
|
72
|
+
with_store_management do |store|
|
73
|
+
store.write "rabbit", @white_rabbit
|
74
|
+
store.read("rabbit").should === @white_rabbit
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# it "should write the data with expiration time" do
|
79
|
+
# @store.write "rabbit", @white_rabbit, :expires_in => 1.second
|
80
|
+
# @store.read("rabbit").should === @white_rabbit ; sleep 2
|
81
|
+
# @store.read("rabbit").should be_nil
|
82
|
+
# end
|
83
|
+
|
84
|
+
it "should not write data if :unless_exist option is true" do
|
85
|
+
with_store_management do |store|
|
86
|
+
store.write "rabbit", @white_rabbit, :unless_exist => true
|
87
|
+
store.read("rabbit").should === @rabbit
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should read raw data" do
|
92
|
+
with_store_management do |store|
|
93
|
+
store.read("rabbit", :raw => true).should == "\004\bU:\017OpenStruct{\006:\tname\"\nbunny"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should write raw data" do
|
98
|
+
with_store_management do |store|
|
99
|
+
store.write "rabbit", @white_rabbit, :raw => true
|
100
|
+
store.read("rabbit", :raw => true).should == %(#<OpenStruct color="white">)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should delete data" do
|
105
|
+
with_store_management do |store|
|
106
|
+
store.delete "rabbit"
|
107
|
+
store.read("rabbit").should be_nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should delete matched data" do
|
112
|
+
with_store_management do |store|
|
113
|
+
store.delete_matched "rabb*"
|
114
|
+
store.read("rabbit").should be_nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should verify existence of an object in the store" do
|
119
|
+
with_store_management do |store|
|
120
|
+
store.exist?("rabbit").should be_true
|
121
|
+
store.exist?("rab-a-dub").should be_false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should increment a key" do
|
126
|
+
with_store_management do |store|
|
127
|
+
3.times { store.increment "counter" }
|
128
|
+
store.read("counter", :raw => true).to_i.should == 3
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should decrement a key" do
|
133
|
+
with_store_management do |store|
|
134
|
+
3.times { store.increment "counter" }
|
135
|
+
2.times { store.decrement "counter" }
|
136
|
+
store.read("counter", :raw => true).to_i.should == 1
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should increment a key by given value" do
|
141
|
+
with_store_management do |store|
|
142
|
+
store.increment "counter", 3
|
143
|
+
store.read("counter", :raw => true).to_i.should == 3
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should decrement a key by given value" do
|
148
|
+
with_store_management do |store|
|
149
|
+
3.times { store.increment "counter" }
|
150
|
+
store.decrement "counter", 2
|
151
|
+
store.read("counter", :raw => true).to_i.should == 1
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should clear the store" do
|
156
|
+
with_store_management do |store|
|
157
|
+
store.clear
|
158
|
+
store.instance_variable_get(:@data).keys("*").should be_empty
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should return store stats" do
|
163
|
+
with_store_management do |store|
|
164
|
+
store.stats.should_not be_empty
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should fetch data" do
|
169
|
+
with_store_management do |store|
|
170
|
+
store.fetch("rabbit").should == @rabbit
|
171
|
+
store.fetch("rub-a-dub").should be_nil
|
172
|
+
store.fetch("rub-a-dub") { "Flora de Cana" }
|
173
|
+
store.fetch("rub-a-dub").should === "Flora de Cana"
|
174
|
+
store.fetch("rabbit", :force => true).should be_nil # force cache miss
|
175
|
+
# store.fetch("rabbit", :force => true, :expires_in => 1.second) { @white_rabbit }
|
176
|
+
# store.fetch("rabbit").should === @white_rabbit ; sleep 2
|
177
|
+
# store.fetch("rabbit").should be_nil
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
def instantiate_store(addresses = nil)
|
183
|
+
RedisStore.new(addresses).instance_variable_get(:@data)
|
184
|
+
end
|
185
|
+
|
186
|
+
def with_store_management
|
187
|
+
yield @store
|
188
|
+
yield @dstore
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
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.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
@@ -27,12 +27,14 @@ files:
|
|
27
27
|
- Rakefile
|
28
28
|
- lib/cache/merb/redis_store.rb
|
29
29
|
- lib/cache/rails/redis_store.rb
|
30
|
+
- lib/cache/sinatra/redis_store.rb
|
30
31
|
- lib/redis-store.rb
|
31
32
|
- lib/redis/distributed_marshaled_redis.rb
|
32
33
|
- lib/redis/marshaled_redis.rb
|
33
34
|
- redis-store.gemspec
|
34
35
|
- spec/cache/merb/redis_store_spec.rb
|
35
36
|
- spec/cache/rails/redis_store_spec.rb
|
37
|
+
- spec/cache/sinatra/redis_store_spec.rb
|
36
38
|
- spec/config/master.conf
|
37
39
|
- spec/config/single.conf
|
38
40
|
- spec/config/slave.conf
|
@@ -68,5 +70,6 @@ summary: Redis cache and session stores for Ruby web frameworks
|
|
68
70
|
test_files:
|
69
71
|
- spec/cache/merb/redis_store_spec.rb
|
70
72
|
- spec/cache/rails/redis_store_spec.rb
|
73
|
+
- spec/cache/sinatra/redis_store_spec.rb
|
71
74
|
- spec/redis/distributed_marshaled_redis_spec.rb
|
72
75
|
- spec/redis/marshaled_redis_spec.rb
|