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
@@ -5,12 +5,10 @@ module Moneta
5
5
  module Adapters
6
6
  # ActiveRecord as key/value stores
7
7
  # @api public
8
- class ActiveRecord
9
- include Defaults
10
-
8
+ class ActiveRecord < Adapter
11
9
  supports :create, :increment, :each_key
12
10
 
13
- attr_reader :table, :key_column, :value_column
11
+ attr_reader :table
14
12
  delegate :with_connection, to: :connection_pool
15
13
 
16
14
  @connection_lock = ::Mutex.new
@@ -37,9 +35,30 @@ module Moneta
37
35
  end
38
36
  end
39
37
 
38
+ config :key_column, default: :k
39
+ config :value_column, default: :v
40
+
41
+ backend required: false do |table: :moneta, connection: nil, create_table: nil|
42
+ @spec = spec_for_connection(connection)
43
+
44
+ # Ensure the table name is a symbol.
45
+ table_name = table.to_sym
46
+
47
+ if create_table == nil
48
+ default_create_table(table_name)
49
+ elsif create_table
50
+ with_connection(&create_table)
51
+ end
52
+
53
+ @table = ::Arel::Table.new(table_name)
54
+
55
+ # backend is only used if there's an existing ActiveRecord model
56
+ nil
57
+ end
58
+
40
59
  # @param [Hash] options
41
60
  # @option options [Object] :backend A class object inheriting from ActiveRecord::Base to use as a table
42
- # @option options [String] :table ('moneta') Table name
61
+ # @option options [String,Symbol] :table (:moneta) Table name
43
62
  # @option options [Hash/String/Symbol] :connection ActiveRecord connection configuration (`Hash` or `String`), or
44
63
  # symbol giving the name of a Rails connection (e.g. :production)
45
64
  # @option options [Proc, Boolean] :create_table Proc called with a connection if table
@@ -47,54 +66,12 @@ module Moneta
47
66
  # @option options [Symbol] :key_column (:k) The name of the column to use for keys
48
67
  # @option options [Symbol] :value_column (:v) The name of the column to use for values
49
68
  def initialize(options = {})
50
- @key_column = options.delete(:key_column) || :k
51
- @value_column = options.delete(:value_column) || :v
69
+ super
52
70
 
53
- if backend = options.delete(:backend)
71
+ # If a :backend was provided, use it to set the spec and table
72
+ if backend
54
73
  @spec = backend.connection_pool.spec
55
- @table = ::Arel::Table.new(backend.table_name.to_sym)
56
- else
57
- # Feed the connection info into ActiveRecord and get back a name to use for getting the
58
- # connection pool
59
- connection = options.delete(:connection)
60
- @spec =
61
- case connection
62
- when Symbol
63
- connection
64
- when Hash, String
65
- # Normalize the connection specification to a hash
66
- resolver = ::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new \
67
- 'dummy' => connection
68
-
69
- # Turn the config into a standardised hash, sans a couple of bits
70
- hash = resolver.resolve(:dummy)
71
- hash.delete('name')
72
- hash.delete(:password) # For security
73
- # Make a name unique to this config
74
- name = 'moneta?' + URI.encode_www_form(hash.to_a.sort)
75
- # Add into configurations unless its already there (initially done without locking for
76
- # speed)
77
- unless self.class.configurations.key? name
78
- self.class.connection_lock.synchronize do
79
- self.class.configurations[name] = connection \
80
- unless self.class.configurations.key? name
81
- end
82
- end
83
-
84
- name.to_sym
85
- else
86
- ::ActiveRecord::Base.connection_pool.spec.name.to_s
87
- end
88
-
89
- table_name = (options.delete(:table) || :moneta).to_sym
90
- create_table_proc = options.delete(:create_table)
91
- if create_table_proc == nil
92
- create_table(table_name)
93
- elsif create_table_proc
94
- with_connection(&create_table_proc)
95
- end
96
-
97
- @table = ::Arel::Table.new(table_name)
74
+ @table = ::Arel::Table.new(backend.table_name)
98
75
  end
99
76
  end
100
77
 
@@ -110,8 +87,8 @@ module Moneta
110
87
  # (see Proxy#each_key)
111
88
  def each_key(&block)
112
89
  with_connection do |conn|
113
- return enum_for(:each_key) { conn.select_value(arel_sel.project(table[key_column].count)) } unless block_given?
114
- conn.select_values(arel_sel.project(table[key_column])).each { |k| yield(k) }
90
+ return enum_for(:each_key) { conn.select_value(arel_sel.project(table[config.key_column].count)) } unless block_given?
91
+ conn.select_values(arel_sel.project(table[config.key_column])).each { |k| yield(k) }
115
92
  end
116
93
  self
117
94
  end
@@ -136,10 +113,10 @@ module Moneta
136
113
  def delete(key, options = {})
137
114
  with_connection do |conn|
138
115
  conn.transaction do
