moneta 1.4.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +35 -38
  3. data/CHANGES +9 -0
  4. data/CONTRIBUTORS +2 -0
  5. data/Gemfile +143 -55
  6. data/README.md +5 -4
  7. data/lib/moneta/adapter.rb +52 -0
  8. data/lib/moneta/adapters/activerecord.rb +77 -68
  9. data/lib/moneta/adapters/activesupportcache.rb +22 -31
  10. data/lib/moneta/adapters/cassandra.rb +114 -116
  11. data/lib/moneta/adapters/client.rb +17 -18
  12. data/lib/moneta/adapters/couch.rb +31 -26
  13. data/lib/moneta/adapters/datamapper.rb +9 -5
  14. data/lib/moneta/adapters/daybreak.rb +15 -21
  15. data/lib/moneta/adapters/dbm.rb +6 -12
  16. data/lib/moneta/adapters/file.rb +21 -13
  17. data/lib/moneta/adapters/fog.rb +5 -6
  18. data/lib/moneta/adapters/gdbm.rb +6 -12
  19. data/lib/moneta/adapters/hbase.rb +10 -12
  20. data/lib/moneta/adapters/kyotocabinet.rb +22 -27
  21. data/lib/moneta/adapters/leveldb.rb +14 -20
  22. data/lib/moneta/adapters/lmdb.rb +19 -22
  23. data/lib/moneta/adapters/localmemcache.rb +7 -13
  24. data/lib/moneta/adapters/lruhash.rb +20 -20
  25. data/lib/moneta/adapters/memcached/dalli.rb +25 -33
  26. data/lib/moneta/adapters/memcached/native.rb +14 -20
  27. data/lib/moneta/adapters/memory.rb +5 -7
  28. data/lib/moneta/adapters/mongo.rb +53 -52
  29. data/lib/moneta/adapters/pstore.rb +21 -27
  30. data/lib/moneta/adapters/redis.rb +42 -37
  31. data/lib/moneta/adapters/restclient.rb +17 -25
  32. data/lib/moneta/adapters/riak.rb +8 -9
  33. data/lib/moneta/adapters/sdbm.rb +6 -12
  34. data/lib/moneta/adapters/sequel/mysql.rb +8 -8
  35. data/lib/moneta/adapters/sequel/postgres.rb +17 -17
  36. data/lib/moneta/adapters/sequel/postgres_hstore.rb +47 -47
  37. data/lib/moneta/adapters/sequel/sqlite.rb +9 -9
  38. data/lib/moneta/adapters/sequel.rb +56 -65
  39. data/lib/moneta/adapters/sqlite.rb +37 -35
  40. data/lib/moneta/adapters/tdb.rb +8 -14
  41. data/lib/moneta/adapters/tokyocabinet.rb +19 -17
  42. data/lib/moneta/adapters/tokyotyrant.rb +29 -30
  43. data/lib/moneta/adapters/yaml.rb +1 -5
  44. data/lib/moneta/config.rb +101 -0
  45. data/lib/moneta/expires.rb +0 -1
  46. data/lib/moneta/expires_support.rb +3 -4
  47. data/lib/moneta/pool.rb +1 -1
  48. data/lib/moneta/proxy.rb +29 -0
  49. data/lib/moneta/server.rb +21 -14
  50. data/lib/moneta/version.rb +1 -1
  51. data/lib/moneta/wrapper.rb +5 -0
  52. data/lib/moneta.rb +2 -0
  53. data/moneta.gemspec +1 -0
  54. data/spec/moneta/adapters/client/client_helper.rb +4 -3
  55. data/spec/moneta/adapters/faraday_helper.rb +3 -2
  56. data/spec/moneta/adapters/lruhash/adapter_lruhash_spec.rb +10 -6
  57. data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +2 -2
  58. data/spec/moneta/config_spec.rb +219 -0
  59. data/spec/moneta/proxies/transformer/transformer_bson_spec.rb +3 -1
  60. data/spec/moneta/proxies/transformer/transformer_marshal_escape_spec.rb +2 -0
  61. data/spec/rack/session_moneta_spec.rb +44 -25
  62. data/spec/restserver.rb +3 -14
  63. metadata +24 -6
