moneta 1.4.2 → 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.
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