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
@@ -4,14 +4,34 @@ module Moneta
|
|
4
4
|
module Adapters
|
5
5
|
# Cassandra backend
|
6
6
|
# @api public
|
7
|
-
class Cassandra
|
8
|
-
include Defaults
|
7
|
+
class Cassandra < Adapter
|
9
8
|
include ExpiresSupport
|
10
9
|
|
11
|
-
attr_reader :backend
|
12
|
-
|
13
10
|
supports :each_key
|
14
11
|
|
12
|
+
config :table, default: 'moneta'
|
13
|
+
config :key_column, default: 'key'
|
14
|
+
config :value_column, default: 'value'
|
15
|
+
config :updated_column, default: 'updated_at'
|
16
|
+
config :expired_column, default: 'expired'
|
17
|
+
config :read_consistency, default: :all
|
18
|
+
config :write_consistency, default: :all
|
19
|
+
|
20
|
+
backend do |keyspace: 'moneta', cluster: nil, create_keyspace: nil, **options|
|
21
|
+
cluster ||= ::Cassandra.cluster(options).tap do |own_cluster|
|
22
|
+
@own_cluster = own_cluster
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
cluster.connect(keyspace)
|
27
|
+
rescue ::Cassandra::Errors::InvalidError
|
28
|
+
backend = cluster.connect
|
29
|
+
create_keyspace(backend, keyspace, create_keyspace)
|
30
|
+
backend.execute("USE " + keyspace)
|
31
|
+
backend
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
15
35
|
# @param [Hash] options
|
16
36
|
# @option options [String] :keyspace ('moneta') Cassandra keyspace
|
17
37
|
# @option options [String] :table ('moneta') Cassandra table
|
@@ -35,39 +55,17 @@ module Moneta
|
|
35
55
|
# keyspace does not already exist.
|
36
56
|
# @option options [::Cassandra::Cluster] :cluster Existing cluster to use
|
37
57
|
# @option options [::Cassandra::Session] :backend Existing session to use
|
38
|
-
# @option options Other options passed to `Cassandra#
|
58
|
+
# @option options Other options passed to `Cassandra#cluster`
|
39
59
|
def initialize(options = {})
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@write_consistency = options.delete(:write_consistency) || :all
|
50
|
-
@create_keyspace = options.delete(:create_keyspace)
|
51
|
-
|
52
|
-
unless @backend = options.delete(:backend)
|
53
|
-
cluster = options.delete(:cluster) || (@own_cluster = ::Cassandra.cluster(options))
|
54
|
-
begin
|
55
|
-
@backend = cluster.connect(keyspace)
|
56
|
-
rescue ::Cassandra::Errors::InvalidError
|
57
|
-
@backend = cluster.connect
|
58
|
-
create_keyspace(keyspace)
|
59
|
-
@backend.execute("USE " + keyspace)
|
60
|
-
@backend
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
@backend.execute <<-CQL
|
65
|
-
CREATE TABLE IF NOT EXISTS #{@table} (
|
66
|
-
#{@key_column} blob,
|
67
|
-
#{@value_column} blob,
|
68
|
-
#{@updated_column} timeuuid,
|
69
|
-
#{@expired_column} boolean,
|
70
|
-
PRIMARY KEY (#{@key_column}, #{@updated_column})
|
60
|
+
super
|
61
|
+
|
62
|
+
backend.execute <<-CQL
|
63
|
+
CREATE TABLE IF NOT EXISTS #{config.table} (
|
64
|
+
#{config.key_column} blob,
|
65
|
+
#{config.value_column} blob,
|
66
|
+
#{config.updated_column} timeuuid,
|
67
|
+
#{config.expired_column} boolean,
|
68
|
+
PRIMARY KEY (#{config.key_column}, #{config.updated_column})
|
71
69
|
)
|
72
70
|
CQL
|
73
71
|
|
@@ -82,18 +80,18 @@ module Moneta
|
|
82
80
|
# whole column, when we want to update the expiry we load the value
|
83
81
|
# and then re-set it in order to update the TTL.
|
84
82
|
return false unless
|
85
|
-
row =
|
86
|
-
row[
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
83
|
+
row = backend.execute(@load, options.merge(consistency: rc, arguments: [key])).first and
|
84
|
+
row[config.expired_column] != nil
|
85
|
+
backend.execute(@update_expires,
|
86
|
+
options.merge(consistency: wc,
|
87
|
+
arguments: [(expires || 0).to_i,
|
88
|
+
timestamp,
|
89
|
+
row[config.value_column],
|
90
|
+
key,
|
91
|
+
row[config.updated_column]]))
|
94
92
|
true
|
95
|
-
elsif row =
|
96
|
-
row[
|
93
|
+
elsif row = backend.execute(@key, options.merge(consistency: rc, arguments: [key])).first
|
94
|
+
row[config.expired_column] != nil
|
97
95
|
else
|
98
96
|
false
|
99
97
|
end
|
@@ -102,17 +100,17 @@ module Moneta
|
|
102
100
|
# (see Proxy#load)
|
103
101
|
def load(key, options = {})
|
104
102
|
rc, wc = consistency(options)
|
105
|
-
if row =
|
103
|
+
if row = backend.execute(@load, options.merge(consistency: rc, arguments: [key])).first and row[config.expired_column] != nil
|
106
104
|
if (expires = expires_value(options, nil)) != nil
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
105
|
+
backend.execute(@update_expires,
|
106
|
+
options.merge(consistency: wc,
|
107
|
+
arguments: [(expires || 0).to_i,
|
108
|
+
timestamp,
|
109
|
+
row[config.value_column],
|
110
|
+
key,
|
111
|
+
row[config.updated_column]]))
|
114
112
|
end
|
115
|
-
row[
|
113
|
+
row[config.value_column]
|
116
114
|
end
|
117
115
|
end
|
118
116
|
|
@@ -121,33 +119,33 @@ module Moneta
|
|
121
119
|
_, wc = consistency(options)
|
122
120
|
expires = expires_value(options)
|
123
121
|
t = timestamp
|
124
|
-
batch =
|
122
|
+
batch = backend.batch do |batch|
|
125
123
|
batch.add(@store_delete, arguments: [t, key])
|
126
124
|
batch.add(@store, arguments: [key, value, (expires || 0).to_i, t + 1])
|
127
125
|
end
|
128
|
-
|
126
|
+
backend.execute(batch, options.merge(consistency: wc))
|
129
127
|
value
|
130
128
|
end
|
131
129
|
|
132
130
|
# (see Proxy#delete)
|
133
131
|
def delete(key, options = {})
|
134
132
|
rc, wc = consistency(options)
|
135
|
-
result =
|
136
|
-
if row = result.first and row[
|
137
|
-
|
138
|
-
row[
|
133
|
+
result = backend.execute(@delete_value, options.merge(consistency: rc, arguments: [key]))
|
134
|
+
if row = result.first and row[config.expired_column] != nil
|
135
|
+
backend.execute(@delete, options.merge(consistency: wc, arguments: [timestamp, key, row[config.updated_column]]))
|
136
|
+
row[config.value_column]
|
139
137
|
end
|
140
138
|
end
|
141
139
|
|
142
140
|
# (see Proxy#clear)
|
143
141
|
def clear(options = {})
|
144
|
-
|
142
|
+
backend.execute(@clear)
|
145
143
|
self
|
146
144
|
end
|
147
145
|
|
148
146
|
# (see Proxy#close)
|
149
147
|
def close
|
150
|
-
|
148
|
+
backend.close_async
|
151
149
|
@backend = nil
|
152
150
|
if @own_cluster
|
153
151
|
@own_cluster.close_async
|
@@ -160,11 +158,11 @@ module Moneta
|
|
160
158
|
def each_key
|
161
159
|
rc, = consistency
|
162
160
|
return enum_for(:each_key) unless block_given?
|
163
|
-
result =
|
161
|
+
result = backend.execute(@each_key, consistency: rc, page_size: 100)
|
164
162
|
loop do
|
165
163
|
result.each do |row|
|
166
|
-
next if row[
|
167
|
-
yield row[
|
164
|
+
next if row[config.expired_column] == nil
|
165
|
+
yield row[config.key_column]
|
168
166
|
end
|
169
167
|
|
170
168
|
break if result.last_page?
|
@@ -176,27 +174,27 @@ module Moneta
|
|
176
174
|
# (see Proxy#slice)
|
177
175
|
def slice(*keys, **options)
|
178
176
|
rc, wc = consistency(options)
|
179
|
-
result =
|
177
|
+
result = backend.execute(@slice, options.merge(consistency: rc, arguments: [keys]))
|
180
178
|
expires = expires_value(options, nil)
|
181
179
|
updated = [] if expires != nil
|
182
180
|
pairs = result.map do |row|
|
183
|
-
next if row[
|
181
|
+
next if row[config.expired_column] == nil
|
184
182
|
if expires != nil
|
185
|
-
updated << [row[
|
183
|
+
updated << [row[config.key_column], row[config.value_column], row[config.updated_column]]
|
186
184
|
end
|
187
|
-
[row[
|
185
|
+
[row[config.key_column], row[config.value_column]]
|
188
186
|
end.compact
|
189
187
|
|
190
188
|
if expires != nil && !updated.empty?
|
191
189
|
ttl = (expires || 0).to_i
|
192
190
|
t = timestamp
|
193
|
-
batch =
|
191
|
+
batch = backend.batch do |batch|
|
194
192
|
updated.each do |key, value, updated|
|
195
193
|
batch.add(@update_expires, arguments: [ttl, t, value, key, updated])
|
196
194
|
end
|
197
195
|
end
|
198
196
|
|
199
|
-
|
197
|
+
backend.execute(batch, options.merge(consistency: wc))
|
200
198
|
end
|
201
199
|
|
202
200
|
pairs
|
@@ -240,13 +238,13 @@ module Moneta
|
|
240
238
|
_rc, wc = consistency(options)
|
241
239
|
expires = expires_value(options)
|
242
240
|
t = timestamp
|
243
|
-
batch =
|
241
|
+
batch = backend.batch do |batch|
|
244
242
|
batch.add(@merge_delete, arguments: [t, keys])
|
245
243
|
pairs.each do |key, value|
|
246
244
|
batch.add(@store, arguments: [key, value, (expires || 0).to_i, t + 1])
|
247
245
|
end
|
248
246
|
end
|
249
|
-
|
247
|
+
backend.execute(batch, options.merge(consistency: wc))
|
250
248
|
|
251
249
|
self
|
252
250
|
end
|
@@ -257,7 +255,7 @@ module Moneta
|
|
257
255
|
(Time.now.to_r * 1_000_000).to_i
|
258
256
|
end
|
259
257
|
|
260
|
-
def create_keyspace(keyspace)
|
258
|
+
def create_keyspace(backend, keyspace, create_keyspace)
|
261
259
|
options = {
|
262
260
|
replication: {
|
263
261
|
class: 'SimpleStrategy',
|
@@ -265,13 +263,13 @@ module Moneta
|
|
265
263
|
}
|
266
264
|
}
|
267
265
|
|
268
|
-
case
|
266
|
+
case create_keyspace
|
269
267
|
when Proc
|
270
|
-
return
|
268
|
+
return create_keyspace.call(keyspace)
|
271
269
|
when false
|
272
270
|
return
|
273
271
|
when Hash
|
274
|
-
options.merge!(
|
272
|
+
options.merge!(create_keyspace)
|
275
273
|
end
|
276
274
|
|
277
275
|
# This is a bit hacky, but works. Options in Cassandra look like JSON,
|
@@ -281,7 +279,7 @@ module Moneta
|
|
281
279
|
key.to_s + ' = ' + MultiJson.dump(value).tr(?", ?')
|
282
280
|
end.join(' AND ')
|
283
281
|
|
284
|
-
|
282
|
+
backend.execute "CREATE KEYSPACE IF NOT EXISTS %<keyspace>s WITH %<options>s" % {
|
285
283
|
keyspace: keyspace,
|
286
284
|
options: option_str
|
287
285
|
}
|
@@ -291,65 +289,65 @@ module Moneta
|
|
291
289
|
end
|
292
290
|
|
293
291
|
def prepare_statements
|
294
|
-
@key =
|
295
|
-
SELECT #{
|
296
|
-
FROM #{
|
292
|
+
@key = backend.prepare(<<-CQL)
|
293
|
+
SELECT #{config.updated_column}, #{config.expired_column}
|
294
|
+
FROM #{config.table} WHERE #{config.key_column} = ?
|
297
295
|
LIMIT 1
|
298
296
|
CQL
|
299
|
-
@store_delete =
|
300
|
-
DELETE FROM #{
|
297
|
+
@store_delete = backend.prepare(<<-CQL)
|
298
|
+
DELETE FROM #{config.table}
|
301
299
|
USING TIMESTAMP ?
|
302
|
-
WHERE #{
|
300
|
+
WHERE #{config.key_column} = ?
|
303
301
|
CQL
|
304
|
-
@store =
|
305
|
-
INSERT INTO #{
|
302
|
+
@store = backend.prepare(<<-CQL)
|
303
|
+
INSERT INTO #{config.table} (#{config.key_column}, #{config.value_column}, #{config.updated_column}, #{config.expired_column})
|
306
304
|
VALUES (?, ?, now(), false)
|
307
305
|
USING TTL ? AND TIMESTAMP ?
|
308
306
|
CQL
|
309
|
-
@load =
|
310
|
-
SELECT #{
|
311
|
-
FROM #{
|
312
|
-
WHERE #{
|
307
|
+
@load = backend.prepare(<<-CQL)
|
308
|
+
SELECT #{config.value_column}, #{config.updated_column}, #{config.expired_column}
|
309
|
+
FROM #{config.table}
|
310
|
+
WHERE #{config.key_column} = ?
|
313
311
|
LIMIT 1
|
314
312
|
CQL
|
315
|
-
@update_expires =
|
316
|
-
UPDATE #{
|
313
|
+
@update_expires = backend.prepare(<<-CQL)
|
314
|
+
UPDATE #{config.table}
|
317
315
|
USING TTL ? AND TIMESTAMP ?
|
318
|
-
SET #{
|
319
|
-
WHERE #{
|
316
|
+
SET #{config.value_column} = ?, #{config.expired_column} = false
|
317
|
+
WHERE #{config.key_column} = ? AND #{config.updated_column} = ?
|
320
318
|
CQL
|
321
|
-
@clear =
|
322
|
-
@delete_value =
|
323
|
-
SELECT #{
|
324
|
-
FROM #{
|
325
|
-
WHERE #{
|
319
|
+
@clear = backend.prepare("TRUNCATE #{config.table}")
|
320
|
+
@delete_value = backend.prepare(<<-CQL)
|
321
|
+
SELECT #{config.value_column}, #{config.updated_column}, #{config.expired_column}
|
322
|
+
FROM #{config.table}
|
323
|
+
WHERE #{config.key_column} = ?
|
326
324
|
LIMIT 1
|
327
325
|
CQL
|
328
|
-
@delete =
|
329
|
-
DELETE FROM #{
|
326
|
+
@delete = backend.prepare(<<-CQL, idempotent: true)
|
327
|
+
DELETE FROM #{config.table}
|
330
328
|
USING TIMESTAMP ?
|
331
|
-
WHERE #{
|
329
|
+
WHERE #{config.key_column} = ? AND #{config.updated_column} = ?
|
332
330
|
CQL
|
333
|
-
@each_key =
|
334
|
-
SELECT #{
|
335
|
-
FROM #{
|
331
|
+
@each_key = backend.prepare(<<-CQL)
|
332
|
+
SELECT #{config.key_column}, #{config.expired_column}
|
333
|
+
FROM #{config.table}
|
336
334
|
CQL
|
337
|
-
@slice =
|
338
|
-
SELECT #{
|
339
|
-
FROM #{
|
340
|
-
WHERE #{
|
335
|
+
@slice = backend.prepare(<<-CQL)
|
336
|
+
SELECT #{config.key_column}, #{config.value_column}, #{config.updated_column}, #{config.expired_column}
|
337
|
+
FROM #{config.table}
|
338
|
+
WHERE #{config.key_column} IN ?
|
341
339
|
CQL
|
342
|
-
@merge_delete =
|
343
|
-
DELETE FROM #{
|
340
|
+
@merge_delete = backend.prepare(<<-CQL)
|
341
|
+
DELETE FROM #{config.table}
|
344
342
|
USING TIMESTAMP ?
|
345
|
-
WHERE #{
|
343
|
+
WHERE #{config.key_column} IN ?
|
346
344
|
CQL
|
347
345
|
end
|
348
346
|
|
349
347
|
def consistency(options = {})
|
350
348
|
[
|
351
|
-
options[:read_consistency] ||
|
352
|
-
options[:write_consistency] ||
|
349
|
+
options[:read_consistency] || config.read_consistency,
|
350
|
+
options[:write_consistency] || config.write_consistency
|
353
351
|
]
|
354
352
|
end
|
355
353
|
end
|
@@ -4,20 +4,19 @@ module Moneta
|
|
4
4
|
module Adapters
|
5
5
|
# Moneta client backend
|
6
6
|
# @api public
|
7
|
-
class Client
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
7
|
+
class Client < Adapter
|
8
|
+
# @!method initialize(options = {})
|
9
|
+
# @param [Hash] options
|
10
|
+
# @option options [TCPSocket | UNIXSocket] :backend an open socket to use
|
11
|
+
# @option options [Integer] :port (9000) TCP port
|
12
|
+
# @option options [String] :host ('127.0.0.1') Hostname
|
13
|
+
# @option options [String] :socket Unix socket file name as alternative to `:port` and `:host`
|
14
|
+
backend do |socket: nil, host: '127.0.0.1', port: 9000|
|
15
|
+
if socket
|
16
|
+
UNIXSocket.open(socket)
|
17
|
+
else
|
18
|
+
TCPSocket.open(host, port)
|
19
|
+
end
|
21
20
|
end
|
22
21
|
|
23
22
|
# (see Proxy#key?)
|
@@ -66,7 +65,7 @@ module Moneta
|
|
66
65
|
|
67
66
|
# (see Proxy#close)
|
68
67
|
def close
|
69
|
-
|
68
|
+
backend.close
|
70
69
|
nil
|
71
70
|
end
|
72
71
|
|
@@ -114,19 +113,19 @@ module Moneta
|
|
114
113
|
|
115
114
|
def write(*args)
|
116
115
|
s = Marshal.dump(args)
|
117
|
-
|
116
|
+
backend.write([s.bytesize].pack('N') << s)
|
118
117
|
end
|
119
118
|
|
120
119
|
# JRuby doesn't support socket#recv with flags
|
121
120
|
if defined?(JRUBY_VERSION)
|
122
121
|
def read(bytes)
|
123
|
-
received =
|
122
|
+
received = backend.read(bytes)
|
124
123
|
raise EOFError, "Server closed socket" unless received && received.bytesize == bytes
|
125
124
|
received
|
126
125
|
end
|
127
126
|
else
|
128
127
|
def read(bytes)
|
129
|
-
received =
|
128
|
+
received = backend.recv(bytes, Socket::MSG_WAITALL)
|
130
129
|
raise EOFError, "Server closed socket" unless received && received.bytesize == bytes
|
131
130
|
received
|
132
131
|
end
|
@@ -12,9 +12,7 @@ module Moneta
|
|
12
12
|
# db['key'] = {a: 1, b: 2}
|
13
13
|
#
|
14
14
|
# @api public
|
15
|
-
class Couch
|
16
|
-
include Defaults
|
17
|
-
|
15
|
+
class Couch < Adapter
|
18
16
|
# @api private
|
19
17
|
class HTTPError < StandardError
|
20
18
|
attr_reader :status, :request_method, :key
|
@@ -28,10 +26,21 @@ module Moneta
|
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
31
|
-
attr_reader :backend
|
32
|
-
|
33
29
|
supports :create, :each_key
|
34
30
|
|
31
|
+
config :value_field, default: 'value'
|
32
|
+
config :type_field, default: 'type'
|
33
|
+
config :login
|
34
|
+
config :password
|
35
|
+
config :adapter
|
36
|
+
config :skip_create_db
|
37
|
+
|
38
|
+
backend do |scheme: 'http', host: '127.0.0.1', port: 5984, db: 'moneta', adapter: nil, **options|
|
39
|
+
::Faraday.new "#{scheme}://#{host}:#{port}/#{db}", options do |faraday|
|
40
|
+
faraday.adapter adapter if adapter
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
35
44
|
# @param [Hash] options
|
36
45
|
# @option options [String] :host ('127.0.0.1') Couch host
|
37
46
|
# @option options [String] :port (5984) Couch port
|
@@ -46,26 +55,22 @@ module Moneta
|
|
46
55
|
# @option options Other options passed to {Faraday::new} (unless
|
47
56
|
# :backend option is provided).
|
48
57
|
def initialize(options = {})
|
49
|
-
|
50
|
-
|
51
|
-
login
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
block = if faraday_adapter = options.delete(:adapter)
|
59
|
-
proc { |faraday| faraday.adapter(faraday_adapter) }
|
60
|
-
end
|
61
|
-
::Faraday.new("#{scheme}://#{host}:#{port}/#{db}", options, &block)
|
58
|
+
super
|
59
|
+
|
60
|
+
if config.login && config.password
|
61
|
+
# Faraday 1.x had a `basic_auth` function
|
62
|
+
if backend.respond_to? :basic_auth
|
63
|
+
backend.basic_auth(config.login, config.password)
|
64
|
+
else
|
65
|
+
backend.request :authorization, :basic, config.login, config.password
|
66
|
+
end
|
62
67
|
end
|
63
|
-
|
68
|
+
|
64
69
|
@rev_cache = Moneta.build do
|
65
70
|
use :Lock
|
66
71
|
adapter :LRUHash
|
67
72
|
end
|
68
|
-
create_db
|
73
|
+
create_db unless config.skip_create_db
|
69
74
|
end
|
70
75
|
|
71
76
|
# (see Proxy#key?)
|
@@ -278,15 +283,15 @@ module Moneta
|
|
278
283
|
end
|
279
284
|
|
280
285
|
def doc_to_value(doc)
|
281
|
-
case doc[
|
286
|
+
case doc[config.type_field]
|
282
287
|
when 'Hash'
|
283
288
|
doc = doc.dup
|
284
289
|
doc.delete('_id')
|
285
290
|
doc.delete('_rev')
|
286
|
-
doc.delete(
|
291
|
+
doc.delete(config.type_field)
|
287
292
|
doc
|
288
293
|
else
|
289
|
-
doc[
|
294
|
+
doc[config.value_field]
|
290
295
|
end
|
291
296
|
end
|
292
297
|
|
@@ -294,11 +299,11 @@ module Moneta
|
|
294
299
|
doc =
|
295
300
|
case value
|
296
301
|
when Hash
|
297
|
-
value.merge(
|
302
|
+
value.merge(config.type_field => 'Hash')
|
298
303
|
when String
|
299
|
-
{
|
304
|
+
{ config.value_field => value, config.type_field => 'String' }
|
300
305
|
when Float, Integer
|
301
|
-
{
|
306
|
+
{ config.value_field => value, config.type_field => 'Number' }
|
302
307
|
else
|
303
308
|
raise ArgumentError, "Invalid value type: #{value.class}"
|
304
309
|
end
|
@@ -7,6 +7,7 @@ module Moneta
|
|
7
7
|
# @api public
|
8
8
|
class DataMapper
|
9
9
|
include Defaults
|
10
|
+
include Config
|
10
11
|
include NilValues
|
11
12
|
|
12
13
|
supports :create
|
@@ -19,15 +20,18 @@ module Moneta
|
|
19
20
|
self.raise_on_save_failure = true
|
20
21
|
end
|
21
22
|
|
23
|
+
config :setup, required: true
|
24
|
+
config :repository, default: :moneta, coerce: :to_sym
|
25
|
+
config :table, default: :moneta, coerce: :to_sym
|
26
|
+
|
22
27
|
# @param [Hash] options
|
23
28
|
# @option options [String] :setup Datamapper setup string
|
24
29
|
# @option options [String/Symbol] :repository (:moneta) Repository name
|
25
30
|
# @option options [String/Symbol] :table (:moneta) Table name
|
26
31
|
def initialize(options = {})
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
::DataMapper.setup(@repository, options[:setup])
|
32
|
+
configure(options)
|
33
|
+
Store.storage_names[config.repository] = config.table.to_s
|
34
|
+
::DataMapper.setup(config.repository, config.setup)
|
31
35
|
context { Store.auto_upgrade! }
|
32
36
|
end
|
33
37
|
|
@@ -91,7 +95,7 @@ module Moneta
|
|
91
95
|
private
|
92
96
|
|
93
97
|
def context
|
94
|
-
::DataMapper.repository(
|
98
|
+
::DataMapper.repository(config.repository) { yield }
|
95
99
|
end
|
96
100
|
end
|
97
101
|
end
|
@@ -4,58 +4,52 @@ module Moneta
|
|
4
4
|
module Adapters
|
5
5
|
# Daybreak backend
|
6
6
|
# @api public
|
7
|
-
class Daybreak
|
8
|
-
include Defaults
|
7
|
+
class Daybreak < Adapter
|
9
8
|
include DBMAdapter
|
10
9
|
include IncrementSupport
|
11
10
|
include CreateSupport
|
12
11
|
include EachKeySupport
|
13
12
|
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
begin
|
20
|
-
raise ArgumentError, 'Option :file is required' unless options[:file]
|
21
|
-
::Daybreak::DB.new(options[:file], serializer: ::Daybreak::Serializer::None)
|
22
|
-
end
|
23
|
-
end
|
13
|
+
# @!method initialize(options = {})
|
14
|
+
# @param [Hash] options
|
15
|
+
# @option options [String] :file Database file
|
16
|
+
# @option options [::Daybreak] :backend Use existing backend instance
|
17
|
+
backend { |file:| ::Daybreak::DB.new(file, serializer: ::Daybreak::Serializer::None) }
|
24
18
|
|
25
19
|
# (see Proxy#load)
|
26
20
|
def load(key, options = {})
|
27
|
-
|
28
|
-
|
21
|
+
backend.load if options[:sync]
|
22
|
+
backend[key]
|
29
23
|
end
|
30
24
|
|
31
25
|
# (see Proxy#store)
|
32
26
|
def store(key, value, options = {})
|
33
|
-
|
34
|
-
|
27
|
+
backend[key] = value
|
28
|
+
backend.flush if options[:sync]
|
35
29
|
value
|
36
30
|
end
|
37
31
|
|
38
32
|
# (see Proxy#increment)
|
39
33
|
def increment(key, amount = 1, options = {})
|
40
|
-
|
34
|
+
backend.lock { super }
|
41
35
|
end
|
42
36
|
|
43
37
|
# (see Proxy#create)
|
44
38
|
def create(key, value, options = {})
|
45
|
-
|
39
|
+
backend.lock { super }
|
46
40
|
end
|
47
41
|
|
48
42
|
# (see Proxy#merge!)
|
49
43
|
def merge!(pairs, options = {})
|
50
44
|
if block_given?
|
51
|
-
|
52
|
-
|
45
|
+
backend.lock do
|
46
|
+
backend.update(pairs.map do |key, new_value|
|
53
47
|
new_value = yield(key, load(key), new_value) if key?(key)
|
54
48
|
[key, new_value]
|
55
49
|
end)
|
56
50
|
end
|
57
51
|
else
|
58
|
-
|
52
|
+
backend.update(pairs)
|
59
53
|
end
|
60
54
|
|
61
55
|
self
|