@@ -4,47 +4,39 @@ module Moneta
4
4
  module Adapters
5
5
  # Memcached backend (using gem dalli)
6
6
  # @api public
7
- class MemcachedDalli
8
- include Defaults
7
+ class MemcachedDalli < Adapter
9
8
  include ExpiresSupport
10
9
 
11
10
  supports :create, :increment
12
- attr_reader :backend
13
-
14
- # @param [Hash] options
15
- # @option options [String] :server ('127.0.0.1:11211') Memcached server
16
- # @option options [Integer] :expires Default expiration time
17
- # @option options [::Dalli::Client] :backend Use existing backend instance
18
- # @option options Other options passed to `Dalli::Client#new`
19
- def initialize(options = {})
20
- self.default_expires = options.delete(:expires)
21
- @backend = options[:backend] ||
22
- begin
23
- server = options.delete(:server) || '127.0.0.1:11211'
24
- ::Dalli::Client.new(server, options)
25
- end
26
- end
11
+
12
+ # @!method initialize(options = {})
13
+ # @param [Hash] options
14
+ # @option options [String] :server ('127.0.0.1:11211') Memcached server
15
+ # @option options [Integer] :expires Default expiration time
16
+ # @option options [::Dalli::Client] :backend Use existing backend instance
17
+ # @option options Other options passed to `Dalli::Client#new`
18
+ backend { |server: '127.0.0.1:11211', **options| ::Dalli::Client.new(server, options) }
27
19
 
28
20
  # (see Proxy#load)
29
21
  def load(key, options = {})
30
- value = @backend.get(key)
22
+ value = backend.get(key)
31
23
  if value
32
24
  expires = expires_value(options, nil)
33
- @backend.set(key, value, expires || nil, raw: true) if expires != nil
25
+ backend.set(key, value, expires || nil, raw: true) if expires != nil
34
26
  value
35
27
  end
36
28
  end
37
29
 
38
30
  # (see Proxy#store)
39
31
  def store(key, value, options = {})
40
- @backend.set(key, value, expires_value(options) || nil, raw: true)
32
+ backend.set(key, value, expires_value(options) || nil, raw: true)
41
33
  value
42
34
  end
43
35
 
44
36
  # (see Proxy#delete)
45
37
  def delete(key, options = {})
46
- value = @backend.get(key)
47
- @backend.delete(key)
38
+ value = backend.get(key)
39
+ backend.delete(key)
48
40
  value
49
41
  end
50
42
 
@@ -52,9 +44,9 @@ module Moneta
52
44
  def increment(key, amount = 1, options = {})
53
45
  result =
54
46
  if amount >= 0
55
- @backend.incr(key, amount, expires_value(options) || nil)
47
+ backend.incr(key, amount, expires_value(options) || nil)
56
48
  else
57
- @backend.decr(key, -amount, expires_value(options) || nil)
49
+ backend.decr(key, -amount, expires_value(options) || nil)
58
50
  end
59
51
  if result
60
52
  result
@@ -67,32 +59,32 @@ module Moneta
67
59
 
68
60
  # (see Proxy#clear)
69
61
  def clear(options = {})
70
- @backend.flush_all
62
+ backend.flush_all
71
63
  self
72
64
  end
73
65
 
74
66
  # (see Defaults#create)
75
67
  def create(key, value, options = {})
76
- !!@backend.add(key, value, expires_value(options) || nil, raw: true)
68
+ !!backend.add(key, value, expires_value(options) || nil, raw: true)
77
69
  end
78
70
 
79
71
  # (see Proxy#close)
80
72
  def close
81
- @backend.close
73
+ backend.close
82
74
  nil
