redis-store 1.4.1 → 1.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 343594d8b51298be4a1d5c015a64d04fe1939c12
4
- data.tar.gz: 3ee360fae5b291f77f56175cc34e18e635af655d
2
+ SHA256:
3
+ metadata.gz: 10e6007fb1a0708fe88d7627a23a2b5e8e0303e9ee81ec886d27a5c6fdb7ed07
4
+ data.tar.gz: 9415f933dae0284fe8114cf6d350561bc23c58bcf9f1182d734f36fdf8436707
5
5
  SHA512:
6
- metadata.gz: '0906711b266addefc485ddad054c70eb1d25ccfdc8162c9c9225229d1e26be1c66361ef1704a86e8e7e17419ef961c6aec3c85156275794f956921964b599c2e'
7
- data.tar.gz: 4aa88e8de4f00b29e20dd4f93bbaf4f21a18850e6cedebfb93fcbb3b98190b2cc2fd327f4aceb8dc24c66896f66095a24706b94fec31eff078ecd6440e2a9a7c
6
+ metadata.gz: 69a4d72561bf0b9da2c88cc7a32d37a7ca06bb758a60d338bc4c12added10c1549f818f6e0e68af3854e0911a92f591c3c3094455d731f7a48d1cb1b47a9a700
7
+ data.tar.gz: da5fee4e7926a6032ca82fa7ee924c0a676124bffb55c84d5f553f066eb0ca47678cad583403ccc5979d04b7ddcdeb98652edf906472db364713baf483bc64e3
data/.gitignore CHANGED
@@ -2,3 +2,5 @@ Gemfile.lock
2
2
  *.gem
3
3
  tmp/
4
4
  stdout
