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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +35 -38
  3. data/CHANGES +9 -0
  4. data/CONTRIBUTORS +2 -0
  5. data/Gemfile +143 -55
  6. data/README.md +5 -4
  7. data/lib/moneta/adapter.rb +52 -0
  8. data/lib/moneta/adapters/activerecord.rb +77 -68
  9. data/lib/moneta/adapters/activesupportcache.rb +22 -31
  10. data/lib/moneta/adapters/cassandra.rb +114 -116
  11. data/lib/moneta/adapters/client.rb +17 -18
  12. data/lib/moneta/adapters/couch.rb +31 -26
  13. data/lib/moneta/adapters/datamapper.rb +9 -5
  14. data/lib/moneta/adapters/daybreak.rb +15 -21
  15. data/lib/moneta/adapters/dbm.rb +6 -12
  16. data/lib/moneta/adapters/file.rb +21 -13
  17. data/lib/moneta/adapters/fog.rb +5 -6
  18. data/lib/moneta/adapters/gdbm.rb +6 -12
  19. data/lib/moneta/adapters/hbase.rb +10 -12
  20. data/lib/moneta/adapters/kyotocabinet.rb +22 -27
  21. data/lib/moneta/adapters/leveldb.rb +14 -20
  22. data/lib/moneta/adapters/lmdb.rb +19 -22
  23. data/lib/moneta/adapters/localmemcache.rb +7 -13
  24. data/lib/moneta/adapters/lruhash.rb +20 -20
  25. data/lib/moneta/adapters/memcached/dalli.rb +25 -33
  26. data/lib/moneta/adapters/memcached/native.rb +14 -20
  27. data/lib/moneta/adapters/memory.rb +5 -7
  28. data/lib/moneta/adapters/mongo.rb +53 -52
  29. data/lib/moneta/adapters/pstore.rb +21 -27
  30. data/lib/moneta/adapters/redis.rb +42 -37
  31. data/lib/moneta/adapters/restclient.rb +17 -25
  32. data/lib/moneta/adapters/riak.rb +8 -9
  33. data/lib/moneta/adapters/sdbm.rb +6 -12
  34. data/lib/moneta/adapters/sequel/mysql.rb +8 -8
  35. data/lib/moneta/adapters/sequel/postgres.rb +17 -17
  36. data/lib/moneta/adapters/sequel/postgres_hstore.rb +47 -47
  37. data/lib/moneta/adapters/sequel/sqlite.rb +9 -9
  38. data/lib/moneta/adapters/sequel.rb +56 -65
  39. data/lib/moneta/adapters/sqlite.rb +37 -35
  40. data/lib/moneta/adapters/tdb.rb +8 -14
  41. data/lib/moneta/adapters/tokyocabinet.rb +19 -17
  42. data/lib/moneta/adapters/tokyotyrant.rb +29 -30
  43. data/lib/moneta/adapters/yaml.rb +1 -5
  44. data/lib/moneta/config.rb +101 -0
  45. data/lib/moneta/expires.rb +0 -1
  46. data/lib/moneta/expires_support.rb +3 -4
  47. data/lib/moneta/pool.rb +1 -1
  48. data/lib/moneta/proxy.rb +29 -0
  49. data/lib/moneta/server.rb +21 -14
  50. data/lib/moneta/version.rb +1 -1
  51. data/lib/moneta/wrapper.rb +5 -0
  52. data/lib/moneta.rb +2 -0
  53. data/moneta.gemspec +1 -0
  54. data/spec/moneta/adapters/client/client_helper.rb +4 -3
  55. data/spec/moneta/adapters/faraday_helper.rb +3 -2
  56. data/spec/moneta/adapters/lruhash/adapter_lruhash_spec.rb +10 -6
  57. data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +2 -2
  58. data/spec/moneta/config_spec.rb +219 -0
  59. data/spec/moneta/proxies/transformer/transformer_bson_spec.rb +3 -1
  60. data/spec/moneta/proxies/transformer/transformer_marshal_escape_spec.rb +2 -0
  61. data/spec/rack/session_moneta_spec.rb +44 -25
  62. data/spec/restserver.rb +3 -14
  63. 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
@@ -14,7 +14,6 @@ module Moneta
14
14
  def initialize(adapter, options = {})
15
15
  raise 'Store already supports feature :expires' if adapter.supports?(:expires)
16
16
  super
17
- self.default_expires = options[:expires]
18
17
  end
19
18
 
20
19
  # (see Proxy#key?)
@@ -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 = @default_expires)
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 = @default_expires)
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
- TIMEOUT = 1
8
- MAXSIZE = 0x100000
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 > MAXSIZE
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(@socket) if @socket rescue nil
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, TIMEOUT)
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(options)
265
- if @socket = options[:socket]
271
+ def start(host: '127.0.0.1', port: 9000, socket: nil)
272
+ if socket
266
273
  begin
267
- UNIXServer.open(@socket)
274
+ UNIXServer.open(socket)
268
275
  rescue Errno::EADDRINUSE
269
- if client = (UNIXSocket.open(@socket) rescue nil)
276
+ if client = (UNIXSocket.open(socket) rescue nil)
270
277
  client.close
271
278
  raise
272
279
  end
273
- File.unlink(@socket)
280
+ File.unlink(socket)
274
281
  tries ||= 0
275
282
  (tries += 1) < 3 ? retry : raise
276
283
  end
277
284
  else
278
- TCPServer.open(options[:host] || '127.0.0.1', options[:port] || 9000)
285
+ TCPServer.open(host, port)
279
286
  end
280
287
  end
281
288
 
@@ -1,5 +1,5 @@
1
1
  module Moneta
2
2
  # Moneta version number
3
3
  # @api public
4
- VERSION = '1.4.2'.freeze
4
+ VERSION = '1.5.0'.freeze
5
5
  end
@@ -71,5 +71,10 @@ module Moneta
71
71
  def merge!(pairs, options = {})
72
72
  wrap(:merge!, pairs, options) { super }
73
73
  end
74
+
75
+ # (see Proxy#config)
76
+ def config
77
+ wrap(:config) { super }
78
+ end
74
79
  end
75
80
  end
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
- sleep Moneta::Server::TIMEOUT
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.stop
22
- @thread.join
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::DEFAULT_MAX_SIZE)
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::DEFAULT_MAX_SIZE + 1)
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::DEFAULT_MAX_SIZE + 1)
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
- stub_const('Moneta::Adapters::LRUHash::DEFAULT_MAX_COUNT', 5)
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 DEFAULT_MAX_COUNT allows when max_count is nil' do
107
- stub_const('Moneta::Adapters::LRUHash::DEFAULT_MAX_COUNT', 5)
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.backend.database.name).to eq database
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.backend.database.name).to eq database
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
- describe 'transformer_bson', proxy: :Transformer do
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
@@ -1,3 +1,5 @@
1
+ require 'uri'
2
+
1
3
  describe 'transformer_marshal_escape', proxy: :Transformer do
2
4
  moneta_build do
3
5
  Moneta.build do