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
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Moneta
|
4
|
+
# Some docs here
|
5
|
+
module Config
|
6
|
+
# @api private
|
7
|
+
module ClassMethods
|
8
|
+
def config(name, coerce: nil, default: nil, required: false, &block)
|
9
|
+
raise ArgumentError, 'name must be a symbol' unless Symbol === name
|
10
|
+
|
11
|
+
defaults = config_defaults
|
12
|
+
|
13
|
+
raise ArgumentError, "#{name} is already a config option" if defaults.key?(name)
|
14
|
+
raise ArgumentError, "coerce must respond to :to_proc" if coerce && !coerce.respond_to?(:to_proc)
|
15
|
+
|
16
|
+
defaults.merge!(name => default.freeze).freeze
|
17
|
+
instance_variable_set :@config_defaults, defaults
|
18
|
+
|
19
|
+
instance_variable_set :@config_coercions, config_coercions.merge!(name => coerce.to_proc) if coerce
|
20
|
+
instance_variable_set :@config_required_keys, config_required_keys.add(name).freeze if required
|
21
|
+
instance_variable_set :@config_blocks, config_blocks.merge!(name => block) if block
|
22
|
+
end
|
23
|
+
|
24
|
+
def config_variable(name)
|
25
|
+
if instance_variable_defined?(name)
|
26
|
+
instance_variable_get(name).dup
|
27
|
+
elsif superclass.respond_to?(:config_variable)
|
28
|
+
superclass.config_variable(name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def config_defaults
|
33
|
+
config_variable(:@config_defaults) || {}
|
34
|
+
end
|
35
|
+
|
36
|
+
def config_required_keys
|
37
|
+
config_variable(:@config_required_keys) || Set.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def config_coercions
|
41
|
+
config_variable(:@config_coercions) || {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def config_blocks
|
45
|
+
config_variable(:@config_blocks) || {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def config_struct
|
49
|
+
unless @config_struct
|
50
|
+
keys = config_defaults.keys
|
51
|
+
@config_struct = Struct.new(*keys) unless keys.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
@config_struct
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def config
|
59
|
+
raise "Not configured" unless defined?(@config)
|
60
|
+
@config
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.included(base)
|
64
|
+
base.extend(ClassMethods)
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def configure(**options)
|
70
|
+
raise 'Already configured' if defined?(@config)
|
71
|
+
|
72
|
+
self.class.config_required_keys.each do |key|
|
73
|
+
raise ArgumentError, "#{key} is required" unless options.key? key
|
74
|
+
end
|
75
|
+
|
76
|
+
defaults = self.class.config_defaults
|
77
|
+
|
78
|
+
overrides, remainder = options
|
79
|
+
.partition { |key,| defaults.key? key }
|
80
|
+
.map { |pairs| pairs.to_h }
|
81
|
+
|
82
|
+
self.class.config_coercions.each do |key, coerce|
|
83
|
+
overrides[key] = coerce.call(overrides[key]) if overrides.key?(key)
|
84
|
+
end
|
85
|
+
|
86
|
+
overridden = defaults.merge!(overrides)
|
87
|
+
|
88
|
+
config_blocks = self.class.config_blocks
|
89
|
+
values = overridden.map do |key, value|
|
90
|
+
if config_block = config_blocks[key]
|
91
|
+
instance_exec(**overridden, &config_block)
|
92
|
+
else
|
93
|
+
value
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
@config = self.class.config_struct&.new(*values).freeze
|
98
|
+
remainder
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/moneta/expires.rb
CHANGED
@@ -3,8 +3,6 @@ module Moneta
|
|
3
3
|
#
|
4
4
|
#
|
5
5
|
module ExpiresSupport
|
6
|
-
attr_accessor :default_expires
|
7
|
-
|
8
6
|
protected
|
9
7
|
|
10
8
|
# Calculates the time when something will expire.
|
@@ -19,7 +17,7 @@ module Moneta
|
|
19
17
|
# @return [false] if it should not expire
|
20
18
|
# @return [Time] the time when something should expire
|
21
19
|
# @return [nil] if it is not known
|
22
|
-
def expires_at(options, default =
|
20
|
+
def expires_at(options, default = config.expires)
|
23
21
|
value = expires_value(options, default)
|
24
22
|
Numeric === value ? Time.now + value : value
|
25
23
|
end
|
@@ -36,7 +34,7 @@ module Moneta
|
|
36
34
|
# @return [false] if it should not expire
|
37
35
|
# @return [Numeric] seconds until expiration
|
38
36
|
# @return [nil] if it is not known
|
39
|
-
def expires_value(options, default =
|
37
|
+
def expires_value(options, default = config.expires)
|
40
38
|
case value = options[:expires]
|
41
39
|
when 0, false
|
42
40
|
false
|
@@ -54,6 +52,7 @@ module Moneta
|
|
54
52
|
class << self
|
55
53
|
def included(base)
|
56
54
|
base.supports(:expires) if base.respond_to?(:supports)
|
55
|
+
base.config :expires
|
57
56
|
end
|
58
57
|
end
|
59
58
|
end
|
data/lib/moneta/pool.rb
CHANGED
@@ -309,9 +309,9 @@ module Moneta
|
|
309
309
|
# store to become available. If not specified, will wait forever.
|
310
310
|
# @yield A builder context for speciying how to construct stores
|
311
311
|
def initialize(options = {}, &block)
|
312
|
-
super(nil)
|
313
312
|
@id = "Moneta::Pool(#{object_id})"
|
314
313
|
@manager = PoolManager.new(Builder.new(&block), **options)
|
314
|
+
super(nil, options)
|
315
315
|
end
|
316
316
|
|
317
317
|
# Closing has no effect on the pool, as stores are closed in the background
|
data/lib/moneta/proxy.rb
CHANGED
@@ -3,6 +3,7 @@ module Moneta
|
|
3
3
|
# @api public
|
4
4
|
class Proxy
|
5
5
|
include Defaults
|
6
|
+
include Config
|
6
7
|
|
7
8
|
attr_reader :adapter
|
8
9
|
|
@@ -10,6 +11,7 @@ module Moneta
|
|
10
11
|
# @param [Hash] options
|
11
12
|
def initialize(adapter, options = {})
|
12
13
|
@adapter = adapter
|
14
|
+
configure(**options)
|
13
15
|
end
|
14
16
|
|
15
17
|
# (see Defaults#key?)
|
@@ -133,5 +135,32 @@ module Moneta
|
|
133
135
|
super
|
134
136
|
end
|
135
137
|
end
|
138
|
+
|
139
|
+
# Overrides the default implementation of the config method to:
|
140
|
+
#
|
141
|
+
# * pass the adapter's config, if this proxy has no configuration of its
|
142
|
+
# own
|
143
|
+
# * return a merged configuration, allowing the proxy have precedence over
|
144
|
+
# the adapter
|
145
|
+
def config
|
146
|
+
unless @proxy_config
|
147
|
+
config = super
|
148
|
+
adapter_config = adapter&.config
|
149
|
+
|
150
|
+
@proxy_config =
|
151
|
+
if config && adapter_config
|
152
|
+
adapter_members = adapter_config.members - config.members
|
153
|
+
members = config.members + adapter_members
|
154
|
+
struct = Struct.new(*members)
|
155
|
+
|
156
|
+
values = config.values + adapter_config.to_h.values_at(*adapter_members)
|
157
|
+
struct.new(*values)
|
158
|
+
else
|
159
|
+
config || adapter_config
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
@proxy_config
|
164
|
+
end
|
136
165
|
end
|
137
166
|
end
|
data/lib/moneta/server.rb
CHANGED
@@ -4,14 +4,17 @@ module Moneta
|
|
4
4
|
# Moneta server to be used together with Moneta::Adapters::Client
|
5
5
|
# @api public
|
6
6
|
class Server
|
7
|
-
|
8
|
-
|
7
|
+
include Config
|
8
|
+
|
9
|
+
config :timeout, default: 1
|
10
|
+
config :max_size, default: 0x100000
|
9
11
|
|
10
12
|
# @api private
|
11
13
|
class Connection
|
12
|
-
def initialize(io, store)
|
14
|
+
def initialize(io, store, max_size)
|
13
15
|
@io = io
|
14
16
|
@store = store
|
17
|
+
@max_size = max_size
|
15
18
|
@fiber = Fiber.new { run }
|
16
19
|
end
|
17
20
|
|
@@ -58,7 +61,7 @@ module Moneta
|
|
58
61
|
|
59
62
|
def read_msg
|
60
63
|
size = read(4).unpack('N').first
|
61
|
-
throw :closed, 'Message too big' if size >
|
64
|
+
throw :closed, 'Message too big' if size > @max_size
|
62
65
|
Marshal.load(read(size))
|
63
66
|
end
|
64
67
|
|
@@ -155,9 +158,13 @@ module Moneta
|
|
155
158
|
# @param [Hash] options
|
156
159
|
# @option options [Integer] :port (9000) TCP port
|
157
160
|
# @option options [String] :socket Alternative Unix socket file name
|
161
|
+
# @option options [Integer] :timeout (1) Number of seconds to timeout on IO.select
|
162
|
+
# @option options [Integer] :max_size (0x100000) Maximum number of bytes
|
163
|
+
# allowed to be sent by clients in requests
|
158
164
|
def initialize(store, options = {})
|
165
|
+
options = configure(**options)
|
159
166
|
@store = store
|
160
|
-
@server = start(options)
|
167
|
+
@server = start(**options)
|
161
168
|
@ios = [@server]
|
162
169
|
@reads = @ios.dup
|
163
170
|
@writes = []
|
@@ -187,7 +194,7 @@ module Moneta
|
|
187
194
|
@ios
|
188
195
|
.reject { |io| io == @server }
|
189
196
|
.each { |io| close_connection(io) }
|
190
|
-
File.unlink(
|
197
|
+
File.unlink(config.socket) if config.socket rescue nil
|
191
198
|
end
|
192
199
|
end
|
193
200
|
|
@@ -202,7 +209,7 @@ module Moneta
|
|
202
209
|
private
|
203
210
|
|
204
211
|
def mainloop
|
205
|
-
if ready = IO.select(@reads, @writes, @ios,
|
212
|
+
if ready = IO.select(@reads, @writes, @ios, config.timeout)
|
206
213
|
reads, writes, errors = ready
|
207
214
|
errors.each { |io| close_connection(io) }
|
208
215
|
|
@@ -229,7 +236,7 @@ module Moneta
|
|
229
236
|
|
230
237
|
def accept_connection
|
231
238
|
io = @server.accept
|
232
|
-
@connections[io] = Connection.new(io, @store)
|
239
|
+
@connections[io] = Connection.new(io, @store, config.max_size)
|
233
240
|
@ios << io
|
234
241
|
resume(io)
|
235
242
|
ensure
|
@@ -261,21 +268,21 @@ module Moneta
|
|
261
268
|
end
|
262
269
|
end
|
263
270
|
|
264
|
-
def start(
|
265
|
-
if
|
271
|
+
def start(host: '127.0.0.1', port: 9000, socket: nil)
|
272
|
+
if socket
|
266
273
|
begin
|
267
|
-
UNIXServer.open(
|
274
|
+
UNIXServer.open(socket)
|
268
275
|
rescue Errno::EADDRINUSE
|
269
|
-
if client = (UNIXSocket.open(
|
276
|
+
if client = (UNIXSocket.open(socket) rescue nil)
|
270
277
|
client.close
|
271
278
|
raise
|
272
279
|
end
|
273
|
-
File.unlink(
|
280
|
+
File.unlink(socket)
|
274
281
|
tries ||= 0
|
275
282
|
(tries += 1) < 3 ? retry : raise
|
276
283
|
end
|
277
284
|
else
|
278
|
-
TCPServer.open(
|
285
|
+
TCPServer.open(host, port)
|
279
286
|
end
|
280
287
|
end
|
281
288
|
|
data/lib/moneta/version.rb
CHANGED
data/lib/moneta/wrapper.rb
CHANGED
data/lib/moneta.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
# * {Moneta.new}
|
4
4
|
# * {Moneta.build}
|
5
5
|
module Moneta
|
6
|
+
autoload :Adapter, 'moneta/adapter'
|
6
7
|
autoload :Builder, 'moneta/builder'
|
7
8
|
autoload :Cache, 'moneta/cache'
|
8
9
|
autoload :CreateSupport, 'moneta/create_support'
|
10
|
+
autoload :Config, 'moneta/config'
|
9
11
|
autoload :DBMAdapter, 'moneta/dbm_adapter'
|
10
12
|
autoload :Defaults, 'moneta/defaults'
|
11
13
|
autoload :EachKeySupport, 'moneta/each_key_support'
|
data/moneta.gemspec
CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
|
|
26
26
|
|
27
27
|
s.required_ruby_version = '>= 2.3.0'
|
28
28
|
|
29
|
+
s.add_development_dependency 'multi_json', '~> 1.15.0'
|
29
30
|
s.add_development_dependency 'parallel_tests', '~> 2.29.2'
|
30
31
|
s.add_development_dependency 'rantly', '~> 1.2.0'
|
31
32
|
s.add_development_dependency 'rspec', '~> 3.0'
|
@@ -12,13 +12,14 @@ RSpec.shared_context :start_server do |**options|
|
|
12
12
|
puts "Failed to start server - #{ex.message}"
|
13
13
|
tries ||= 0
|
14
14
|
tries += 1
|
15
|
-
|
15
|
+
timeout = options[:timeout] || Moneta::Server.config_defaults[:timeout]
|
16
|
+
sleep 1
|
16
17
|
tries < 3 ? retry : raise
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
21
|
after :context do
|
21
|
-
@server
|
22
|
-
@thread
|
22
|
+
@server&.stop
|
23
|
+
@thread&.join
|
23
24
|
end
|
24
25
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
RSpec.shared_context :faraday_adapter do
|
2
2
|
before :context do
|
3
|
-
require 'faraday/adapter/manticore' if defined?(JRUBY_VERSION)
|
3
|
+
#require 'faraday/adapter/manticore' if defined?(JRUBY_VERSION)
|
4
4
|
end
|
5
5
|
|
6
6
|
let(:faraday_adapter) do
|
7
|
-
defined?(JRUBY_VERSION) ? :manticore : :net_http
|
7
|
+
#defined?(JRUBY_VERSION) ? :manticore : :net_http
|
8
|
+
:net_http
|
8
9
|
end
|
9
10
|
end
|
@@ -52,8 +52,10 @@ describe 'adapter_lruhash', adapter: :LRUHash do
|
|
52
52
|
|
53
53
|
it 'adds a value that is as large as the default max_size when max_size is missing' do
|
54
54
|
store = Moneta::Adapters::LRUHash.new
|
55
|
+
expect(store.config.max_size).to eq Moneta::Adapters::LRUHash.config_defaults[:max_size]
|
56
|
+
|
55
57
|
large_item = 'Really big'
|
56
|
-
allow(large_item).to receive(:bytesize).and_return(Moneta::Adapters::LRUHash
|
58
|
+
allow(large_item).to receive(:bytesize).and_return(Moneta::Adapters::LRUHash.config_defaults[:max_size])
|
57
59
|
store[:really_big] = large_item
|
58
60
|
store[:really_big].should eq(large_item)
|
59
61
|
end
|
@@ -61,7 +63,7 @@ describe 'adapter_lruhash', adapter: :LRUHash do
|
|
61
63
|
it 'does not add values that are larger than the default max_size when max_size is missing' do
|
62
64
|
store = Moneta::Adapters::LRUHash.new
|
63
65
|
large_item = 'Really big'
|
64
|
-
allow(large_item).to receive(:bytesize).and_return(Moneta::Adapters::LRUHash
|
66
|
+
allow(large_item).to receive(:bytesize).and_return(Moneta::Adapters::LRUHash.config_defaults[:max_size] + 1)
|
65
67
|
store[:really_big] = large_item
|
66
68
|
store[:really_big].should be_nil
|
67
69
|
end
|
@@ -69,7 +71,7 @@ describe 'adapter_lruhash', adapter: :LRUHash do
|
|
69
71
|
it 'adds values that are larger than the default max_size when max_size is nil' do
|
70
72
|
store = Moneta::Adapters::LRUHash.new(max_size: nil)
|
71
73
|
large_item = 'Really big'
|
72
|
-
allow(large_item).to receive(:bytesize).and_return(Moneta::Adapters::LRUHash
|
74
|
+
allow(large_item).to receive(:bytesize).and_return(Moneta::Adapters::LRUHash.config_defaults[:max_size] + 1)
|
73
75
|
store[:really_big] = large_item
|
74
76
|
store[:really_big].should eq(large_item)
|
75
77
|
end
|
@@ -94,7 +96,8 @@ describe 'adapter_lruhash', adapter: :LRUHash do
|
|
94
96
|
end
|
95
97
|
|
96
98
|
it 'only allows the default number of items when max_count is missing' do
|
97
|
-
|
99
|
+
defaults = Moneta::Adapters::LRUHash.config_defaults
|
100
|
+
allow(Moneta::Adapters::LRUHash).to receive(:config_defaults).and_return(defaults.merge(max_count: 5))
|
98
101
|
store = Moneta::Adapters::LRUHash.new(max_value: nil, max_size: nil)
|
99
102
|
(1..6).each { |n| store[n] = n }
|
100
103
|
store.key?(1).should be false
|
@@ -103,8 +106,9 @@ describe 'adapter_lruhash', adapter: :LRUHash do
|
|
103
106
|
store[6].should eq(6)
|
104
107
|
end
|
105
108
|
|
106
|
-
it 'adds more values than
|
107
|
-
|
109
|
+
it 'adds more values than the default max_count allows when max_count is nil' do
|
110
|
+
defaults = Moneta::Adapters::LRUHash.config_defaults
|
111
|
+
allow(Moneta::Adapters::LRUHash).to receive(:config_defaults).and_return(defaults.merge(max_count: 5))
|
108
112
|
store = Moneta::Adapters::LRUHash.new(max_count: nil, max_value: nil, max_size: nil)
|
109
113
|
(1..6).each { |n| store[n] = n }
|
110
114
|
store[1].should eq(1)
|
@@ -28,7 +28,7 @@ describe 'adapter_mongo', adapter: :Mongo do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'uses the database specified via the :database option' do
|
31
|
-
expect(store.
|
31
|
+
expect(store.config.database).to eq database
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'uses the database specified via the :db option' do
|
@@ -36,6 +36,6 @@ describe 'adapter_mongo', adapter: :Mongo do
|
|
36
36
|
db: database,
|
37
37
|
collection: 'adapter_mongo'
|
38
38
|
)
|
39
|
-
expect(store.
|
39
|
+
expect(store.config.database).to eq database
|
40
40
|
end
|
41
41
|
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
describe Moneta::Config do
|
2
|
+
describe 'without any configuration' do
|
3
|
+
it 'does not set the config attribute' do
|
4
|
+
klass = Class.new do
|
5
|
+
include ::Moneta::Config
|
6
|
+
|
7
|
+
def initialize(**options)
|
8
|
+
configure(**options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
instance = klass.new(k: 'v')
|
13
|
+
expect(instance.config).to be nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'basic functionality' do
|
18
|
+
subject :klass do
|
19
|
+
Class.new do
|
20
|
+
include ::Moneta::Config
|
21
|
+
|
22
|
+
config :a
|
23
|
+
config :b
|
24
|
+
|
25
|
+
def initialize(**options)
|
26
|
+
configure(**options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets all config values to nil by default' do
|
32
|
+
instance = klass.new
|
33
|
+
expect(instance.config.a).to eq nil
|
34
|
+
expect(instance.config.b).to be nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'sets config values with values provided to #configure' do
|
38
|
+
instance = klass.new(a: 1)
|
39
|
+
expect(instance.config.a).to eq 1
|
40
|
+
expect(instance.config.b).to be nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'freezes the config' do
|
44
|
+
instance = klass.new
|
45
|
+
expect(instance.config.frozen?).to be true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'with required arguments' do
|
50
|
+
subject :klass do
|
51
|
+
Class.new do
|
52
|
+
include ::Moneta::Config
|
53
|
+
|
54
|
+
config :a, required: true
|
55
|
+
config :b, default: 'x', required: true
|
56
|
+
|
57
|
+
def initialize(**options)
|
58
|
+
configure(**options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'raises an ArgumentError if #configure is called without one of the required arguments' do
|
64
|
+
expect { klass.new(a: 1) }.to raise_error ArgumentError, 'b is required'
|
65
|
+
expect { klass.new(b: 1) }.to raise_error ArgumentError, 'a is required'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'with defaults' do
|
70
|
+
subject :klass do
|
71
|
+
Class.new do
|
72
|
+
include ::Moneta::Config
|
73
|
+
|
74
|
+
config :a, default: 't'
|
75
|
+
config :b, default: 's'
|
76
|
+
|
77
|
+
def initialize(**options)
|
78
|
+
configure(**options)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'uses the defaults if no argument is provided' do
|
84
|
+
instance = klass.new(a: 1)
|
85
|
+
|
86
|
+
expect(instance.config.a).to eq 1
|
87
|
+
expect(instance.config.b).to eq 's'
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'allows falsy values to override truthy defaults' do
|
91
|
+
instance = klass.new(a: nil, b: false)
|
92
|
+
|
93
|
+
expect(instance.config.a).to be nil
|
94
|
+
expect(instance.config.b).to be false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'with coercion' do
|
99
|
+
describe 'using a symbol' do
|
100
|
+
subject :klass do
|
101
|
+
Class.new do
|
102
|
+
include ::Moneta::Config
|
103
|
+
|
104
|
+
config :a, coerce: :to_s
|
105
|
+
|
106
|
+
def initialize(**options)
|
107
|
+
configure(**options)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it "uses the symbol's to_proc property" do
|
113
|
+
instance = klass.new(a: :x)
|
114
|
+
expect(instance.config.a).to eq 'x'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'using a lambda' do
|
119
|
+
subject :klass do
|
120
|
+
Class.new do
|
121
|
+
include ::Moneta::Config
|
122
|
+
|
123
|
+
config :a, coerce: lambda { |a| a.to_sym }
|
124
|
+
|
125
|
+
def initialize(**options)
|
126
|
+
configure(**options)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it "calls the lambda" do
|
132
|
+
instance = klass.new(a: 'x')
|
133
|
+
expect(instance.config.a).to eq :x
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe 'with a block' do
|
139
|
+
subject :klass do
|
140
|
+
Class.new do
|
141
|
+
include ::Moneta::Config
|
142
|
+
|
143
|
+
config :a do |a:, b:|
|
144
|
+
{ a: a, b: b, test: @test }
|
145
|
+
end
|
146
|
+
|
147
|
+
config :b, default: 'b default'
|
148
|
+
|
149
|
+
def initialize(test: nil, **options)
|
150
|
+
@test = test
|
151
|
+
configure(**options)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'calls the block after all arguments and defaults have been processed' do
|
157
|
+
instance1 = klass.new(a: 'a value')
|
158
|
+
expect(instance1.config.a).to include(a: 'a value', b: 'b default')
|
159
|
+
|
160
|
+
instance2 = klass.new(b: 'b value')
|
161
|
+
expect(instance2.config.a).to include(a: nil, b: 'b value')
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'calls the block using instance_exec' do
|
165
|
+
instance = klass.new(test: 'test value')
|
166
|
+
expect(instance.config.a).to include(test: 'test value')
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe 'with inheritance' do
|
171
|
+
subject :klass do
|
172
|
+
Class.new do
|
173
|
+
include ::Moneta::Config
|
174
|
+
|
175
|
+
config :a
|
176
|
+
|
177
|
+
def initialize(**options)
|
178
|
+
configure(**options)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'does not allow subclasses to override superclass config' do
|
184
|
+
expect do
|
185
|
+
Class.new(klass) do
|
186
|
+
config :a
|
187
|
+
end
|
188
|
+
end.to raise_error ArgumentError, 'a is already a config option'
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'does not affect the superclass when additional config is added to the subclass' do
|
192
|
+
klass2 = Class.new(klass) do
|
193
|
+
config :b
|
194
|
+
end
|
195
|
+
|
196
|
+
instance1 = klass.new(a: 1, b: 2)
|
197
|
+
expect(instance1.config.to_h).to eq(a: 1)
|
198
|
+
|
199
|
+
instance2 = klass2.new(a: 1, b: 2)
|
200
|
+
expect(instance2.config.to_h).to eq(a: 1, b: 2)
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'is possible for two subclasses to have the same additional config' do
|
204
|
+
klass2 = Class.new(klass) do
|
205
|
+
config :b
|
206
|
+
end
|
207
|
+
|
208
|
+
klass3 = Class.new(klass) do
|
209
|
+
config :b
|
210
|
+
end
|
211
|
+
|
212
|
+
instance2 = klass2.new(a: 2, b: 1)
|
213
|
+
expect(instance2.config.to_h).to eq(a: 2, b: 1)
|
214
|
+
|
215
|
+
instance3 = klass3.new(a: 1, b: 2)
|
216
|
+
expect(instance3.config.to_h).to eq(a: 1, b: 2)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# Currently broken in JRuby 9.3 - see https://github.com/jruby/jruby/issues/6941
|
2
|
+
|
3
|
+
describe 'transformer_bson', proxy: :Transformer, broken: defined?(JRUBY_VERSION) && ::Gem::Version.new(JRUBY_VERSION) >= ::Gem::Version.new('9.3.0.0') do
|
2
4
|
moneta_build do
|
3
5
|
Moneta.build do
|
4
6
|
use :Transformer, key: :bson, value: :bson
|