139
- sel = arel_sel_key(key).project(table[value_column]).lock
116
+ sel = arel_sel_key(key).project(table[config.value_column]).lock
140
117
  value = decode(conn, conn.select_value(sel))
141
118
 
142
- del = arel_del.where(table[key_column].eq(key))
119
+ del = arel_del.where(table[config.key_column].eq(key))
143
120
  conn.delete(del)
144
121
 
145
122
  value
@@ -155,7 +132,7 @@ module Moneta
155
132
  amount
156
133
  rescue ::ActiveRecord::RecordNotUnique
157
134
  conn.transaction do
158
- sel = arel_sel_key(key).project(table[value_column]).lock
135
+ sel = arel_sel_key(key).project(table[config.value_column]).lock
159
136
  value = decode(conn, conn.select_value(sel))
160
137
  value = (value ? Integer(value) : 0) + amount
161
138
  # Re-raise if the upate affects no rows (i.e. row deleted after attempted insert,
@@ -213,13 +190,13 @@ module Moneta
213
190
 
214
191
  sel = arel_sel
215
192
  .join(temp_table)
216
- .on(table[key_column].eq(temp_table[:key]))
217
- .project(table[key_column], table[value_column])
193
+ .on(table[config.key_column].eq(temp_table[:key]))
194
+ .project(table[config.key_column], table[config.value_column])
218
195
  sel = sel.lock if lock
219
196
  result = conn.select_all(sel)
220
197
 
221
- k = key_column.to_s
222
- v = value_column.to_s
198
+ k = config.key_column.to_s
199
+ v = config.value_column.to_s
223
200
  result.map do |row|
224
201
  [row[k], decode(conn, row[v])]
225
202
  end
@@ -275,7 +252,7 @@ module Moneta
275
252
  self.class.retrieve_or_establish_connection_pool(@spec)
276
253
  end
277
254
 
278
- def create_table(table_name)
255
+ def default_create_table(table_name)
279
256
  with_connection do |conn|
280
257
  return if conn.table_exists?(table_name)
281
258
 
@@ -283,10 +260,10 @@ module Moneta
283
260
  self.class.connection_lock.synchronize do
284
261
  conn.create_table(table_name, id: false) do |t|
285
262
  # Do not use binary key (Issue #17)
286
- t.string key_column, null: false
287
- t.binary value_column
263
+ t.string config.key_column, null: false
264
+ t.binary config.value_column
288
265
  end
289
- conn.add_index(table_name, key_column, unique: true)
266
+ conn.add_index(table_name, config.key_column, unique: true)
290
267
  end
291
268
  end
292
269
  end
@@ -304,21 +281,21 @@ module Moneta
304
281
  end
305
282
 
306
283
  def arel_sel_key(key)
307
- arel_sel.where(table[key_column].eq(key))
284
+ arel_sel.where(table[config.key_column].eq(key))
308
285
  end
309
286
 
310
287
  def conn_ins(conn, key, value)
311
288
  ins = ::Arel::InsertManager.new.into(table)
312
- ins.insert([[table[key_column], key], [table[value_column], value]])
289
+ ins.insert([[table[config.key_column], key], [table[config.value_column], value]])
313
290
  conn.insert ins
314
291
  end
315
292
 
316
293
  def conn_upd(conn, key, value)
317
- conn.update arel_upd.where(table[key_column].eq(key)).set([[table[value_column], value]])
294
+ conn.update arel_upd.where(table[config.key_column].eq(key)).set([[table[config.value_column], value]])
318
295
  end
319
296
 
320
297
  def conn_sel_value(conn, key)
321
- decode(conn, conn.select_value(arel_sel_key(key).project(table[value_column])))
298
+ decode(conn, conn.select_value(arel_sel_key(key).project(table[config.value_column])))
322
299
  end
323
300
 
324
301
  def encode(conn, value)
@@ -346,6 +323,38 @@ module Moneta
346
323
  value
347
324
  end
348
325
  end
326
+
327
+ # Feed the connection info into ActiveRecord and get back a name to use
328
+ # for getting the connection pool
329
+ def spec_for_connection(connection)
330
+ case connection
331
+ when Symbol
332
+ connection
333
+ when Hash, String
334
+ # Normalize the connection specification to a hash
335
+ resolver = ::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new \
336
+ 'dummy' => connection
337
+
338
+ # Turn the config into a standardised hash, sans a couple of bits
339
+ hash = resolver.resolve(:dummy)
340
+ hash.delete('name')
341
+ hash.delete(:password) # For security
342
+ # Make a name unique to this config
343
+ name = 'moneta?' + URI.encode_www_form(hash.to_a.sort)
344
+ # Add into configurations unless its already there (initially done without locking for
345
+ # speed)
346
+ unless self.class.configurations.key? name
347
+ self.class.connection_lock.synchronize do
348
+ self.class.configurations[name] = connection \
349
+ unless self.class.configurations.key? name
350
+ end
351
+ end
352
+
353
+ name.to_sym
354
+ else
355
+ ::ActiveRecord::Base.connection_pool.spec.name.to_s
356
+ end
357
+ end
349
358
  end