83
75
  end
84
76
 
85
77
  # (see Defaults#slice)
86
78
  def slice(*keys, **options)
87
- @backend.get_multi(keys).tap do |pairs|
79
+ backend.get_multi(keys).tap do |pairs|
88
80
  next if pairs.empty?
89
81
  expires = expires_value(options, nil)
90
82
  next if expires == nil
91
83
  expires = expires.to_i if Numeric === expires
92
84
  expires ||= 0
93
- @backend.multi do
85
+ backend.multi do
94
86
  pairs.each do |key, value|
95
- @backend.set(key, value, expires, false)
87
+ backend.set(key, value, expires, false)
96
88
  end
97
89
  end
98
90
  end
@@ -112,7 +104,7 @@ module Moneta
112
104
 
113
105
  if block_given?
114
106
  keys = pairs.map { |key, _| key }.to_a
115
- old_pairs = @backend.get_multi(keys)
107
+ old_pairs = backend.get_multi(keys)
116
108
  pairs = pairs.map do |key, new_value|
117
109
  if old_pairs.key? key
118
110
  new_value = yield(key, old_pairs[key], new_value)
@@ -121,9 +113,9 @@ module Moneta
121
113
  end
122
114
  end
123
115
 
124
- @backend.multi do
116
+ backend.multi do
125
117
  pairs.each do |key, value|
126
- @backend.set(key, value, expires, raw: true)
118
+ backend.set(key, value, expires, raw: true)
127
119
  end
128
120
  end
129
121
 
@@ -4,30 +4,24 @@ module Moneta
4
4
  module Adapters
5
5
  # Memcached backend (using gem memcached)
6
6
  # @api public
7
- class MemcachedNative
8
- include Defaults
7
+ class MemcachedNative < Adapter
9
8
  include ExpiresSupport
10
9
 
11
10
  supports :create, :increment
12
- attr_reader :backend
13
11
 
14
- # @param [Hash] options
15
- # @option options [String] :server ('127.0.0.1:11211') Memcached server
16
- # @option options [String] :namespace Key namespace
17
- # @option options [Integer] :expires (604800) Default expiration time
18
- # @option options [::Memcached] :backend Use existing backend instance
19
- # @option options Other options passed to `Memcached#new`
20
- def initialize(options = {})
21
- server = options.delete(:server) || '127.0.0.1:11211'
22
- self.default_expires = options.delete(:expires)
23
- @backend = options[:backend] ||
24
- begin
25
- options[:prefix_key] = options.delete(:namespace) if options[:namespace]
26
- # We don't want a limitation on the key charset. Therefore we use the binary protocol.
27
- # It is also faster.
28
- options[:binary_protocol] = true unless options.include?(:binary_protocol)
29
- ::Memcached.new(server, options)
30
- end
12
+ # @!method initialize(options = {})
13
+ # @param [Hash] options
14
+ # @option options [String] :server ('127.0.0.1:11211') Memcached server
15
+ # @option options [String] :namespace Key namespace
16
+ # @option options [Integer] :expires (604800) Default expiration time
17
+ # @option options [::Memcached] :backend Use existing backend instance
18
+ # @option options Other options passed to `Memcached#new`
19
+ backend do |server: '127.0.0.1:11211', namespace: nil, **options|
20
+ options[:prefix_key] = namespace if namespace
21
+ # We don't want a limitation on the key charset. Therefore we use the binary protocol.
22
+ # It is also faster.
23
+ options[:binary_protocol] = true unless options.include?(:binary_protocol)
24
+ ::Memcached.new(server, options)
31
25
  end
32
26
 
33
27
  # (see Proxy#load)
@@ -2,19 +2,17 @@ module Moneta
2
2
  module Adapters
3
3
  # Memory backend using a hash to store the entries
4
4
  # @api public
5
- class Memory
6
- include Defaults
5
+ class Memory < Adapter
7
6
  include NilValues
