nono-redis-store 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +11 -0
  2. data/CHANGELOG +262 -0
  3. data/Gemfile +33 -0
  4. data/Gemfile.lock +194 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +191 -0
  7. data/Rakefile +60 -0
  8. data/VERSION +1 -0
  9. data/lib/action_controller/session/redis_session_store.rb +74 -0
  10. data/lib/active_support/cache/redis_store.rb +228 -0
  11. data/lib/cache/merb/redis_store.rb +75 -0
  12. data/lib/cache/sinatra/redis_store.rb +126 -0
  13. data/lib/i18n/backend/redis.rb +67 -0
  14. data/lib/rack/cache/redis_entitystore.rb +51 -0
  15. data/lib/rack/cache/redis_metastore.rb +42 -0
  16. data/lib/rack/session/merb.rb +32 -0
  17. data/lib/rack/session/redis.rb +81 -0
  18. data/lib/redis-store.rb +44 -0
  19. data/lib/redis/distributed_store.rb +35 -0
  20. data/lib/redis/factory.rb +46 -0
  21. data/lib/redis/store.rb +30 -0
  22. data/lib/redis/store/interface.rb +17 -0
  23. data/lib/redis/store/marshalling.rb +41 -0
  24. data/lib/redis/store/namespace.rb +54 -0
  25. data/lib/redis/store/ttl.rb +37 -0
  26. data/lib/redis/store/version.rb +11 -0
  27. data/redis-store.gemspec +103 -0
  28. data/spec/action_controller/session/redis_session_store_spec.rb +121 -0
  29. data/spec/active_support/cache/redis_store_spec.rb +405 -0
  30. data/spec/cache/merb/redis_store_spec.rb +143 -0
  31. data/spec/cache/sinatra/redis_store_spec.rb +192 -0
  32. data/spec/config/master.conf +312 -0
  33. data/spec/config/single.conf +312 -0
  34. data/spec/config/slave.conf +312 -0
  35. data/spec/i18n/backend/redis_spec.rb +56 -0
  36. data/spec/rack/cache/entitystore/pony.jpg +0 -0
  37. data/spec/rack/cache/entitystore/redis_spec.rb +120 -0
  38. data/spec/rack/cache/metastore/redis_spec.rb +255 -0
  39. data/spec/rack/session/redis_spec.rb +234 -0
  40. data/spec/redis/distributed_store_spec.rb +47 -0
  41. data/spec/redis/factory_spec.rb +110 -0
  42. data/spec/redis/store/interface_spec.rb +23 -0
  43. data/spec/redis/store/marshalling_spec.rb +83 -0
  44. data/spec/redis/store/namespace_spec.rb +76 -0
  45. data/spec/redis/store/version_spec.rb +7 -0
  46. data/spec/spec_helper.rb +43 -0
  47. data/tasks/redis.tasks.rb +220 -0
  48. metadata +141 -0