350
359
  end
351
360
  end
@@ -2,32 +2,23 @@ module Moneta
2
2
  module Adapters
3
3
  # ActiveSupport::Cache::Store adapter
4
4
  # @api public
5
- class ActiveSupportCache
6
- include Defaults
5
+ class ActiveSupportCache < Adapter
7
6
  include ExpiresSupport
8
7
 
9
8
  supports :increment
10
9
 
11
- # @param [Hash] options
12
- # @option options [Numeric] :expires default expiration in seconds
13
- def initialize(options = {})
14
- self.default_expires = options.delete(:expires)
15
- @backend =
16
- if options[:backend]
17
- options[:backend]
18
- elsif defined?(Rails)
19
- Rails.cache
20
- else
21
- raise ArgumentError, 'Option :backend is required'
22
- end
23
- end
10
+ # @!method initialize(options = {})
11
+ # @param [Hash] options
12
+ # @option options [ActiveSupport::Cache::Store] :backend (Rails.cache) Cache store to use
13
+ # @option options [Numeric] :expires default expiration in seconds
14
+ backend { Rails.cache if defined?(Rails) }
24
15
 
25
16
  # (see Proxy#key?)
26
17
  def key?(key, options = {})
27
- @backend.exist?(key).tap do |exists|
18
+ backend.exist?(key).tap do |exists|
28
19
  if exists && (expires = expires_value(options, nil)) != nil
29
- value = @backend.read(key, options)
30
- @backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
20
+ value = backend.read(key, options)
21
+ backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
31
22
  end
32
23
  end
33
24
  end
@@ -35,9 +26,9 @@ module Moneta
35
26
  # (see Proxy#load)
36
27
  def load(key, options = {})
37
28
  expires = expires_value(options, nil)
38
- value = @backend.read(key, options)
29
+ value = backend.read(key, options)
39
30
  if value and expires != nil
40
- @backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
31
+ backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
41
32
  end
42
33
  value
43
34
  end
@@ -45,7 +36,7 @@ module Moneta
45
36
  # (see Proxy#store)
46
37
  def store(key, value, options = {})
47
38
  expires = expires_value(options)
48
- @backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
39
+ backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
49
40
  value
50
41
  end
51
42
 
@@ -53,11 +44,11 @@ module Moneta
53
44
  def increment(key, amount = 1, options = {})
54
45
  expires = expires_value(options)
55
46
  options.delete(:raw)
56
- existing = Integer(@backend.fetch(key, options.merge(raw: true)) { 0 })
47
+ existing = Integer(backend.fetch(key, options.merge(raw: true)) { 0 })
57
48
  if amount > 0
58
- @backend.increment(key, amount, options.merge(expires_in: expires ? expires.seconds : nil))
49
+ backend.increment(key, amount, options.merge(expires_in: expires ? expires.seconds : nil))
59
50
  elsif amount < 0
60
- @backend.decrement(key, -amount, options.merge(expires_in: expires ? expires.seconds : nil))
51
+ backend.decrement(key, -amount, options.merge(expires_in: expires ? expires.seconds : nil))
61
52
  else
62
53
  existing
63
54
  end
@@ -65,25 +56,25 @@ module Moneta
65
56
 
66
57
  # (see Proxy#delete)
67
58
  def delete(key, options = {})
68
- value = @backend.read(key, options)
59
+ value = backend.read(key, options)
69
60
  if value != nil
70
- @backend.delete(key, options)
61
+ backend.delete(key, options)
71
62
  options[:raw] ? value.to_s : value
72
63
  end
73
64
  end
74
65
 
75
66
  # (see Proxy#clear)
76
67
  def clear(options = {})
77
- @backend.clear
68
+ backend.clear
78
69
  self
79
70
  end
80
71
 
81
72
  # (see Proxy#slice)
82
73
  def slice(*keys, **options)
83
- hash = @backend.read_multi(*keys)
74
+ hash = backend.read_multi(*keys)
84
75
  if (expires = expires_value(options, nil)) != nil
85
76
  hash.each do |key, value|
86
- @backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
77
+ backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
87
78
  end
88
79
  end
89
80
  if options[:raw]
@@ -115,13 +106,13 @@ module Moneta
115
106
 
116
107
  hash = Hash === pairs ? pairs : Hash[pairs.to_a]
117
108
  expires = expires_value(options)
118
- @backend.write_multi(hash, options.merge(expires_in: expires ? expires.seconds : nil))
109
+ backend.write_multi(hash, options.merge(expires_in: expires ? expires.seconds : nil))
119
110
  self
120
111
  end
121
112
 
122
113
  private
123
114
 
124
- def expires_value(options, default = @default_expires)
115
+ def expires_value(options, default = config.expires)
125
116
  super.tap { options.delete(:expires) unless options.frozen? }
126
117
  end
127
118
  end