moneta 1.4.2 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|