moneta 1.3.0 → 1.4.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/.rubocop.yml +26 -8
- data/CHANGES +6 -0
- data/CONTRIBUTORS +2 -1
- data/Gemfile +7 -5
- data/README.md +2 -6
- data/feature_matrix.yaml +0 -10
- data/lib/moneta.rb +9 -9
- data/lib/moneta/adapters/mongo.rb +256 -7
- data/lib/moneta/adapters/redis.rb +5 -1
- data/lib/moneta/adapters/sequel.rb +45 -464
- data/lib/moneta/adapters/sequel/mysql.rb +66 -0
- data/lib/moneta/adapters/sequel/postgres.rb +80 -0
- data/lib/moneta/adapters/sequel/postgres_hstore.rb +240 -0
- data/lib/moneta/adapters/sequel/sqlite.rb +57 -0
- data/lib/moneta/adapters/sqlite.rb +7 -7
- data/lib/moneta/create_support.rb +21 -0
- data/lib/moneta/dbm_adapter.rb +31 -0
- data/lib/moneta/{mixins.rb → defaults.rb} +1 -302
- data/lib/moneta/each_key_support.rb +27 -0
- data/lib/moneta/expires_support.rb +60 -0
- data/lib/moneta/hash_adapter.rb +68 -0
- data/lib/moneta/increment_support.rb +16 -0
- data/lib/moneta/nil_values.rb +35 -0
- data/lib/moneta/option_support.rb +51 -0
- data/lib/moneta/transformer/helper/bson.rb +5 -15
- data/lib/moneta/version.rb +1 -1
- data/lib/rack/cache/moneta.rb +14 -15
- data/moneta.gemspec +7 -9
- data/script/benchmarks +1 -2
- data/script/contributors +11 -6
- data/spec/active_support/cache_moneta_store_spec.rb +27 -29
- data/spec/features/concurrent_increment.rb +2 -3
- data/spec/features/create_expires.rb +15 -15
- data/spec/features/default_expires.rb +11 -12
- data/spec/features/expires.rb +215 -210
- data/spec/helper.rb +16 -33
- data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +16 -1
- data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +1 -1
- data/spec/moneta/adapters/mongo/standard_mongo_spec.rb +1 -1
- data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +7 -34
- data/spec/moneta/adapters/sequel/helper.rb +37 -0
- data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +4 -10
- data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +7 -8
- data/spec/moneta/proxies/shared/shared_unix_spec.rb +10 -0
- data/spec/restserver.rb +15 -0
- metadata +39 -58
- data/lib/moneta/adapters/mongo/base.rb +0 -103
- data/lib/moneta/adapters/mongo/moped.rb +0 -166
- data/lib/moneta/adapters/mongo/official.rb +0 -156
- data/spec/moneta/adapters/mongo/adapter_mongo_moped_spec.rb +0 -26
- data/spec/moneta/adapters/mongo/adapter_mongo_moped_with_default_expires_spec.rb +0 -14
- data/spec/moneta/adapters/mongo/adapter_mongo_official_spec.rb +0 -27
- data/spec/moneta/adapters/mongo/adapter_mongo_official_with_default_expires_spec.rb +0 -14
- data/spec/moneta/adapters/mongo/standard_mongo_moped_spec.rb +0 -7
- data/spec/moneta/adapters/mongo/standard_mongo_official_spec.rb +0 -7
- data/spec/quality_spec.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 667e187cf3e0d0ce7e721e07036f70e0b65f1b52818fc09eb87311872896ce95
|
4
|
+
data.tar.gz: 7e9063dfa4ea6f1bfbe4bee54821c75e9a82d0f54f3f820c6e396fafeeba006a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e2d3880c0d3fed1ee66cb57f5ebba13a83dd7e4447ec1e99e601a85ff9bc454cf7bf4e9d62c8672324faddf9f396e4488c08400a08e1c1dd0181056655142b5
|
7
|
+
data.tar.gz: 4be30ddd9249b7222c2354984815efde88ca3c8cfe65db26c13df1585bd80425cc8f55f68b869d07016183be29b4ae8495a3408fe3b67e0f27d21ef2f6de6a83
|
data/.rubocop.yml
CHANGED
@@ -6,11 +6,7 @@ AllCops:
|
|
6
6
|
- script/**/*
|
7
7
|
- vendor/**/*
|
8
8
|
|
9
|
-
Layout/
|
10
|
-
Exclude:
|
11
|
-
- lib/moneta/transformer/config.rb
|
12
|
-
|
13
|
-
Layout/AlignHash:
|
9
|
+
Layout/ArrayAlignment:
|
14
10
|
Exclude:
|
15
11
|
- lib/moneta/transformer/config.rb
|
16
12
|
|
@@ -25,6 +21,13 @@ Layout/ExtraSpacing:
|
|
25
21
|
- lib/moneta.rb
|
26
22
|
- lib/moneta/transformer/config.rb
|
27
23
|
|
24
|
+
Layout/HashAlignment:
|
25
|
+
Exclude:
|
26
|
+
- lib/moneta/transformer/config.rb
|
27
|
+
|
28
|
+
Layout/LineLength:
|
29
|
+
Max: 160
|
30
|
+
|
28
31
|
Layout/MultilineMethodCallIndentation:
|
29
32
|
EnforcedStyle: indented
|
30
33
|
|
@@ -38,12 +41,18 @@ Layout/SpaceInsideArrayLiteralBrackets:
|
|
38
41
|
Lint/AssignmentInCondition:
|
39
42
|
Enabled: false
|
40
43
|
|
44
|
+
Lint/RaiseException:
|
45
|
+
Enabled: true
|
46
|
+
|
41
47
|
Lint/ShadowedException:
|
42
48
|
Enabled: false
|
43
49
|
|
44
50
|
Lint/ShadowingOuterLocalVariable:
|
45
51
|
Enabled: false
|
46
52
|
|
53
|
+
Lint/StructNewOverride:
|
54
|
+
Enabled: true
|
55
|
+
|
47
56
|
Lint/UnusedMethodArgument:
|
48
57
|
Enabled: false
|
49
58
|
|
@@ -67,12 +76,12 @@ Metrics/BlockLength:
|
|
67
76
|
Exclude:
|
68
77
|
- spec/**/*
|
69
78
|
|
70
|
-
Metrics/LineLength:
|
71
|
-
Max: 160
|
72
|
-
|
73
79
|
Metrics/MethodLength:
|
74
80
|
Enabled: false
|
75
81
|
|
82
|
+
Metrics/ModuleLength:
|
83
|
+
Enabled: false
|
84
|
+
|
76
85
|
Metrics/ParameterLists:
|
77
86
|
Enabled: false
|
78
87
|
|
@@ -117,6 +126,15 @@ Style/FormatString:
|
|
117
126
|
Style/GuardClause:
|
118
127
|
Enabled: false
|
119
128
|
|
129
|
+
Style/HashEachMethods:
|
130
|
+
Enabled: true
|
131
|
+
|
132
|
+
Style/HashTransformKeys:
|
133
|
+
Enabled: true
|
134
|
+
|
135
|
+
Style/HashTransformValues:
|
136
|
+
Enabled: true
|
137
|
+
|
120
138
|
Style/IfUnlessModifier:
|
121
139
|
Enabled: false
|
122
140
|
|
data/CHANGES
CHANGED
data/CONTRIBUTORS
CHANGED
@@ -5,8 +5,8 @@ Alessio Signorini <alessio@signorini.us>
|
|
5
5
|
Anthony Eden <anthonyeden@gmail.com>
|
6
6
|
Atoxhybrid <atoxhybrid@gmail.com>
|
7
7
|
AtoxIO <atoxhybrid@gmail.com>
|
8
|
-
Benjamin Yu <benjaminlyu@gmail.com>
|
9
8
|
Ben Schwarz <ben.schwarz@gmail.com>
|
9
|
+
Benjamin Yu <benjaminlyu@gmail.com>
|
10
10
|
Daniel Mendler <mail@daniel-mendler.de>
|
11
11
|
Denis Defreyne <denis.defreyne@stoneship.org>
|
12
12
|
Derek Kastner <dkastner@gmail.com>
|
@@ -18,6 +18,7 @@ Jari Bakken <jari.bakken@gmail.com>
|
|
18
18
|
Jay Mitchell <jaybmitchell@gmail.com>
|
19
19
|
Jeremy Voorhis <jvoorhis@gmail.com>
|
20
20
|
Jon Crosby <jon@joncrosby.me>
|
21
|
+
Jonathan Gnagy <jonathan.l.gnagy@sherwin.com>
|
21
22
|
lakshan <lakshan@web2media.net>
|
22
23
|
Mal McKay <mal.mckay@gmail.com>
|
23
24
|
Marek Skrobacki <skrobul@skrobul.com>
|
data/Gemfile
CHANGED
@@ -14,7 +14,7 @@ gem 'bert', platforms: :ruby, group: :bert
|
|
14
14
|
gem 'php-serialize', group: :php
|
15
15
|
|
16
16
|
group :bson do
|
17
|
-
gem 'bson', '>=
|
17
|
+
gem 'bson', '>= 4.0.0'
|
18
18
|
end
|
19
19
|
|
20
20
|
group :msgpack do
|
@@ -38,7 +38,6 @@ gem 'daybreak', group: :daybreak
|
|
38
38
|
gem 'activerecord', '~> 5.2', group: :activerecord
|
39
39
|
gem 'redis', '~> 4.0.0', group: :redis
|
40
40
|
gem 'mongo', '>= 2', group: :mongo_official
|
41
|
-
gem 'moped', '>= 2', group: :mongo_moped
|
42
41
|
gem 'sequel', group: :sequel
|
43
42
|
gem 'dalli', group: :memcached_dalli
|
44
43
|
gem 'riak-client', group: :riak
|
@@ -94,9 +93,7 @@ end
|
|
94
93
|
|
95
94
|
# Rails integration testing
|
96
95
|
group :rails do
|
97
|
-
|
98
|
-
gem 'actionpack'
|
99
|
-
end
|
96
|
+
gem 'actionpack', '~> 5.2.0'
|
100
97
|
gem 'minitest', '~> 5.0'
|
101
98
|
end
|
102
99
|
|
@@ -105,3 +102,8 @@ group :doc, optional: true do
|
|
105
102
|
gem 'kramdown', '~> 1.17.0'
|
106
103
|
gem 'yard', '~> 0.9.20'
|
107
104
|
end
|
105
|
+
|
106
|
+
# Used for running a dev console
|
107
|
+
group :console, optional: true do
|
108
|
+
gem 'irb'
|
109
|
+
end
|
data/README.md
CHANGED
@@ -120,7 +120,7 @@ Out of the box, it supports the following backends. Use the backend name symbol
|
|
120
120
|
* [Simple Samba database TDB](http://tdb.samba.org/) (`:TDB`)
|
121
121
|
* Document databases:
|
122
122
|
* [CouchDB](http://couchdb.apache.org/) (`:Couch`)
|
123
|
-
* [MongoDB](http://www.mongodb.org/) (`:Mongo
|
123
|
+
* [MongoDB](http://www.mongodb.org/) (`:Mongo`)
|
124
124
|
* Moneta network protocols:
|
125
125
|
* Moneta key/value client (`:Client` works with `Moneta::Server`)
|
126
126
|
* Moneta HTTP/REST client (`:RestClient` works with `Rack::MonetaRest`)
|
@@ -142,11 +142,7 @@ __NOTE:__ <a name="backend-matrix"></a>The backend matrix is much more readable
|
|
142
142
|
|
143
143
|
<tr><th colspan="2">Persistent stores</th><th colspan="12"></th></tr>
|
144
144
|
|
145
|
-
<tr><td>Mongo</td><td>mongo
|
146
|
-
|
147
|
-
<tr><td>MongoOfficial</td><td>mongo</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://www.mongodb.org/">MongoDB</a> database</td></tr>
|
148
|
-
|
149
|
-
<tr><td>MongoMoped</td><td>moped</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://www.mongodb.org/">MongoDB</a> database</td></tr>
|
145
|
+
<tr><td>Mongo</td><td>mongo</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://www.mongodb.org/">MongoDB</a> database</td></tr>
|
150
146
|
|
151
147
|
<tr><td>Redis</td><td>redis</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://redis.io/">Redis</a> database</td></tr>
|
152
148
|
|
data/feature_matrix.yaml
CHANGED
@@ -8,20 +8,10 @@ notes:
|
|
8
8
|
server if you want multiprocess concurrency!
|
9
9
|
backends:
|
10
10
|
- adapter: Mongo
|
11
|
-
platforms: [ MRI, JRuby ]
|
12
|
-
gems: mongo or moped
|
13
|
-
features: [ threadsafe, multiprocess, increment, create, expires, each_key, bulk_read, bulk_write ]
|
14
|
-
description: "[MongoDB](http://www.mongodb.org/) database"
|
15
|
-
- adapter: MongoOfficial
|
16
11
|
platforms: [ MRI, JRuby ]
|
17
12
|
gems: mongo
|
18
13
|
features: [ threadsafe, multiprocess, increment, create, expires, each_key, bulk_read, bulk_write ]
|
19
14
|
description: "[MongoDB](http://www.mongodb.org/) database"
|
20
|
-
- adapter: MongoMoped
|
21
|
-
platforms: [ MRI, JRuby ]
|
22
|
-
gems: moped
|
23
|
-
features: [ threadsafe, multiprocess, increment, create, expires, each_key, bulk_read, bulk_write ]
|
24
|
-
description: "[MongoDB](http://www.mongodb.org/) database"
|
25
15
|
- adapter: Redis
|
26
16
|
platforms: [ MRI, JRuby ]
|
27
17
|
gems: redis
|
data/lib/moneta.rb
CHANGED
@@ -5,20 +5,22 @@
|
|
5
5
|
module Moneta
|
6
6
|
autoload :Builder, 'moneta/builder'
|
7
7
|
autoload :Cache, 'moneta/cache'
|
8
|
-
autoload :CreateSupport, 'moneta/
|
9
|
-
autoload :
|
10
|
-
autoload :
|
8
|
+
autoload :CreateSupport, 'moneta/create_support'
|
9
|
+
autoload :DBMAdapter, 'moneta/dbm_adapter'
|
10
|
+
autoload :Defaults, 'moneta/defaults'
|
11
|
+
autoload :EachKeySupport, 'moneta/each_key_support'
|
11
12
|
autoload :Enumerable, 'moneta/enumerable'
|
12
|
-
autoload :ExpiresSupport, 'moneta/
|
13
|
+
autoload :ExpiresSupport, 'moneta/expires_support'
|
13
14
|
autoload :Expires, 'moneta/expires'
|
14
15
|
autoload :Fallback, 'moneta/fallback'
|
15
|
-
autoload :HashAdapter, 'moneta/
|
16
|
-
autoload :IncrementSupport, 'moneta/
|
16
|
+
autoload :HashAdapter, 'moneta/hash_adapter'
|
17
|
+
autoload :IncrementSupport, 'moneta/increment_support'
|
17
18
|
autoload :Lock, 'moneta/lock'
|
18
19
|
autoload :Logger, 'moneta/logger'
|
19
20
|
autoload :Mutex, 'moneta/synchronize'
|
21
|
+
autoload :NilValues, 'moneta/nil_values'
|
20
22
|
autoload :OptionMerger, 'moneta/optionmerger'
|
21
|
-
autoload :OptionSupport, 'moneta/
|
23
|
+
autoload :OptionSupport, 'moneta/option_support'
|
22
24
|
autoload :Pool, 'moneta/pool'
|
23
25
|
autoload :Proxy, 'moneta/proxy'
|
24
26
|
autoload :Semaphore, 'moneta/synchronize'
|
@@ -58,8 +60,6 @@ module Moneta
|
|
58
60
|
autoload :MemcachedNative, 'moneta/adapters/memcached/native'
|
59
61
|
autoload :Memory, 'moneta/adapters/memory'
|
60
62
|
autoload :Mongo, 'moneta/adapters/mongo'
|
61
|
-
autoload :MongoMoped, 'moneta/adapters/mongo/moped'
|
62
|
-
autoload :MongoOfficial, 'moneta/adapters/mongo/official'
|
63
63
|
autoload :Null, 'moneta/adapters/null'
|
64
64
|
autoload :PStore, 'moneta/adapters/pstore'
|
65
65
|
autoload :Redis, 'moneta/adapters/redis'
|
@@ -1,12 +1,261 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
|
1
3
|
module Moneta
|
2
|
-
# @api private
|
3
4
|
module Adapters
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# MongoDB backend
|
6
|
+
#
|
7
|
+
# Supports expiration, documents will be automatically removed starting
|
8
|
+
# with mongodb >= 2.2 (see {http://docs.mongodb.org/manual/tutorial/expire-data/}).
|
9
|
+
#
|
10
|
+
# You can store hashes directly using this adapter.
|
11
|
+
#
|
12
|
+
# @example Store hashes
|
13
|
+
# db = Moneta::Adapters::MongoOfficial.new
|
14
|
+
# db['key'] = {a: 1, b: 2}
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
class Mongo
|
18
|
+
include Defaults
|
19
|
+
include ExpiresSupport
|
20
|
+
|
21
|
+
supports :each_key, :create, :increment
|
22
|
+
attr_reader :backend
|
23
|
+
|
24
|
+
DEFAULT_PORT = 27017
|
25
|
+
|
26
|
+
# @param [Hash] options
|
27
|
+
# @option options [String] :collection ('moneta') MongoDB collection name
|
28
|
+
# @option options [String] :host ('127.0.0.1') MongoDB server host
|
29
|
+
# @option options [String] :user Username used to authenticate
|
30
|
+
# @option options [String] :password Password used to authenticate
|
31
|
+
# @option options [Integer] :port (MongoDB default port) MongoDB server port
|
32
|
+
# @option options [String] :db ('moneta') MongoDB database
|
33
|
+
# @option options [Integer] :expires Default expiration time
|
34
|
+
# @option options [String] :expires_field ('expiresAt') Document field to store expiration time
|
35
|
+
# @option options [String] :value_field ('value') Document field to store value
|
36
|
+
# @option options [String] :type_field ('type') Document field to store value type
|
37
|
+
# @option options [::Mongo::Client] :backend Use existing backend instance
|
38
|
+
# @option options Other options passed to `Mongo::MongoClient#new`
|
39
|
+
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'
|
44
|
+
|
45
|
+
collection = options.delete(:collection) || 'moneta'
|
46
|
+
db = options.delete(:db) || 'moneta'
|
47
|
+
@backend = options[:backend] ||
|
48
|
+
begin
|
49
|
+
host = options.delete(:host) || '127.0.0.1'
|
50
|
+
port = options.delete(:port) || DEFAULT_PORT
|
51
|
+
options[:logger] ||= ::Logger.new(STDERR).tap do |logger|
|
52
|
+
logger.level = ::Logger::ERROR
|
53
|
+
end
|
54
|
+
::Mongo::Client.new(["#{host}:#{port}"], options)
|
55
|
+
end
|
56
|
+
@backend.use(db)
|
57
|
+
@collection = @backend[collection]
|
58
|
+
if @backend.command(buildinfo: 1).documents.first['version'] >= '2.2'
|
59
|
+
@collection.indexes.create_one({ @expires_field => 1 }, expire_after: 0)
|
60
|
+
else
|
61
|
+
warn 'Moneta::Adapters::Mongo - You are using MongoDB version < 2.2, expired documents will not be deleted'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# (see Proxy#load)
|
66
|
+
def load(key, options = {})
|
67
|
+
view = @collection.find(:$and => [
|
68
|
+
{ _id: to_binary(key) },
|
69
|
+
not_expired
|
70
|
+
])
|
71
|
+
|
72
|
+
doc = view.limit(1).first
|
73
|
+
|
74
|
+
if doc
|
75
|
+
update_expiry(options, nil) do |expires|
|
76
|
+
view.update_one(:$set => { @expires_field => expires })
|
77
|
+
end
|
78
|
+
|
79
|
+
doc_to_value(doc)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# (see Proxy#store)
|
84
|
+
def store(key, value, options = {})
|
85
|
+
key = to_binary(key)
|
86
|
+
@collection.replace_one({ _id: key },
|
87
|
+
value_to_doc(key, value, options),
|
88
|
+
upsert: true)
|
89
|
+
value
|
90
|
+
end
|
91
|
+
|
92
|
+
# (see Proxy#each_key)
|
93
|
+
def each_key
|
94
|
+
return enum_for(:each_key) unless block_given?
|
95
|
+
@collection.find.each { |doc| yield from_binary(doc[:_id]) }
|
96
|
+
self
|
97
|
+
end
|
98
|
+
|
99
|
+
# (see Proxy#delete)
|
100
|
+
def delete(key, options = {})
|
101
|
+
key = to_binary(key)
|
102
|
+
if doc = @collection.find(_id: key).find_one_and_delete and
|
103
|
+
!doc[@expires_field] || doc[@expires_field] >= Time.now
|
104
|
+
doc_to_value(doc)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# (see Proxy#increment)
|
109
|
+
def increment(key, amount = 1, options = {})
|
110
|
+
@collection.find_one_and_update({ :$and => [{ _id: to_binary(key) }, not_expired] },
|
111
|
+
{ :$inc => { @value_field => amount } },
|
112
|
+
return_document: :after,
|
113
|
+
upsert: true)[@value_field]
|
114
|
+
end
|
115
|
+
|
116
|
+
# (see Proxy#create)
|
117
|
+
def create(key, value, options = {})
|
118
|
+
key = to_binary(key)
|
119
|
+
@collection.insert_one(value_to_doc(key, value, options))
|
120
|
+
true
|
121
|
+
rescue ::Mongo::Error::OperationFailure => ex
|
122
|
+
raise unless ex.message =~ /^E11000 / # duplicate key error
|
123
|
+
false
|
124
|
+
end
|
125
|
+
|
126
|
+
# (see Proxy#clear)
|
127
|
+
def clear(options = {})
|
128
|
+
@collection.delete_many
|
129
|
+
self
|
130
|
+
end
|
131
|
+
|
132
|
+
# (see Proxy#close)
|
133
|
+
def close
|
134
|
+
@backend.close
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
|
138
|
+
# (see Proxy#slice)
|
139
|
+
def slice(*keys, **options)
|
140
|
+
view = @collection.find(:$and => [
|
141
|
+
{ _id: { :$in => keys.map(&method(:to_binary)) } },
|
142
|
+
not_expired
|
143
|
+
])
|
144
|
+
pairs = view.map { |doc| [from_binary(doc[:_id]), doc_to_value(doc)] }
|
145
|
+
|
146
|
+
update_expiry(options, nil) do |expires|
|
147
|
+
view.update_many(:$set => { @expires_field => expires })
|
148
|
+
end
|
149
|
+
|
150
|
+
pairs
|
151
|
+
end
|
152
|
+
|
153
|
+
# (see Proxy#merge!)
|
154
|
+
def merge!(pairs, options = {})
|
155
|
+
existing = Hash[slice(*pairs.map { |key, _| key })]
|
156
|
+
update_pairs, insert_pairs = pairs.partition { |key, _| existing.key?(key) }
|
157
|
+
|
158
|
+
@collection.insert_many(insert_pairs.map do |key, value|
|
159
|
+
value_to_doc(to_binary(key), value, options)
|
160
|
+
end)
|
161
|
+
|
162
|
+
update_pairs.each do |key, value|
|
163
|
+
value = yield(key, existing[key], value) if block_given?
|
164
|
+
binary = to_binary(key)
|
165
|
+
@collection.replace_one({ _id: binary }, value_to_doc(binary, value, options))
|
166
|
+
end
|
167
|
+
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
# (see Proxy#fetch_values)
|
172
|
+
def fetch_values(*keys, **options)
|
173
|
+
return values_at(*keys, **options) unless block_given?
|
174
|
+
hash = Hash[slice(*keys, **options)]
|
175
|
+
keys.map do |key|
|
176
|
+
if hash.key?(key)
|
177
|
+
hash[key]
|
178
|
+
else
|
179
|
+
yield key
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# (see Proxy#values_at)
|
185
|
+
def values_at(*keys, **options)
|
186
|
+
hash = Hash[slice(*keys, **options)]
|
187
|
+
keys.map { |key| hash[key] }
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def doc_to_value(doc)
|
193
|
+
case doc[@type_field]
|
194
|
+
when 'Hash'
|
195
|
+
doc = doc.dup
|
196
|
+
doc.delete('_id')
|
197
|
+
doc.delete(@type_field)
|
198
|
+
doc.delete(@expires_field)
|
199
|
+
doc
|
200
|
+
when 'Number'
|
201
|
+
doc[@value_field]
|
202
|
+
else
|
203
|
+
# In ruby_bson version 2 (and probably up), #to_s no longer returns the binary data
|
204
|
+
from_binary(doc[@value_field])
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def value_to_doc(key, value, options)
|
209
|
+
case value
|
210
|
+
when Hash
|
211
|
+
value.merge('_id' => key,
|
212
|
+
@type_field => 'Hash',
|
213
|
+
# @expires_field must be a Time object (BSON date datatype)
|
214
|
+
@expires_field => expires_at(options) || nil)
|
215
|
+
when Float, Integer
|
216
|
+
{ '_id' => key,
|
217
|
+
@type_field => 'Number',
|
218
|
+
@value_field => value,
|
219
|
+
# @expires_field must be a Time object (BSON date datatype)
|
220
|
+
@expires_field => expires_at(options) || nil }
|
221
|
+
when String
|
222
|
+
intvalue = value.to_i
|
223
|
+
{ '_id' => key,
|
224
|
+
@type_field => 'String',
|
225
|
+
@value_field => intvalue.to_s == value ? intvalue : to_binary(value),
|
226
|
+
# @expires_field must be a Time object (BSON date datatype)
|
227
|
+
@expires_field => expires_at(options) || nil }
|
228
|
+
else
|
229
|
+
raise ArgumentError, "Invalid value type: #{value.class}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# BSON will use String#force_encoding to make the string 8-bit
|
234
|
+
# ASCII. This could break unicode text so we should dup in this
|
235
|
+
# case, and it also fails with frozen strings.
|
236
|
+
def to_binary(str)
|
237
|
+
str = str.dup if str.frozen? || str.encoding != Encoding::ASCII_8BIT
|
238
|
+
::BSON::Binary.new(str)
|
239
|
+
end
|
240
|
+
|
241
|
+
def from_binary(binary)
|
242
|
+
binary.is_a?(::BSON::Binary) ? binary.data : binary.to_s
|
243
|
+
end
|
244
|
+
|
245
|
+
def not_expired
|
246
|
+
{
|
247
|
+
:$or => [
|
248
|
+
{ @expires_field => nil },
|
249
|
+
{ @expires_field => { :$gte => Time.now } }
|
250
|
+
]
|
251
|
+
}
|
252
|
+
end
|
253
|
+
|
254
|
+
def update_expiry(options, default)
|
255
|
+
if (expires = expires_at(options, default)) != nil
|
256
|
+
yield(expires || nil)
|
257
|
+
end
|
258
|
+
end
|
10
259
|
end
|
11
260
|
end
|
12
261
|
end
|