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
@@ -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