8
7
  include HashAdapter
9
8
  include IncrementSupport
10
9
  include CreateSupport
11
10
  include EachKeySupport
12
11
 
13
- # @param [Hash] options Options hash
14
- # @option options [Hash] :backend Use existing backend instance
15
- def initialize(options = {})
16
- @backend = options[:backend] || {}
17
- end
12
+ # @!method initialize(options = {})
13
+ # @param [Hash] options Options hash
14
+ # @option options [Hash] :backend Use existing backend instance
15
+ backend { {} }
18
16
  end
19
17
  end
20
18
  end
@@ -14,14 +14,33 @@ module Moneta
14
14
  # db['key'] = {a: 1, b: 2}
15
15
  #
16
16
  # @api public
17
- class Mongo
18
- include Defaults
17
+ class Mongo < Adapter
19
18
  include ExpiresSupport
20
19
 
21
20
  supports :each_key, :create, :increment
22
- attr_reader :backend
23
21
 
24
- DEFAULT_PORT = 27017
22
+ config :collection, default: 'moneta'
23
+
24
+ config :db
25
+ config :database, default: 'moneta' do |database:, db:, **|
26
+ if db
27
+ warn('Moneta::Adapters::Mongo - the :db option is deprecated and will be removed in a future version. Use :database instead')
28
+ db
29
+ else
30
+ database
31
+ end
32
+ end
33
+
34
+ config :expires_field, default: 'expiresAt'
35
+ config :value_field, default: 'value'
36
+ config :type_field, default: 'type'
37
+
38
+ backend do |host: '127.0.0.1', port: 27017, **options|
39
+ options[:logger] ||= ::Logger.new(STDERR).tap do |logger|
40
+ logger.level = ::Logger::ERROR
41
+ end
42
+ ::Mongo::Client.new(["#{host}:#{port}"], options)
43
+ end
25
44
 
26
45
  # @param [Hash] options
27
46
  # @option options [String] :collection ('moneta') MongoDB collection name
@@ -37,31 +56,13 @@ module Moneta
37
56
  # @option options [::Mongo::Client] :backend Use existing backend instance
38
57
  # @option options Other options passed to `Mongo::MongoClient#new`
39
58
  def initialize(options = {})
40
- self.default_expires = options.delete(:expires)
41
- @expires_field = options.delete(:expires_field) || 'expiresAt'
42
- @value_field = options.delete(:value_field) || 'value'
43
- @type_field = options.delete(:type_field) || 'type'
59
+ super
44
60
 
45
- collection = options.delete(:collection) || 'moneta'
61
+ @database = backend.use(config.database)
62
+ @collection = @database[config.collection]
46
63
 
47
- if options.key?(:db)
48
- warn('Moneta::Adapters::Mongo - the :db option is deprecated and will be removed in a future version. Use :database instead')
49
- end
50
- database = options.delete(:database) || options.delete(:db) || 'moneta'
51
- backend = options[:backend] ||
52
- begin
53
- host = options.delete(:host) || '127.0.0.1'
54
- port = options.delete(:port) || DEFAULT_PORT
55
- options[:logger] ||= ::Logger.new(STDERR).tap do |logger|
56
- logger.level = ::Logger::ERROR
57
- end
58
- ::Mongo::Client.new(["#{host}:#{port}"], options)
59
- end
60
-
61
- @backend = backend.use(database)
62
- @collection = @backend[collection]
63
- if @backend.command(buildinfo: 1).documents.first['version'] >= '2.2'
64
- @collection.indexes.create_one({ @expires_field => 1 }, expire_after: 0)
64
+ if @database.command(buildinfo: 1).documents.first['version'] >= '2.2'
65
+ @collection.indexes.create_one({ config.expires_field => 1 }, expire_after: 0)
65
66
  else
66
67
  warn 'Moneta::Adapters::Mongo - You are using MongoDB version < 2.2, expired documents will not be deleted'
67
68
  end
