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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +35 -38
- data/CHANGES +9 -0
- data/CONTRIBUTORS +2 -0
- data/Gemfile +143 -55
- data/README.md +5 -4
- data/lib/moneta/adapter.rb +52 -0
- data/lib/moneta/adapters/activerecord.rb +77 -68
- data/lib/moneta/adapters/activesupportcache.rb +22 -31
- data/lib/moneta/adapters/cassandra.rb +114 -116
- data/lib/moneta/adapters/client.rb +17 -18
- data/lib/moneta/adapters/couch.rb +31 -26
- data/lib/moneta/adapters/datamapper.rb +9 -5
- data/lib/moneta/adapters/daybreak.rb +15 -21
- data/lib/moneta/adapters/dbm.rb +6 -12
- data/lib/moneta/adapters/file.rb +21 -13
- data/lib/moneta/adapters/fog.rb +5 -6
- data/lib/moneta/adapters/gdbm.rb +6 -12
- data/lib/moneta/adapters/hbase.rb +10 -12
- data/lib/moneta/adapters/kyotocabinet.rb +22 -27
- data/lib/moneta/adapters/leveldb.rb +14 -20
- data/lib/moneta/adapters/lmdb.rb +19 -22
- data/lib/moneta/adapters/localmemcache.rb +7 -13
- data/lib/moneta/adapters/lruhash.rb +20 -20
- data/lib/moneta/adapters/memcached/dalli.rb +25 -33
- data/lib/moneta/adapters/memcached/native.rb +14 -20
- data/lib/moneta/adapters/memory.rb +5 -7
- data/lib/moneta/adapters/mongo.rb +53 -52
- data/lib/moneta/adapters/pstore.rb +21 -27
- data/lib/moneta/adapters/redis.rb +42 -37
- data/lib/moneta/adapters/restclient.rb +17 -25
- data/lib/moneta/adapters/riak.rb +8 -9
- data/lib/moneta/adapters/sdbm.rb +6 -12
- data/lib/moneta/adapters/sequel/mysql.rb +8 -8
- data/lib/moneta/adapters/sequel/postgres.rb +17 -17
- data/lib/moneta/adapters/sequel/postgres_hstore.rb +47 -47
- data/lib/moneta/adapters/sequel/sqlite.rb +9 -9
- data/lib/moneta/adapters/sequel.rb +56 -65
- data/lib/moneta/adapters/sqlite.rb +37 -35
- data/lib/moneta/adapters/tdb.rb +8 -14
- data/lib/moneta/adapters/tokyocabinet.rb +19 -17
- data/lib/moneta/adapters/tokyotyrant.rb +29 -30
- data/lib/moneta/adapters/yaml.rb +1 -5
- data/lib/moneta/config.rb +101 -0
- data/lib/moneta/expires.rb +0 -1
- data/lib/moneta/expires_support.rb +3 -4
- data/lib/moneta/pool.rb +1 -1
- data/lib/moneta/proxy.rb +29 -0
- data/lib/moneta/server.rb +21 -14
- data/lib/moneta/version.rb +1 -1
- data/lib/moneta/wrapper.rb +5 -0
- data/lib/moneta.rb +2 -0
- data/moneta.gemspec +1 -0
- data/spec/moneta/adapters/client/client_helper.rb +4 -3
- data/spec/moneta/adapters/faraday_helper.rb +3 -2
- data/spec/moneta/adapters/lruhash/adapter_lruhash_spec.rb +10 -6
- data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +2 -2
- data/spec/moneta/config_spec.rb +219 -0
- data/spec/moneta/proxies/transformer/transformer_bson_spec.rb +3 -1
- data/spec/moneta/proxies/transformer/transformer_marshal_escape_spec.rb +2 -0
- data/spec/rack/session_moneta_spec.rb +44 -25
- data/spec/restserver.rb +3 -14
- 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
|
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]
|
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
|
-
|
51
|
-
@value_column = options.delete(:value_column) || :v
|
69
|
+
super
|
52
70
|
|
53
|
-
|
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
|
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
|
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
|
-
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
18
|
+
backend.exist?(key).tap do |exists|
|
28
19
|
if exists && (expires = expires_value(options, nil)) != nil
|
29
|
-
value =
|
30
|
-
|
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 =
|
29
|
+
value = backend.read(key, options)
|
39
30
|
if value and expires != nil
|
40
|
-
|
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
|
-
|
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(
|
47
|
+
existing = Integer(backend.fetch(key, options.merge(raw: true)) { 0 })
|
57
48
|
if amount > 0
|
58
|
-
|
49
|
+
backend.increment(key, amount, options.merge(expires_in: expires ? expires.seconds : nil))
|
59
50
|
elsif amount < 0
|
60
|
-
|
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 =
|
59
|
+
value = backend.read(key, options)
|
69
60
|
if value != nil
|
70
|
-
|
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
|
-
|
68
|
+
backend.clear
|
78
69
|
self
|
79
70
|
end
|
80
71
|
|
81
72
|
# (see Proxy#slice)
|
82
73
|
def slice(*keys, **options)
|
83
|
-
hash =
|
74
|
+
hash = backend.read_multi(*keys)
|
84
75
|
if (expires = expires_value(options, nil)) != nil
|
85
76
|
hash.each do |key, value|
|
86
|
-
|
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
|
-
|
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 =
|
115
|
+
def expires_value(options, default = config.expires)
|
125
116
|
super.tap { options.delete(:expires) unless options.frozen? }
|
126
117
|
end
|
127
118
|
end
|