redis-store-pika 1.9.2.1
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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +6 -0
- data/.github/auto-assign-issues.yml +2 -0
- data/.github/workflows/ci.yml +64 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +132 -0
- data/Appraisals +19 -0
- data/CHANGELOG.md +667 -0
- data/CODEOWNERS +1 -0
- data/Gemfile +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +68 -0
- data/Rakefile +15 -0
- data/gemfiles/redis_4_0_x.gemfile +7 -0
- data/gemfiles/redis_4_1_x.gemfile +7 -0
- data/gemfiles/redis_4_6_x.gemfile +7 -0
- data/gemfiles/redis_4_x.gemfile +7 -0
- data/gemfiles/redis_5_x.gemfile +7 -0
- data/lib/redis/distributed_store.rb +68 -0
- data/lib/redis/store/factory.rb +111 -0
- data/lib/redis/store/interface.rb +29 -0
- data/lib/redis/store/namespace.rb +211 -0
- data/lib/redis/store/redis_version.rb +13 -0
- data/lib/redis/store/serialization.rb +67 -0
- data/lib/redis/store/ttl.rb +47 -0
- data/lib/redis/store/version.rb +13 -0
- data/lib/redis/store.rb +85 -0
- data/lib/redis-store-pika.rb +1 -0
- data/lib/redis-store.rb +1 -0
- data/redis-store.gemspec +35 -0
- data/test/redis/distributed_store_test.rb +111 -0
- data/test/redis/store/factory_test.rb +273 -0
- data/test/redis/store/interface_test.rb +27 -0
- data/test/redis/store/namespace_test.rb +316 -0
- data/test/redis/store/redis_version_test.rb +28 -0
- data/test/redis/store/serialization_test.rb +173 -0
- data/test/redis/store/ttl_test.rb +142 -0
- data/test/redis/store/version_test.rb +7 -0
- data/test/redis/store_test.rb +68 -0
- data/test/test_helper.rb +20 -0
- metadata +246 -0
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# This is a fork!
|
2
|
+
|
3
|
+
This is a fork of official redis-store gem published to let people use fixes unavailable on official one yet.
|
4
|
+
Be sure to check updates from official [`redis-store`](https://github.com/redis-store/redis-store) gem sometimes in case the fixes included in this fork are already merged there.
|
5
|
+
(You can subscribe specific issues/PRs/releases on GitHub)
|
6
|
+
|
7
|
+
To install this fork place `redis-store-pika` into Gemfile (no special `require` option needed, I think).
|
8
|
+
Be sure not to install/require `redis-store` at the same time (maybe comment that line out in `Gemfile`)
|
9
|
+
|
10
|
+
## Versioning
|
11
|
+
Since this is a fork (as a temporary measure) semantic versioning won't be used.
|
12
|
+
It would be like `(last offical version)` + `.x` (`x` = how many times it's released)
|
13
|
+
(e.g. Based on `1.9.2` + 1st release = `1.9.2.1`)
|
14
|
+
|
15
|
+
# Redis stores for Ruby frameworks
|
16
|
+
|
17
|
+
__Redis Store__ provides a full set of stores (*Cache*, *I18n*, *Session*, *HTTP Cache*) for modern Ruby frameworks like: __Ruby on Rails__, __Sinatra__, __Rack__, __Rack::Cache__ and __I18n__. It supports object marshalling, timeouts, single or multiple nodes, and namespaces.
|
18
|
+
|
19
|
+
Please check the *README* file of each gem for usage and installation guidelines.
|
20
|
+
|
21
|
+
## Redis Installation
|
22
|
+
|
23
|
+
### Option 1: Homebrew
|
24
|
+
|
25
|
+
MacOS X users should use [Homebrew](https://github.com/mxcl/homebrew) to install Redis:
|
26
|
+
|
27
|
+
```shell
|
28
|
+
brew install redis
|
29
|
+
```
|
30
|
+
|
31
|
+
### Option 2: From Source
|
32
|
+
|
33
|
+
Download and install Redis from [the download page](http://redis.io//download) and follow the instructions.
|
34
|
+
|
35
|
+
## Running tests
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
git clone git://github.com/redis-store/redis-store.git
|
39
|
+
cd redis-store
|
40
|
+
bundle install
|
41
|
+
bundle exec rake
|
42
|
+
```
|
43
|
+
|
44
|
+
If you are on **Snow Leopard** you have to run `env ARCHFLAGS="-arch x86_64" ruby ci/run.rb`
|
45
|
+
|
46
|
+
## Contributors
|
47
|
+
|
48
|
+
https://github.com/redis-store/redis-store/graphs/contributors
|
49
|
+
|
50
|
+
## Versioning
|
51
|
+
|
52
|
+
The **redis-store** family of gems uses [Semantic Versioning](http://semver.org), meaning gems depending on **redis-store**
|
53
|
+
can be reliably inclusive of any version between the current and the next major. We recommend the following dependency
|
54
|
+
in your library's gemspec:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
s.add_dependency 'redis-store', '>= 1.4', '< 2'
|
58
|
+
```
|
59
|
+
|
60
|
+
## Status
|
61
|
+
|
62
|
+
[](http://badge.fury.io/rb/redis-store)
|
63
|
+
[](http://travis-ci.org/redis-store/redis-store?branch=master)
|
64
|
+
[](https://codeclimate.com/github/redis-store/redis-store)
|
65
|
+
|
66
|
+
## Copyright
|
67
|
+
|
68
|
+
2009 - 2013 Luca Guidi - [http://lucaguidi.com](http://lucaguidi.com), released under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'rake'
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'appraisal'
|
5
|
+
require 'rubocop/rake_task'
|
6
|
+
|
7
|
+
RuboCop::RakeTask.new :lint
|
8
|
+
|
9
|
+
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["CI"]
|
10
|
+
task :default do
|
11
|
+
sh "appraisal install && rake appraisal default"
|
12
|
+
end
|
13
|
+
else
|
14
|
+
require 'redis-store/testing/tasks'
|
15
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'redis/distributed'
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
class DistributedStore < Distributed
|
5
|
+
@@timeout = 5
|
6
|
+
attr_reader :ring
|
7
|
+
|
8
|
+
def initialize(addresses, options = {})
|
9
|
+
_extend_namespace options
|
10
|
+
# `@tag` introduced in `redis-rb` 5.0
|
11
|
+
@tag = options[:tag] || /^\{(.+?)\}/
|
12
|
+
@ring = options[:ring] || Redis::HashRing.new([], options[:replicas] || Redis::HashRing::POINTS_PER_SERVER)
|
13
|
+
|
14
|
+
addresses.each do |address|
|
15
|
+
@ring.add_node(::Redis::Store.new _merge_options(address, options))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def nodes
|
20
|
+
ring.nodes
|
21
|
+
end
|
22
|
+
|
23
|
+
def reconnect
|
24
|
+
nodes.each { |node| node.reconnect }
|
25
|
+
end
|
26
|
+
|
27
|
+
def set(key, value, options = nil)
|
28
|
+
node_for(key).set(key, value, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def get(key, options = nil)
|
32
|
+
node_for(key).get(key, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def setnx(key, value, options = nil)
|
36
|
+
node_for(key).setnx(key, value, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def redis_version
|
40
|
+
nodes.first.redis_version unless nodes.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def supports_redis_version?(version)
|
44
|
+
if nodes.empty?
|
45
|
+
false
|
46
|
+
else
|
47
|
+
nodes.first.supports_redis_version?(version)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def setex(key, expiry, value, options = nil)
|
52
|
+
node_for(key).setex(key, expiry, value, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def _extend_namespace(options)
|
57
|
+
@namespace = options[:namespace]
|
58
|
+
extend ::Redis::Store::Namespace if @namespace
|
59
|
+
end
|
60
|
+
|
61
|
+
def _merge_options(address, options)
|
62
|
+
address.merge(
|
63
|
+
:timeout => options[:timeout] || @@timeout,
|
64
|
+
:namespace => options[:namespace]
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
class Redis
|
5
|
+
class Store < self
|
6
|
+
class Factory
|
7
|
+
DEFAULT_PORT = 6379
|
8
|
+
|
9
|
+
def self.create(*options)
|
10
|
+
new(options).create
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(*options)
|
14
|
+
@addresses = []
|
15
|
+
@options = {}
|
16
|
+
extract_addresses_and_options(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create
|
20
|
+
if @addresses.empty?
|
21
|
+
@addresses << {}
|
22
|
+
end
|
23
|
+
|
24
|
+
if @addresses.size > 1
|
25
|
+
::Redis::DistributedStore.new @addresses, @options
|
26
|
+
else
|
27
|
+
::Redis::Store.new @addresses.first.merge(@options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.resolve(uri) #:api: private
|
32
|
+
if uri.is_a?(Hash)
|
33
|
+
extract_host_options_from_hash(uri)
|
34
|
+
else
|
35
|
+
extract_host_options_from_uri(uri)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.extract_host_options_from_hash(options)
|
40
|
+
options = normalize_key_names(options)
|
41
|
+
if host_options?(options)
|
42
|
+
options
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.normalize_key_names(options)
|
49
|
+
options = options.dup
|
50
|
+
if options.key?(:key_prefix) && !options.key?(:namespace)
|
51
|
+
options[:namespace] = options.delete(:key_prefix) # RailsSessionStore
|
52
|
+
end
|
53
|
+
options[:raw] = case
|
54
|
+
when options.key?(:serializer)
|
55
|
+
options[:serializer].nil?
|
56
|
+
when options.key?(:marshalling)
|
57
|
+
!options[:marshalling]
|
58
|
+
else
|
59
|
+
false
|
60
|
+
end
|
61
|
+
options
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.host_options?(options)
|
65
|
+
options.keys.any? { |n| [:host, :db, :port, :path].include?(n) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.extract_host_options_from_uri(uri)
|
69
|
+
uri = URI.parse(uri)
|
70
|
+
if uri.scheme == "unix"
|
71
|
+
options = { :path => uri.path }
|
72
|
+
else
|
73
|
+
_, db, namespace = if uri.path
|
74
|
+
uri.path.split(/\//)
|
75
|
+
end
|
76
|
+
|
77
|
+
options = {
|
78
|
+
:scheme => uri.scheme,
|
79
|
+
:host => uri.hostname,
|
80
|
+
:port => uri.port || DEFAULT_PORT,
|
81
|
+
:password => uri.password.nil? ? nil : CGI.unescape(uri.password.to_s)
|
82
|
+
}
|
83
|
+
|
84
|
+
options[:db] = db.to_i if db
|
85
|
+
options[:namespace] = namespace if namespace
|
86
|
+
end
|
87
|
+
if uri.query
|
88
|
+
query = Hash[URI.decode_www_form(uri.query)]
|
89
|
+
query.each do |(key, value)|
|
90
|
+
options[key.to_sym] = value
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
options
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def extract_addresses_and_options(*options)
|
100
|
+
options.flatten.compact.each do |token|
|
101
|
+
resolved = self.class.resolve(token)
|
102
|
+
if resolved
|
103
|
+
@addresses << resolved
|
104
|
+
else
|
105
|
+
@options.merge!(self.class.normalize_key_names(token))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Redis
|
2
|
+
class Store < self
|
3
|
+
module Interface
|
4
|
+
def get(key, options = nil)
|
5
|
+
super(key)
|
6
|
+
end
|
7
|
+
|
8
|
+
REDIS_SET_OPTIONS = %i(ex px nx xx keepttl).freeze
|
9
|
+
private_constant :REDIS_SET_OPTIONS
|
10
|
+
|
11
|
+
def set(key, value, options = nil)
|
12
|
+
if options && REDIS_SET_OPTIONS.any? { |k| options.key?(k) }
|
13
|
+
kwargs = REDIS_SET_OPTIONS.each_with_object({}) { |key, h| h[key] = options[key] if options.key?(key) }
|
14
|
+
super(key, value, **kwargs)
|
15
|
+
else
|
16
|
+
super(key, value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def setnx(key, value, options = nil)
|
21
|
+
super(key, value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def setex(key, expiry, value, options = nil)
|
25
|
+
super(key, expiry, value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
class Redis
|
2
|
+
class Store < self
|
3
|
+
module Namespace
|
4
|
+
FLUSHDB_BATCH_SIZE = 1000
|
5
|
+
|
6
|
+
def set(key, *args)
|
7
|
+
namespace(key) { |k| super(k, *args) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def setex(key, *args)
|
11
|
+
namespace(key) { |k| super(k, *args) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def setnx(key, *args)
|
15
|
+
namespace(key) { |k| super(k, *args) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def ttl(key, options = nil)
|
19
|
+
namespace(key) { |k| super(k) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def get(key, *args)
|
23
|
+
namespace(key) { |k| super(k, *args) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def exists(*keys)
|
27
|
+
super(*keys.map { |key| interpolate(key) })
|
28
|
+
end
|
29
|
+
|
30
|
+
def exists?(*keys)
|
31
|
+
super(*keys.map { |key| interpolate(key) })
|
32
|
+
end
|
33
|
+
|
34
|
+
def incrby(key, increment)
|
35
|
+
namespace(key) { |k| super(k, increment) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def decrby(key, increment)
|
39
|
+
namespace(key) { |k| super(k, increment) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def keys(pattern = "*")
|
43
|
+
namespace(pattern) { |p| super(p).map { |key| strip_namespace(key) } }
|
44
|
+
end
|
45
|
+
|
46
|
+
def scan(cursor, match: nil, **kwargs)
|
47
|
+
if match
|
48
|
+
namespace(match) do |pattern|
|
49
|
+
cursor, keys = super(cursor, match: pattern, **kwargs)
|
50
|
+
[ cursor, keys.map { |key| strip_namespace(key) } ]
|
51
|
+
end
|
52
|
+
else
|
53
|
+
super(cursor, **kwargs)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def del(*keys)
|
58
|
+
super(*keys.map { |key| interpolate(key) }) if keys.any?
|
59
|
+
end
|
60
|
+
|
61
|
+
def unlink(*keys)
|
62
|
+
super(*keys.map { |key| interpolate(key) }) if keys.any?
|
63
|
+
end
|
64
|
+
|
65
|
+
def watch(*keys)
|
66
|
+
super(*keys.map { |key| interpolate(key) }) if keys.any?
|
67
|
+
end
|
68
|
+
|
69
|
+
def mget(*keys, &blk)
|
70
|
+
options = (keys.pop if keys.last.is_a? Hash) || {}
|
71
|
+
if keys.any?
|
72
|
+
# Serialization gets extended before Namespace does, so we need to pass options further
|
73
|
+
if singleton_class.ancestors.include? Serialization
|
74
|
+
super(*keys.map { |key| interpolate(key) }, options, &blk)
|
75
|
+
else
|
76
|
+
super(*keys.map { |key| interpolate(key) }, &blk)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def expire(key, ttl)
|
82
|
+
namespace(key) { |k| super(k, ttl) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def hdel(key, *fields)
|
86
|
+
namespace(key) { |k| super(k, *fields) }
|
87
|
+
end
|
88
|
+
|
89
|
+
def hget(key, field)
|
90
|
+
namespace(key) { |k| super(k, field) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def hgetall(key)
|
94
|
+
namespace(key) { |k| super(k) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def hexists(key, field)
|
98
|
+
namespace(key) { |k| super(k, field) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def hincrby(key, field, increment)
|
102
|
+
namespace(key) { |k| super(k, field, increment) }
|
103
|
+
end
|
104
|
+
|
105
|
+
def hincrbyfloat(key, field, increment)
|
106
|
+
namespace(key) { |k| super(k, field, increment) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def hkeys(key)
|
110
|
+
namespace(key) { |k| super(k) }
|
111
|
+
end
|
112
|
+
|
113
|
+
def hlen(key)
|
114
|
+
namespace(key) { |k| super(k) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def hmget(key, *fields, &blk)
|
118
|
+
namespace(key) { |k| super(k, *fields, &blk) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def hmset(key, *attrs)
|
122
|
+
namespace(key) { |k| super(k, *attrs) }
|
123
|
+
end
|
124
|
+
|
125
|
+
def hset(key, *args)
|
126
|
+
namespace(key) { |k| super(k, *args) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def hsetnx(key, field, val)
|
130
|
+
namespace(key) { |k| super(k, field, val) }
|
131
|
+
end
|
132
|
+
|
133
|
+
def hvals(key)
|
134
|
+
namespace(key) { |k| super(k) }
|
135
|
+
end
|
136
|
+
|
137
|
+
def hscan(key, *args)
|
138
|
+
namespace(key) { |k| super(k, *args) }
|
139
|
+
end
|
140
|
+
|
141
|
+
def hscan_each(key, *args)
|
142
|
+
namespace(key) { |k| super(k, *args) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def zincrby(key, increment, member)
|
146
|
+
namespace(key) { |k| super(k, increment, member) }
|
147
|
+
end
|
148
|
+
|
149
|
+
def zscore(key, member)
|
150
|
+
namespace(key) { |k| super(k, member) }
|
151
|
+
end
|
152
|
+
|
153
|
+
def zadd(key, *args)
|
154
|
+
namespace(key) { |k| super(k, *args) }
|
155
|
+
end
|
156
|
+
|
157
|
+
def zrem(key, member)
|
158
|
+
namespace(key) { |k| super(k, member) }
|
159
|
+
end
|
160
|
+
|
161
|
+
if respond_to?(:ruby2_keywords, true)
|
162
|
+
ruby2_keywords :set, :setex, :setnx, :hscan, :hscan_each
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_s
|
166
|
+
if namespace_str
|
167
|
+
"#{super} with namespace #{namespace_str}"
|
168
|
+
else
|
169
|
+
super
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def flushdb
|
174
|
+
return super unless namespace_str
|
175
|
+
keys.each_slice(FLUSHDB_BATCH_SIZE) { |key_slice| del(*key_slice) }
|
176
|
+
end
|
177
|
+
|
178
|
+
def with_namespace(ns)
|
179
|
+
old_ns = @namespace
|
180
|
+
@namespace = ns
|
181
|
+
yield self
|
182
|
+
ensure
|
183
|
+
@namespace = old_ns
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
def namespace(key)
|
188
|
+
yield interpolate(key)
|
189
|
+
end
|
190
|
+
|
191
|
+
def namespace_str
|
192
|
+
@namespace.is_a?(Proc) ? @namespace.call : @namespace
|
193
|
+
end
|
194
|
+
|
195
|
+
def interpolate(key)
|
196
|
+
return key unless namespace_str
|
197
|
+
key.match(namespace_regexp) ? key : "#{namespace_str}:#{key}"
|
198
|
+
end
|
199
|
+
|
200
|
+
def strip_namespace(key)
|
201
|
+
return key unless namespace_str
|
202
|
+
key.gsub namespace_regexp, ""
|
203
|
+
end
|
204
|
+
|
205
|
+
def namespace_regexp
|
206
|
+
@namespace_regexps ||= {}
|
207
|
+
@namespace_regexps[namespace_str] ||= %r{^#{namespace_str}\:}
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Redis
|
2
|
+
class Store < self
|
3
|
+
module RedisVersion
|
4
|
+
def redis_version
|
5
|
+
info('server')['redis_version']
|
6
|
+
end
|
7
|
+
|
8
|
+
def supports_redis_version?(version)
|
9
|
+
(redis_version.split(".").map(&:to_i) <=> version.split(".").map(&:to_i)) >= 0
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Redis
|
2
|
+
class Store < self
|
3
|
+
module Serialization
|
4
|
+
def set(key, value, options = nil)
|
5
|
+
_marshal(value, options) { |v| super encode(key), encode(v), options }
|
6
|
+
end
|
7
|
+
|
8
|
+
def setnx(key, value, options = nil)
|
9
|
+
_marshal(value, options) { |v| super encode(key), encode(v), options }
|
10
|
+
end
|
11
|
+
|
12
|
+
def setex(key, expiry, value, options = nil)
|
13
|
+
_marshal(value, options) { |v| super encode(key), expiry, encode(v), options }
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(key, options = nil)
|
17
|
+
_unmarshal super(key), options
|
18
|
+
end
|
19
|
+
|
20
|
+
def mget(*keys, &blk)
|
21
|
+
options = keys.pop if keys.last.is_a?(Hash)
|
22
|
+
super(*keys) do |reply|
|
23
|
+
reply.map! { |value| _unmarshal value, options }
|
24
|
+
blk ? blk.call(reply) : reply
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def mset(*args)
|
29
|
+
options = args.pop if args.length.odd?
|
30
|
+
updates = []
|
31
|
+
args.each_slice(2) do |(key, value)|
|
32
|
+
updates << encode(key)
|
33
|
+
_marshal(value, options) { |v| updates << encode(v) }
|
34
|
+
end
|
35
|
+
super(*updates)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def _marshal(val, options)
|
40
|
+
yield marshal?(options) ? @serializer.dump(val) : val
|
41
|
+
end
|
42
|
+
|
43
|
+
def _unmarshal(val, options)
|
44
|
+
unmarshal?(val, options) ? @serializer.load(val) : val
|
45
|
+
end
|
46
|
+
|
47
|
+
def marshal?(options)
|
48
|
+
!(options && options[:raw])
|
49
|
+
end
|
50
|
+
|
51
|
+
def unmarshal?(result, options)
|
52
|
+
result && result.size > 0 && marshal?(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
if defined?(Encoding)
|
56
|
+
def encode(string)
|
57
|
+
key = string.to_s.dup
|
58
|
+
key.force_encoding(Encoding::BINARY)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
def encode(string)
|
62
|
+
string
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Redis
|
2
|
+
class Store < self
|
3
|
+
module Ttl
|
4
|
+
def set(key, value, options = nil)
|
5
|
+
if ttl = expires_in(options)
|
6
|
+
setex(key, ttl.to_i, value, :raw => true)
|
7
|
+
else
|
8
|
+
super(key, value, options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def setnx(key, value, options = nil)
|
13
|
+
if ttl = expires_in(options)
|
14
|
+
setnx_with_expire(key, value, ttl.to_i, options)
|
15
|
+
else
|
16
|
+
super(key, value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
def setnx_with_expire(key, value, ttl, options = {})
|
22
|
+
with_multi_or_pipelined(options) do |transaction|
|
23
|
+
if transaction.is_a?(Redis::Store) # for redis < 4.6
|
24
|
+
setnx(key, value, :raw => true)
|
25
|
+
expire(key, ttl)
|
26
|
+
else
|
27
|
+
transaction.setnx(key, value)
|
28
|
+
transaction.expire(key, ttl)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def expires_in(options)
|
35
|
+
if options
|
36
|
+
# Rack::Session Merb Rails/Sinatra
|
37
|
+
options[:expire_after] || options[:expires_in] || options[:expire_in]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def with_multi_or_pipelined(options, &block)
|
42
|
+
return pipelined(&block) if options.key?(:cluster) || options[:avoid_multi_commands]
|
43
|
+
multi(&block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Redis
|
2
|
+
class Store < self
|
3
|
+
# Official version + `.x`
|
4
|
+
VERSION = '1.9.2.1'
|
5
|
+
|
6
|
+
def self.redis_client_defined?
|
7
|
+
# Doesn't work if declared as constant
|
8
|
+
# due to unpredictable gem loading order
|
9
|
+
# https://github.com/redis-rb/redis-client
|
10
|
+
defined?(::RedisClient::VERSION)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|