@@ -78,7 +79,7 @@ module Moneta
78
79
 
79
80
  if doc
80
81
  update_expiry(options, nil) do |expires|
81
- view.update_one(:$set => { @expires_field => expires })
82
+ view.update_one(:$set => { config.expires_field => expires })
82
83
  end
83
84
 
84
85
  doc_to_value(doc)
@@ -105,7 +106,7 @@ module Moneta
105
106
  def delete(key, options = {})
106
107
  key = to_binary(key)
107
108
  if doc = @collection.find(_id: key).find_one_and_delete and
108
- !doc[@expires_field] || doc[@expires_field] >= Time.now
109
+ !doc[config.expires_field] || doc[config.expires_field] >= Time.now
109
110
  doc_to_value(doc)
110
111
  end
111
112
  end
@@ -113,9 +114,9 @@ module Moneta
113
114
  # (see Proxy#increment)
114
115
  def increment(key, amount = 1, options = {})
115
116
  @collection.find_one_and_update({ :$and => [{ _id: to_binary(key) }, not_expired] },
116
- { :$inc => { @value_field => amount } },
117
+ { :$inc => { config.value_field => amount } },
117
118
  return_document: :after,
118
- upsert: true)[@value_field]
119
+ upsert: true)[config.value_field]
119
120
  rescue ::Mongo::Error::OperationFailure
120
121
  tries ||= 0
121
122
  (tries += 1) < 3 ? retry : raise
@@ -126,8 +127,8 @@ module Moneta
126
127
  key = to_binary(key)
127
128
  @collection.insert_one(value_to_doc(key, value, options))
128
129
  true
129
- rescue ::Mongo::Error::OperationFailure => ex
130
- raise unless ex.message =~ /^E11000 / # duplicate key error
130
+ rescue ::Mongo::Error::OperationFailure => error
131
+ raise unless error.code == 11000 # duplicate key error
131
132
  false
132
133
  end
133
134
 
@@ -139,7 +140,7 @@ module Moneta
139
140
 
140
141
  # (see Proxy#close)
141
142
  def close
142
- @backend.close
143
+ @database.close
143
144
  nil
144
145
  end
145
146
 
@@ -152,7 +153,7 @@ module Moneta
152
153
  pairs = view.map { |doc| [from_binary(doc[:_id]), doc_to_value(doc)] }
153
154
 
154
155
  update_expiry(options, nil) do |expires|
155
- view.update_many(:$set => { @expires_field => expires })
156
+ view.update_many(:$set => { config.expires_field => expires })
156
157
  end
157
158
 
158
159
  pairs
@@ -198,18 +199,18 @@ module Moneta
198
199
  private
199
200
 
200
201
  def doc_to_value(doc)
201
- case doc[@type_field]
202
+ case doc[config.type_field]
202
203
  when 'Hash'
203
204
  doc = doc.dup
204
205
  doc.delete('_id')
205
- doc.delete(@type_field)
206
- doc.delete(@expires_field)
206
+ doc.delete(config.type_field)
207
+ doc.delete(config.expires_field)
207
208
  doc
208
209
  when 'Number'
209
- doc[@value_field]
210
+ doc[config.value_field]
210
211
  else
211
212
  # In ruby_bson version 2 (and probably up), #to_s no longer returns the binary data
212
- from_binary(doc[@value_field])
213
+ from_binary(doc[config.value_field])
213
214
  end
214
215
  end
215
216
 
@@ -217,22 +218,22 @@ module Moneta
217
218
  case value
218
219
  when Hash
219
220
  value.merge('_id' => key,
220
- @type_field => 'Hash',
221
- # @expires_field must be a Time object (BSON date datatype)
222
- @expires_field => expires_at(options) || nil)
221
+ config.type_field => 'Hash',
222
+ # expires_field must be a Time object (BSON date datatype)
223
+ config.expires_field => expires_at(options) || nil)
223
224
  when Float, Integer