5
+ # Gemfile locks generated by Appraisals
6
+ gemfiles/*.gemfile.lock
@@ -1,19 +1,35 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  cache: bundler
4
+ notifications:
5
+ webhooks: https://www.travisbuddy.com
6
+ on_success: never
7
+
8
+ # 1.9.3 has Bundler 1.7.6 with the "undefined method `spec' for nil" bug
4
9
  before_install: gem install bundler
5
- script: 'bundle exec rake'
10
+
6
11
  rvm:
7
12
  - 1.9.3
8
- - 2.0.0
13
+ - 2.0
9
14
  - 2.1
10
15
  - 2.2
11
- - 2.3.0
16
+ - 2.3
17
+ - 2.4
12
18
  - ruby-head
13
19
  - jruby-head
14
- bundler_args: '--path vendor/bundle'
20
+
21
+ gemfile:
22
+ - gemfiles/redis_3_x.gemfile
23
+ - gemfiles/redis_4_x.gemfile
15
24
 
16
25
  matrix:
17
26
  allow_failures:
18
27
  - rvm: jruby-head
19
28
  - rvm: ruby-head
29
+ exclude:
30
+ - rvm: 1.9.3
31
+ gemfile: gemfiles/redis_4_x.gemfile
32
+ - rvm: 2.0
33
+ gemfile: gemfiles/redis_4_x.gemfile
34
+ - rvm: 2.1
35
+ gemfile: gemfiles/redis_4_x.gemfile
@@ -0,0 +1,8 @@
1
+
2
+ appraise "redis_3_x" do
3
+ gem "redis", "~> 3.0"
4
+ end
5
+
6
+ appraise "redis_4_x" do
7
+ gem "redis", "~> 4.0"
8
+ end
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.0
4
+
5
+ Breaking Changes
6
+
7
+ * None
8
+
9
+ Added
10
+
11
+ * [Support for Redis v4 and above](https://github.com/redis-store/redis-store/pull/292)
12
+ * [Support for distributed mget/read_multi in Redis v4](https://github.com/redis-store/redis-store/pull/282)
13
+ * [Tests for Namespace#flushdb](https://github.com/redis-store/redis-store/pull/299)
14
+ * [Support for UNIX Socket path connections](https://github.com/redis-store/redis-store/pull/298)
15
+ * [HashRing configuration for DistributedStore](https://github.com/redis-store/redis-store/pull/304)
16
+
17
+ Fixed
18
+
19
+ * [Inefficient use of KEYS when calling FLUSHDB without a namespace](https://github.com/redis-store/redis-store/pull/297)
20
+
3
21
  ## 1.4.1
4
22
 
5
23
  Breaking Changes
data/Gemfile CHANGED
@@ -1,2 +1,7 @@
1
1
  source 'https://rubygems.org'
2
+
3
+ if RUBY_VERSION < '2'
4
+ gem 'redis', '< 4'
5
+ end
6
+
2
7
  gemspec
data/Rakefile CHANGED
@@ -2,3 +2,11 @@ require 'bundler/setup'
2
2
  require 'rake'
3
3
  require 'bundler/gem_tasks'
4
4
  require 'redis-store/testing/tasks'
5
+ require 'appraisal'
6
+
7
+
8
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
9
+ task :default do
10
+ sh "appraisal install && rake appraisal default"
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "redis", "~> 3.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "redis", "~> 4.0"
6
+
7
+ gemspec path: "../"
@@ -6,12 +6,12 @@ class Redis
6
6
  attr_reader :ring
7
7
 
8
8
  def initialize(addresses, options = { })
9
- nodes = addresses.map do |address|
10
- ::Redis::Store.new _merge_options(address, options)
11
- end
12
-
13
9
  _extend_namespace options
14
- @ring = Redis::HashRing.new nodes
10
+ @ring = options[:ring] || Redis::HashRing.new([], options[:replicas] || Redis::HashRing::POINTS_PER_SERVER)
11
+
12
+ addresses.each do |address|
13
+ @ring.add_node(::Redis::Store.new _merge_options(address, options))
14
+ end
15
15
  end
16
16
 
17
17
  def nodes
@@ -42,8 +42,17 @@ class Redis
42
42
  end
43
43
 
44
44
  def to_s
45
- h = @client.host
46
- "Redis Client connected to #{/:/ =~ h ? '['+h+']' : h}:#{@client.port} against DB #{@client.db}"
45
+ "Redis Client connected to #{location} against DB #{@client.db}"
46
+ end
47
+
48
+ def location
49
+ if @client.path
50
+ @client.path
51
+ else
52
+ h = @client.host
53
+ h = "[#{h}]" if h.include?(":")
54
+ "#{h}:#{@client.port}"
55
+ end
47
56
  end
48
57
 
49
58
  private
@@ -62,27 +62,33 @@ class Redis
62
62
  end
63
63
 
64
64
  def self.host_options?(options)
65
- if options.keys.any? {|n| [:host, :db, :port].include?(n) }
66
- options
67
- else
68
- nil # just to be clear
69
- end
65
+ options.keys.any? {|n| [:host, :db, :port, :path].include?(n) }
70
66
  end
71
67
 
72
68
  def self.extract_host_options_from_uri(uri)
73
69
  uri = URI.parse(uri)
74
- _, db, namespace = if uri.path
75
- uri.path.split(/\//)
76
- end
77
-
78
- options = {
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
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
+ :host => uri.hostname,
79
+ :port => uri.port || DEFAULT_PORT,
80
+ :password => uri.password.nil? ? nil : CGI::unescape(uri.password.to_s)
81
+ }
82
+
83
+ options[:db] = db.to_i if db
84
+ options[:namespace] = namespace if namespace
85
+ end
86
+ if uri.query
87
+ query = Hash[URI.decode_www_form(uri.query)]
88
+ query.each do |(key, value)|
89
+ options[key.to_sym] = value
90
+ end
91
+ end
86
92
 
87
93
  options
88
94
  end
@@ -47,14 +47,14 @@ class Redis
47
47
  super(*keys.map {|key| interpolate(key) }) if keys.any?
48
48
  end
49
49
 
50
- def mget(*keys)
50
+ def mget(*keys, &blk)
51
51
  options = (keys.pop if keys.last.is_a? Hash) || {}
52
52
  if keys.any?
53
53
  # Serialization gets extended before Namespace does, so we need to pass options further
54
54
  if singleton_class.ancestors.include? Serialization
55
- super(*keys.map {|key| interpolate(key) }, options)
55
+ super(*keys.map {|key| interpolate(key) }, options, &blk)
56
56
  else
57
- super(*keys.map {|key| interpolate(key) })
57
+ super(*keys.map {|key| interpolate(key) }, &blk)
58
58
  end
59
59
  end
60
60
  end
@@ -72,6 +72,7 @@ class Redis
72
72
  end
73
73
 
74
74
  def flushdb
75
+ return super unless namespace_str
75
76
  keys.each_slice(FLUSHDB_BATCH_SIZE) { |key_slice| del(*key_slice) }
76
77
  end
77
78
 
@@ -17,10 +17,11 @@ class Redis
17
17
  _unmarshal super(key), options
18
18
  end
19
19
 
20
- def mget(*keys)
20
+ def mget(*keys, &blk)
21
21
  options = keys.pop if keys.last.is_a?(Hash)
22
- super(*keys).map do |result|
23
- _unmarshal result, options
22
+ super(*keys) do |reply|
23
+ reply.map! { |value| _unmarshal value, options }
24
+ blk ? blk.call(reply) : reply
24
25
  end
25
26
  end
26
27
 
@@ -1,5 +1,5 @@
1
1
  class Redis
2
2
  class Store < self
3
- VERSION = '1.4.1'
3
+ VERSION = '1.5.0'
4
4
  end
5
5
  end
@@ -29,4 +29,5 @@ Gem::Specification.new do |s|
29
29
  s.add_development_dependency 'pry-nav', '~> 0.2.4'
30
30
  s.add_development_dependency 'pry', '~> 0.10.4'
31
31
  s.add_development_dependency 'redis-store-testing'
32
+ s.add_development_dependency 'appraisal', '~> 2.0'
32
33
  end
@@ -39,6 +39,50 @@ describe "Redis::DistributedStore" do
39
39
  @dmr.get("rabbit").must_equal(@rabbit)
40
40
  end
41
41
 
42
+ it "mget" do
43
+ @dmr.set "rabbit2", @white_rabbit
44
+ begin
45
+ @dmr.mget "rabbit", "rabbit2" do |rabbits|
46
+ rabbit, rabbit2 = rabbits
47
+ rabbits.length.must_equal(2)
48
+ rabbit.must_equal(@rabbit)
49
+ rabbit2.must_equal(@white_rabbit)
50
+ end
51
+ rescue Redis::Distributed::CannotDistribute
52
+ # Not supported on redis-rb < 4, and hence Ruby < 2.2.
53
+ end
54
+ end
55
+
56
+ it "mapped_mget" do
57
+ @dmr.set "rabbit2", @white_rabbit
58
+ begin
59
+ result = @dmr.mapped_mget("rabbit", "rabbit2")
60
+ result.keys.must_equal %w[ rabbit rabbit2 ]
61
+ result["rabbit"].must_equal @rabbit
62
+ result["rabbit2"].must_equal @white_rabbit
63
+ rescue Redis::Distributed::CannotDistribute
64
+ # Not supported on redis-rb < 4, and hence Ruby < 2.2.
65
+ end
66
+ end
67
+
68
+ it "passes through ring replica options" do
69
+ dmr = Redis::DistributedStore.new [
70
+ {:host => "localhost", :port => "6380", :db => 0},
71
+ {:host => "localhost", :port => "6381", :db => 0}
72
+ ], replicas: 1024
73
+ dmr.ring.replicas.must_equal 1024
74
+ end
75
+
76
+ it "uses a custom ring object" do
77
+ my_ring = Redis::HashRing.new
78
+ dmr = Redis::DistributedStore.new [
79
+ {:host => "localhost", :port => "6380", :db => 0},
80
+ {:host => "localhost", :port => "6381", :db => 0}
81
+ ], ring: my_ring
82
+ dmr.ring.must_equal my_ring
83
+ dmr.ring.nodes.length.must_equal 2
84
+ end
85
+
42
86
  describe '#redis_version' do
43
87
  it 'returns redis version' do
44
88
  @dmr.nodes.first.expects(:redis_version)
@@ -22,6 +22,11 @@ describe "Redis::Store::Factory" do
22
22
  store.to_s.must_equal("Redis Client connected to localhost:6380 against DB 0")
23
23
  end
24
24
 
25
+ it "uses specified path" do
26
+ store = Redis::Store::Factory.create :path => "/var/run/redis.sock"
27
+ store.to_s.must_equal("Redis Client connected to /var/run/redis.sock against DB 0")
28
+ end
29
+
25
30
  it "uses specified db" do
26
31
  store = Redis::Store::Factory.create :host => "localhost", :port => 6380, :db => 13
27
32
  store.to_s.must_equal("Redis Client connected to localhost:6380 against DB 13")
@@ -115,6 +120,11 @@ describe "Redis::Store::Factory" do
115
120
  store.to_s.must_equal("Redis Client connected to 127.0.0.1:6380 against DB 0")
116
121
  end
117
122
 
123
+ it "uses specified path" do
124
+ store = Redis::Store::Factory.create "unix:///var/run/redis.sock"
125
+ store.to_s.must_equal("Redis Client connected to /var/run/redis.sock against DB 0")
126
+ end
127
+
118
128
  it "uses specified db" do
119
129
  store = Redis::Store::Factory.create "redis://127.0.0.1:6380/13"
120
130
  store.to_s.must_equal("Redis Client connected to 127.0.0.1:6380 against DB 13")
@@ -125,6 +135,16 @@ describe "Redis::Store::Factory" do
125
135
  store.to_s.must_equal("Redis Client connected to 127.0.0.1:6379 against DB 0 with namespace theplaylist")
126
136
  end
127
137
 
138
+ it "uses specified via query namespace" do
139
+ store = Redis::Store::Factory.create "redis://127.0.0.1:6379/0?namespace=theplaylist"
140
+ store.to_s.must_equal("Redis Client connected to 127.0.0.1:6379 against DB 0 with namespace theplaylist")
141
+ end
142
+
143
+ it "uses specified namespace with path" do
144
+ store = Redis::Store::Factory.create "unix:///var/run/redis.sock?db=2&namespace=theplaylist"
145
+ store.to_s.must_equal("Redis Client connected to /var/run/redis.sock against DB 2 with namespace theplaylist")
146
+ end
147
+
128
148
  it "uses specified password" do
129
149
  store = Redis::Store::Factory.create "redis://:secret@127.0.0.1:6379/0/theplaylist"
130
150
  store.instance_variable_get(:@client).password.must_equal("secret")
@@ -139,8 +139,18 @@ describe "Redis::Store::Namespace" do
139
139
  end
140
140
 
141
141
  it "should namespace mget" do
142
- client.expects(:call).with([:mget, "#{@namespace}:rabbit", "#{@namespace}:white_rabbit"])
143
- store.mget "rabbit", "white_rabbit"
142
+ client.expects(:call).with([:mget, "#{@namespace}:rabbit", "#{@namespace}:white_rabbit"]).returns(%w[ foo bar ])
143
+ store.mget "rabbit", "white_rabbit" do |result|
144
+ result.must_equal(%w[ foo bar ])
145
+ end
146
+ end
147
+
148
+ it "should namespace mapped_mget" do
149
+ client.expects(:process).with([[:mget, "#{@namespace}:rabbit", "#{@namespace}:white_rabbit"]]).returns(%w[ foo bar ])
150
+ result = store.mapped_mget "rabbit", "white_rabbit"
151
+ result.keys.must_equal %w[ rabbit white_rabbit ]
152
+ result["rabbit"].must_equal "foo"
153
+ result["white_rabbit"].must_equal "bar"
144
154
  end
145
155
 
146
156
  it "should namespace expire" do
@@ -157,5 +167,20 @@ describe "Redis::Store::Namespace" do
157
167
  client.expects(:call).with([:watch,"#{@namespace}:rabbit"]).once
158
168
  store.watch("rabbit")
159
169
  end
170
+
171
+ it "wraps flushdb with appropriate KEYS * calls" do
172
+ client.expects(:call).with([:flushdb]).never
173
+ client.expects(:call).with([:keys,"#{@namespace}:*"]).once.returns(["rabbit"])
174
+ client.expects(:call).with([:del,"#{@namespace}:rabbit"]).once
175
+ store.flushdb
176
+ end
177
+
178
+ it "skips flushdb wrapping if the namespace is nil" do
179
+ client.expects(:call).with([:flushdb])
180
+ client.expects(:call).with([:keys]).never
181
+ store.with_namespace(nil) do
182
+ store.flushdb
183
+ end
184
+ end
160
185
  end
161
186
  end
@@ -97,28 +97,39 @@ describe "Redis::Serialization" do
97
97
  @store.get("rabbit", :raw => true).must_equal(%(#<OpenStruct color="white">))
98
98
  end
99
99
 
100
- it "doesn't unmarshal on multi get" do
100
+ it "unmarshals on multi get" do
101
101
  @store.set "rabbit2", @white_rabbit
102
- rabbits = @store.mget "rabbit", "rabbit2"
103
- rabbit, rabbit2 = rabbits
104
- rabbits.length.must_equal(2)
105
- rabbit.must_equal(@rabbit)
106
- rabbit2.must_equal(@white_rabbit)
102
+ @store.mget "rabbit", "rabbit2" do |rabbits|
103
+ rabbit, rabbit2 = rabbits
104
+ rabbits.length.must_equal(2)
105
+ rabbit.must_equal(@rabbit)
106
+ rabbit2.must_equal(@white_rabbit)
107
+ end
108
+ end
109
+
110
+ it "unmarshals on mapped_mget" do
111
+ @store.set "rabbit2", @white_rabbit
112
+ result = @store.mapped_mget("rabbit", "rabbit2")
113
+ result.keys.must_equal %w[ rabbit rabbit2 ]
114
+ result["rabbit"].must_equal @rabbit
115
+ result["rabbit2"].must_equal @white_rabbit
107
116
  end
108
117
 
109
118
  if RUBY_VERSION.match /1\.9/
110
119
  it "doesn't unmarshal on multi get if raw option is true" do
111
120
  @store.set "rabbit2", @white_rabbit
112
- rabbit, rabbit2 = @store.mget "rabbit", "rabbit2", :raw => true
113
- rabbit.must_equal("\x04\bU:\x0FOpenStruct{\x06:\tnameI\"\nbunny\x06:\x06EF")
114
- rabbit2.must_equal("\x04\bU:\x0FOpenStruct{\x06:\ncolorI\"\nwhite\x06:\x06EF")
121
+ @store.mget "rabbit", "rabbit2", :raw => true do |rabbit, rabbit2|
122
+ rabbit.must_equal("\x04\bU:\x0FOpenStruct{\x06:\tnameI\"\nbunny\x06:\x06EF")
123
+ rabbit2.must_equal("\x04\bU:\x0FOpenStruct{\x06:\ncolorI\"\nwhite\x06:\x06EF")
124
+ end
115
125
  end
116
126
  else
117
127
  it "doesn't unmarshal on multi get if raw option is true" do
118
128
  @store.set "rabbit2", @white_rabbit
119
- rabbit, rabbit2 = @store.mget "rabbit", "rabbit2", :raw => true
120
- rabbit.must_include("\x04\bU:\x0FOpenStruct{\x06:\tname")
121
- rabbit2.must_include("\x04\bU:\x0FOpenStruct{\x06:\ncolor")
129
+ @store.mget "rabbit", "rabbit2", :raw => true do |rabbit, rabbit2|
130
+ rabbit.must_include("\x04\bU:\x0FOpenStruct{\x06:\tname")
131
+ rabbit2.must_include("\x04\bU:\x0FOpenStruct{\x06:\ncolor")
132
+ end
122
133
  end
123
134
  end
124
135
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-store
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-13 00:00:00.000000000 Z
11
+ date: 2018-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -142,6 +142,20 @@ dependencies:
142
142
  - - ">="
143
143
  - !ruby/object:Gem::Version
144
144
  version: '0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: appraisal
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '2.0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '2.0'
145
159
  description: Namespaced Rack::Session, Rack::Cache, I18n and cache Redis stores for
146
160
  Ruby web frameworks.
147
161
  email:
@@ -152,11 +166,14 @@ extra_rdoc_files: []
152
166
  files:
153
167
  - ".gitignore"
154
168
  - ".travis.yml"
169
+ - Appraisals
155
170
  - CHANGELOG.md
156
171
  - Gemfile
157
172
  - MIT-LICENSE
158
173
  - README.md
159
174
  - Rakefile
175
+ - gemfiles/redis_3_x.gemfile
176
+ - gemfiles/redis_4_x.gemfile
160
177
  - lib/redis-store.rb
161
178
  - lib/redis/distributed_store.rb
162
179
  - lib/redis/store.rb
@@ -198,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
215
  version: '0'
199
216
  requirements: []
200
217
  rubyforge_project: redis-store
201
- rubygems_version: 2.6.11
218
+ rubygems_version: 2.7.3
202
219
  signing_key:
203
220
  specification_version: 4
204
221
  summary: Redis stores for Ruby frameworks