nono-redis-store 1.0.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.
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