224
225
  { '_id' => key,
225
- @type_field => 'Number',
226
- @value_field => value,
227
- # @expires_field must be a Time object (BSON date datatype)
228
- @expires_field => expires_at(options) || nil }
226
+ config.type_field => 'Number',
227
+ config.value_field => value,
228
+ # expires_field must be a Time object (BSON date datatype)
229
+ config.expires_field => expires_at(options) || nil }
229
230
  when String
230
231
  intvalue = value.to_i
231
232
  { '_id' => key,
232
- @type_field => 'String',
233
- @value_field => intvalue.to_s == value ? intvalue : to_binary(value),
233
+ config.type_field => 'String',
234
+ config.value_field => intvalue.to_s == value ? intvalue : to_binary(value),
234
235
  # @expires_field must be a Time object (BSON date datatype)
235
- @expires_field => expires_at(options) || nil }
236
+ config.expires_field => expires_at(options) || nil }
236
237
  else
237
238
  raise ArgumentError, "Invalid value type: #{value.class}"
238
239
  end
@@ -253,8 +254,8 @@ module Moneta
253
254
  def not_expired
254
255
  {
255
256
  :$or => [
256
- { @expires_field => nil },
257
- { @expires_field => { :$gte => Time.now } }
257
+ { config.expires_field => nil },
258
+ { config.expires_field => { :$gte => Time.now } }
258
259
  ]
259
260
  }
260
261
  end
@@ -5,63 +5,61 @@ module Moneta
5
5
  module Adapters
6
6
  # PStore backend
7
7
  # @api public
8
- class PStore
9
- include Defaults
8
+ class PStore < Adapter
10
9
  include NilValues
11
10
 
12
11
  supports :create, :increment, :each_key
13
- attr_reader :backend
12
+
13
+ backend do |file:, threadsafe: false|
14
+ FileUtils.mkpath(::File.dirname(file))
15
+ ::PStore.new(file, threadsafe)
16
+ end
14
17
 
15
18
  # @param [Hash] options
16
19
  # @option options [String] :file PStore file
20
+ # @option options [Boolean] :threadsafe Makes the PStore thread-safe
17
21
  # @option options [::PStore] :backend Use existing backend instance
18
22
  def initialize(options = {})
19
- @backend = options[:backend] ||
20
- begin
21
- raise ArgumentError, 'Option :file is required' unless options[:file]
22
- FileUtils.mkpath(::File.dirname(options[:file]))
23
- new_store(options)
24
- end
25
-
23
+ super
26
24
  @id = "Moneta::Adapters::PStore(#{object_id})"
27
25
  end
28
26
 
29
27
  # (see Proxy#key?)
30
28
  def key?(key, options = {})
31
- transaction(true) { @backend.root?(key) }
29
+ transaction(true) { backend.root?(key) }
32
30
  end
33
31
 
34
32
  # (see Proxy#each_key)
35
33
  def each_key(&block)
36
- return enum_for(:each_key) { transaction(true) { @backend.roots.size } } unless block_given?
34
+ return enum_for(:each_key) { transaction(true) { backend.roots.size } } unless block_given?
37
35
 
38
36
  transaction(true) do
39
- @backend.roots.each { |k| yield(k) }
37
+ backend.roots.each { |k| yield(k) }
40
38
  end
41
39
  self
42
40
  end
43
41
 
44
42
  # (see Proxy#load)
45
43
  def load(key, options = {})
46
- transaction(true) { @backend[key] }
44
+ transaction(true) { backend[key] }
47
45
  end
48
46
 
49
47
  # (see Proxy#store)
50
48
  def store(key, value, options = {})
51
- transaction { @backend[key] = value }
49
+ transaction { backend[key] = value }
52
50
  end
53
51
 
54
52
  # (see Proxy#delete)
55
53
  def delete(key, options = {})
56
- transaction { @backend.delete(key) }
54
+ transaction { backend.delete(key) }
57
55
  end