@@ -0,0 +1,75 @@
1
+ module Merb
2
+ module Cache
3
+ class RedisStore < AbstractStore
4
+ # Instantiate the store.
5
+ #
6
+ # Example:
7
+ # RedisStore.new
8
+ # # => host: localhost, port: 6379, db: 0
9
+ #
10
+ # RedisStore.new :servers => ["example.com"]
11
+ # # => host: example.com, port: 6379, db: 0
12
+ #
13
+ # RedisStore.new :servers => ["example.com:23682"]
14
+ # # => host: example.com, port: 23682, db: 0
15
+ #
16
+ # RedisStore.new :servers => ["example.com:23682/1"]
17
+ # # => host: example.com, port: 23682, db: 1
18
+ #
19
+ # RedisStore.new :servers => ["example.com:23682/1/theplaylist"]
20
+ # # => host: example.com, port: 23682, db: 1, namespace: theplaylist
21
+ #
22
+ # RedisStore.new :servers => ["localhost:6379/0", "localhost:6380/0"]
23
+ # # => instantiate a cluster
24
+ def initialize(config = { })
25
+ @data = Redis::Factory.create config[:servers]
26
+ end
27
+
28
+ def writable?(key, parameters = {}, conditions = {})
29
+ true
30
+ end
31
+
32
+ def read(key, parameters = {}, conditions = {})
33
+ @data.get normalize(key, parameters), conditions
34
+ end
35
+
36
+ def write(key, data = nil, parameters = {}, conditions = {})
37
+ if writable?(key, parameters, conditions)
38
+ method = conditions && conditions[:unless_exist] ? :setnx : :set
39
+ @data.send method, normalize(key, parameters), data, conditions
40
+ end
41
+ end
42
+
43
+ def write_all(key, data = nil, parameters = {}, conditions = {})
44
+ write key, data, parameters, conditions
45
+ end
46
+
47
+ def fetch(key, parameters = {}, conditions = {}, &blk)
48
+ read(key, parameters) || (write key, yield, parameters, conditions if block_given?)
49
+ end
50
+
51
+ def exists?(key, parameters = {})
52
+ @data.exists normalize(key, parameters)
53
+ end
54
+
55
+ def delete(key, parameters = {})
56
+ @data.del normalize(key, parameters)
57
+ end
58
+
59
+ def delete_all
60
+ @data.flushdb
61
+ end
62
+
63
+ def delete_all!
64
+ delete_all
65
+ end
66
+
67
+ private
68
+ # Returns cache key calculated from base key
69
+ # and SHA2 hex from parameters.
70
+ def normalize(key, parameters = {})
71
+ parameters.empty? ? "#{key}" : "#{key}--#{parameters.to_sha2}"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,126 @@
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
14
+ # # => host: localhost, port: 6379, db: 0
15
+ #
16
+ # RedisStore.new "example.com"
17
+ # # => host: example.com, port: 6379, db: 0
18
+ #
19
+ # RedisStore.new "example.com:23682"
20
+ # # => host: example.com, port: 23682, db: 0
21
+ #
22
+ # RedisStore.new "example.com:23682/1"
23
+ # # => host: example.com, port: 23682, db: 1
24
+ #
25
+ # RedisStore.new "example.com:23682/1/theplaylist"
26
+ # # => host: example.com, port: 23682, db: 1, namespace: theplaylist
27
+ #
28
+ # RedisStore.new "localhost:6379/0", "localhost:6380/0"
29
+ # # => instantiate a cluster
30
+ def initialize(*addresses)
31
+ @data = Redis::Factory.create addresses
32
+ end
33
+
34
+ def write(key, value, options = nil)
35
+ if options && options[:unless_exist]
36
+ @data.setnx key, value, options
37
+ else
38
+ @data.set key, value, options
39
+ end
40
+ end
41
+
42
+ def read(key, options = nil)
43
+ @data.get(key, options)
44
+ end
45
+
46
+ def delete(key, options = nil)
47
+ @data.del key
48
+ end
49
+
50
+ def exist?(key, options = nil)
51
+ @data.exists key
52
+ end
53
+
54
+ # Increment a key in the store.
55
+ #
56
+ # If the key doesn't exist it will be initialized on 0.
57
+ # If the key exist but it isn't a Fixnum it will be initialized on 0.
58
+ #
59
+ # Example:
60
+ # We have two objects in cache:
61
+ # counter # => 23
62
+ # rabbit # => #<Rabbit:0x5eee6c>
63
+ #
64
+ # cache.increment "counter"
65
+ # cache.read "counter", :raw => true # => "24"
66
+ #
67
+ # cache.increment "counter", 6
68
+ # cache.read "counter", :raw => true # => "30"
69
+ #
70
+ # cache.increment "a counter"
71
+ # cache.read "a counter", :raw => true # => "1"
72
+ #
73
+ # cache.increment "rabbit"
74
+ # cache.read "rabbit", :raw => true # => "1"
75
+ def increment(key, amount = 1)
76
+ @data.incrby key, amount
77
+ end
78
+
79
+ # Decrement a key in the store
80
+ #
81
+ # If the key doesn't exist it will be initialized on 0.
82
+ # If the key exist but it isn't a Fixnum it will be initialized on 0.
83
+ #
84
+ # Example:
85
+ # We have two objects in cache:
86
+ # counter # => 23
87
+ # rabbit # => #<Rabbit:0x5eee6c>
88
+ #
89
+ # cache.decrement "counter"
90
+ # cache.read "counter", :raw => true # => "22"
91
+ #
92
+ # cache.decrement "counter", 2
93
+ # cache.read "counter", :raw => true # => "20"
94
+ #
95
+ # cache.decrement "a counter"
96
+ # cache.read "a counter", :raw => true # => "-1"
97
+ #
98
+ # cache.decrement "rabbit"
99
+ # cache.read "rabbit", :raw => true # => "-1"
100
+ def decrement(key, amount = 1)
101
+ @data.decrby key, amount
102
+ end
103
+
104
+ # Delete objects for matched keys.
105
+ #
106
+ # Example:
107
+ # cache.del_matched "rab*"
108
+ def delete_matched(matcher, options = nil)
109
+ @data.keys(matcher).each { |key| @data.del key }
110
+ end
111
+
112
+ def fetch(key, options = {})
113
+ (!options[:force] && data = read(key, options)) || (write key, yield, options if block_given?)
114
+ end
115
+
116
+ # Clear all the data from the store.
117
+ def clear
118
+ @data.flushdb
119
+ end
120
+
121
+ def stats
122
+ @data.info
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,67 @@
1
+ module I18n
2
+ module Backend
3
+ class Redis
4
+ include Base, Flatten
5
+ attr_accessor :store
6
+
7
+ # Instantiate the store.
8
+ #
9
+ # Example:
10
+ # RedisStore.new
11
+ # # => host: localhost, port: 6379, db: 0
12
+ #
13
+ # RedisStore.new "example.com"
14
+ # # => host: example.com, port: 6379, db: 0
15
+ #
16
+ # RedisStore.new "example.com:23682"
17
+ # # => host: example.com, port: 23682, db: 0
18
+ #
19
+ # RedisStore.new "example.com:23682/1"
20
+ # # => host: example.com, port: 23682, db: 1
21
+ #
22
+ # RedisStore.new "example.com:23682/1/theplaylist"
23
+ # # => host: example.com, port: 23682, db: 1, namespace: theplaylist
24
+ #
25
+ # RedisStore.new "localhost:6379/0", "localhost:6380/0"
26
+ # # => instantiate a cluster
27
+ def initialize(*addresses)
28
+ @store = ::Redis::Factory.create(addresses)
29
+ end
30
+
31
+ def translate(locale, key, options = {})
32
+ options[:resolve] ||= false
33
+ super locale, key, options
34
+ end
35
+
36
+ def store_translations(locale, data, options = {})
37
+ escape = options.fetch(:escape, true)
38
+ flatten_translations(locale, data, escape, false).each do |key, value|
39
+ case value
40
+ when Proc
41
+ raise "Key-value stores cannot handle procs"
42
+ else
43
+ @store.set "#{locale}.#{key}", value
44
+ end
45
+ end
46
+ end
47
+
48
+ def available_locales
49
+ locales = @store.keys.map { |k| k =~ /\./; $` }
50
+ locales.uniq!
51
+ locales.compact!
52
+ locales.map! { |k| k.to_sym }
53
+ locales
54
+ end
55
+
56
+ protected
57
+ def lookup(locale, key, scope = [], options = {})
58
+ key = normalize_flat_keys(locale, key, scope, options[:separator])
59
+ @store.get "#{locale}.#{key}"
60
+ end
61
+
62
+ def resolve_link(locale, key)
63
+ key
64
+ end
65
+ end
66
+ end
67
+ end
@@ -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.exists 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.del 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 Redis::Store 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 = ::Redis::Factory.create 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.del(hexdigest(key))
35
+ nil
36
+ end
37
+ end
38
+
39
+ REDIS = Redis
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ module Merb
2
+ # HACK for cyclic dependency: redis-store is required before Merb session stores
3
+ class Mash < Hash; end
4
+ class SessionContainer < Mash; class_inheritable_accessor :session_store_type end
5
+ class SessionStoreContainer < SessionContainer; end
6
+
7
+ class RedisSession < SessionStoreContainer
8
+ self.session_store_type = :redis
9
+ end
10
+
11
+ module RedisStore
12
+ def retrieve_session(session_id)
13
+ get("session:#{session_id}")
14
+ end
15
+
16
+ def store_session(session_id, data)
17
+ set("session:#{session_id}", data)
18
+ end
19
+
20
+ def delete_session(session_id)
21
+ delete("session:#{session_id}")
22
+ end
23
+ end
24
+ end
25
+
26
+ module Rack
27
+ module Session
28
+ class Redis
29
+ include Merb::RedisStore
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,81 @@
1
+ module Rack
2
+ module Session
3
+ class Redis < Abstract::ID
4
+ attr_reader :mutex, :pool
5
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :redis_server => "redis://127.0.0.1:6379/0"
6
+
7
+ def initialize(app, options = {})
8
+ super
9
+ @mutex = Mutex.new
10
+ @pool = ::Redis::Factory.create options[:redis_server] || @default_options[:redis_server]
11
+ end
12
+
13
+ def generate_sid
14
+ loop do
15
+ sid = super
16
+ break sid unless @pool.get(sid)
17
+ end
18
+ end
19
+
20
+ def get_session(env, sid)
21
+ session = @pool.get(sid) if sid
22
+ @mutex.lock if env['rack.multithread']
23
+ unless sid and session
24
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
25
+ session = {}
26
+ sid = generate_sid
27
+ ret = @pool.set sid, session
28
+ raise "Session collision on '#{sid.inspect}'" unless ret
29
+ end
30
+ session.instance_variable_set('@old', {}.merge(session))
31
+ return [sid, session]
32
+ rescue Errno::ECONNREFUSED
33
+ warn "#{self} is unable to find server."
34
+ warn $!.inspect
35
+ return [ nil, {} ]
36
+ ensure
37
+ @mutex.unlock if env['rack.multithread']
38
+ end
39
+
40
+ def set_session(env, session_id, new_session, options)
41
+ @mutex.lock if env['rack.multithread']
42
+ session = @pool.get(session_id) rescue {}
43
+ if options[:renew] or options[:drop]
44
+ @pool.del session_id
45
+ return false if options[:drop]
46
+ session_id = generate_sid
47
+ @pool.set session_id, 0
48
+ end
49
+ old_session = new_session.instance_variable_get('@old') || {}
50
+ session = merge_sessions session_id, old_session, new_session, session
51
+ @pool.set session_id, session, options
52
+ return session_id
53
+ rescue Errno::ECONNREFUSED
54
+ warn "#{self} is unable to find server."
55
+ warn $!.inspect
56
+ return false
57
+ ensure
58
+ @mutex.unlock if env['rack.multithread']
59
+ end
60
+
61
+ private
62
+ def merge_sessions(sid, old, new, cur=nil)
63
+ cur ||= {}
64
+ unless Hash === old and Hash === new
65
+ warn 'Bad old or new sessions provided.'
66
+ return cur
67
+ end
68
+
69
+ delete = old.keys - new.keys
70
+ warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
71
+ delete.each{|k| cur.delete k }
72
+
73
+ update = new.keys.select{|k| new[k] != old[k] }
74
+ warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
75
+ update.each{|k| cur[k] = new[k] }
76
+
77
+ cur
78
+ end
79
+ end
80
+ end
81
+ end