58
56
 
59
57
  # (see Proxy#increment)
60
58
  def increment(key, amount = 1, options = {})
61
59
  transaction do
62
- existing = @backend[key]
60
+ existing = backend[key]
63
61
  value = (existing == nil ? 0 : Integer(existing)) + amount
64
- @backend[key] = value.to_s
62
+ backend[key] = value.to_s
65
63
  value
66
64
  end
67
65
  end
@@ -69,10 +67,10 @@ module Moneta
69
67
  # (see Proxy#create)
70
68
  def create(key, value, options = {})
71
69
  transaction do
72
- if @backend.root?(key)
70
+ if backend.root?(key)
73
71
  false
74
72
  else
75
- @backend[key] = value
73
+ backend[key] = value
76
74
  true
77
75
  end
78
76
  end
@@ -81,8 +79,8 @@ module Moneta
81
79
  # (see Proxy#clear)
82
80
  def clear(options = {})
83
81
  transaction do
84
- @backend.roots.each do |key|
85
- @backend.delete(key)
82
+ backend.roots.each do |key|
83
+ backend.delete(key)
86
84
  end
87
85
  end
88
86
  self
@@ -109,10 +107,6 @@ module Moneta
109
107
 
110
108
  class TransactionError < StandardError; end
111
109
 
112
- def new_store(options)
113
- ::PStore.new(options[:file], options[:threadsafe])
114
- end
115
-
116
110
  def transaction(read_only = false)
117
111
  case Thread.current[@id]
118
112
  when read_only, false
@@ -122,7 +116,7 @@ module Moneta
122
116
  else
123
117
  begin
124
118
  Thread.current[@id] = read_only
125
- @backend.transaction(read_only) { yield }
119
+ backend.transaction(read_only) { yield }
126
120
  ensure
127
121
  Thread.current[@id] = nil
128
122
  end
@@ -4,32 +4,28 @@ module Moneta
4
4
  module Adapters
5
5
  # Redis backend
6
6
  # @api public
7
- class Redis
8
- include Defaults
7
+ class Redis < Adapter
9
8
  include ExpiresSupport
10
9
 
11
10
  supports :create, :increment, :each_key
12
- attr_reader :backend
13
-
14
- # @param [Hash] options
15
- # @option options [Integer] :expires Default expiration time
16
- # @option options [::Redis] :backend Use existing backend instance
17
- # @option options Other options passed to `Redis#new`
18
- def initialize(options = {})
19
- self.default_expires = options.delete(:expires)
20
- @backend = options[:backend] || ::Redis.new(options)
21
- end
11
+
12
+ # @!method initialize(options = {})
13
+ # @param [Hash] options
14
+ # @option options [Integer] :expires Default expiration time
15
+ # @option options [::Redis] :backend Use existing backend instance
16
+ # @option options Other options passed to `Redis#new`
17
+ backend { |**options| ::Redis.new(options) }
22
18
 
23
19
  # (see Proxy#key?)
24
20
  #
25
21
  # This method considers false and 0 as "no-expire" and every positive
26
22
  # number as a time to live in seconds.
27
23
  def key?(key, options = {})
28
- with_expiry_update(key, default: nil, **options) do
29
- if @backend.respond_to?(:exists?)
30
- @backend.exists?(key)
24
+ with_expiry_update(key, default: nil, **options) do |pipeline_handle|
25
+ if pipeline_handle.respond_to?(:exists?)
26
+ pipeline_handle.exists?(key)
31
27
  else
32
- @backend.exists(key)
28
+ pipeline_handle.exists(key)
33
29
  end
34
30
  end
35
31
  end
@@ -44,8 +40,8 @@ module Moneta
44
40
 
45
41
  # (see Proxy#load)
46
42
  def load(key, options = {})
47
- with_expiry_update(key, default: nil, **options) do
48
- @backend.get(key)
43
+ with_expiry_update(key, default: nil, **options) do |pipeline_handle|
44
+ pipeline_handle.get(key)
49
45
  end
50
46
  end
51
47
 
@@ -63,17 +59,17 @@ module Moneta
63
59
  # (see Proxy#delete)
64
60
  def delete(key, options = {})
65
61
  future = nil
66
- @backend.pipelined do
67
- future = @backend.get(key)
68
- @backend.del(key)
62
+ @backend.pipelined do |pipeline|
63
+ future = pipeline.get(key)
64
+ pipeline.del(key)
69
65
  end
70
66
  future.value
71
67
  end
72
68
 
73
69
  # (see Proxy#increment)
74
70
  def increment(key, amount = 1, options = {})
75
- with_expiry_update(key, **options) do
76
- @backend.incrby(key, amount)
71
+ with_expiry_update(key, **options) do |pipeline_handle|
72
+ pipeline_handle.incrby(key, amount)
77
73
  end
78
74
  end
79
75
 
@@ -85,10 +81,10 @@ module Moneta
85
81
 
86
82
  # (see Defaults#create)
87
83
  def create(key, value, options = {})
88
- expires = expires_value(options, @default_expires)
84
+ expires = expires_value(options, config.expires)
89
85
 
90
86
  if @backend.setnx(key, value)
91
- update_expires(key, expires)
87
+ update_expires(@backend, key, expires)
92
88
  true
93
89
  else
94
90
  false
@@ -103,8 +99,8 @@ module Moneta
103
99
 
104
100
  # (see Defaults#values_at)
105
101
  def values_at(*keys, **options)
106
- with_expiry_update(*keys, default: nil, **options) do
107
- @backend.mget(*keys)
102
+ with_expiry_update(*keys, default: nil, **options) do |pipeline_handle|
103
+ pipeline_handle.mget(*keys)
108
104
  end
109
105
  end
110
106
 
@@ -130,8 +126,8 @@ module Moneta
130
126
  end
131
127
  end
132
128
 
133
- with_expiry_update(*keys, **options) do
134
- @backend.mset(*pairs.to_a.flatten(1))
129
+ with_expiry_update(*keys, **options) do |pipeline_handle|
130
+ pipeline_handle.mset(*pairs.to_a.flatten(1))
135
131
  end
136
132
 
137
133
  self
@@ -139,24 +135,33 @@ module Moneta
139
135
 
140
136
  protected
141
137
 
142
- def update_expires(key, expires)
138
+ def update_expires(pipeline_handle, key, expires)
143
139
  case expires
144
140
  when false
145
- @backend.persist(key)
141
+ pipeline_handle.persist(key)
146
142
  when Numeric
147
- @backend.pexpire(key, (expires * 1000).to_i)
143
+ pipeline_handle.pexpire(key, (expires * 1000).to_i)
148
144
  end
149
145
  end
150
146
 
151
- def with_expiry_update(*keys, default: @default_expires, **options)
147
+ def with_expiry_update(*keys, default: config.expires, **options)
152
148
  expires = expires_value(options, default)
153
149
  if expires == nil
154
- yield
150
+ yield(@backend)
155
151
  else
156
152
  future = nil
157
- @backend.multi do
158
- future = yield
159
- keys.each { |key| update_expires(key, expires) }
153
+ @backend.multi do |pipeline|
154
+ # as of redis 4.6 calling redis methods on the redis client itself
155
+ # is deprecated in favor of a pipeline handle provided by the
156
+ # +multi+ call. This will cause in error in redis >= 5.0.
157
+ #
158
+ # In order to continue supporting redis versions < 4.6, the following
159
+ # fallback has been introduced and can be removed once moneta
160
+ # no longer supports redis < 4.6.
161
+
162
+ pipeline_handle = pipeline || @backend
163
+ future = yield(pipeline_handle)
164
+ keys.each { |key| update_expires(pipeline_handle, key, expires) }
160
165
  end
161
166
  future.value
162
167
  end