nxt_registry 0.3.4 → 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3c4dec7379f1076565a8fa2306e9aba2bfbd12f1191648929635db61a7b4a94
4
- data.tar.gz: '081a471bb51c5e53089ee8341f439b8b70bdb22bb45c24add42673210ecacd0c'
3
+ metadata.gz: 3263cdc8c48171dfc6ecfe2bbe2eedb97d72e0ab8d367945ed04ec2480b2caa0
4
+ data.tar.gz: a7f48fa6c75182d1489cdcb28f19127892bd63b3de655e3e0db40e1b81204031
5
5
  SHA512:
6
- metadata.gz: 7913f84e7bfea3f58efd487eb9bbdc34c988fb3297101aa52525e530c8a6bec5b2d5bc6dfc81bd188f4145c99a74dbbb65b556b6acf847b16df529a65b612999
7
- data.tar.gz: 59fe97c3fe3bbc653bd63f6e7bd835d08189136fc328d9ce0fa62e8199dcc311fa152d2d0ebedb1b7a580b3ef20a6253dfbb02c605a700f88a42076bd57935be
6
+ metadata.gz: 672c5855be89793548452a157d5c196b7a79271269ff43d056b18a2711b2c6ce7fb7cccca11892e05161fcf682eed15b55d7194bc27968d0d99b1066ae8b5ecc
7
+ data.tar.gz: ae6e5c77734afc4d9f732e7179fa62ef1f4b96c5a1278632cb91ce79ea33b803bd22b83f4084711d674698b786f6749033349ed541b152c05b43532936d53df3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ # v0.3.10 2021-03-30
2
+ - Do not synchronize resolve with mutex
3
+
4
+ # v0.3.9 2021-03-10
5
+
6
+ - Synchronize access with Mutex
7
+ - Add key_resolver
8
+ - Fix: Inherit options set through accessors
9
+
10
+ # v0.3.5 2020-12-23
11
+
12
+ - Allow to inherit options in nested registries
13
+
14
+ # v0.3.5 2020-12-04
15
+
16
+ - Allow patterns as keys
17
+
1
18
  # v0.3.4 2020-12-04
2
19
 
3
20
  - Bring back Singleton
data/Gemfile.lock CHANGED
@@ -1,26 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nxt_registry (0.3.4)
4
+ nxt_registry (0.3.10)
5
5
  activesupport
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (6.0.3.4)
10
+ activesupport (6.1.3.2)
11
11
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
- i18n (>= 0.7, < 2)
13
- minitest (~> 5.1)
14
- tzinfo (~> 1.1)
15
- zeitwerk (~> 2.2, >= 2.2.2)
12
+ i18n (>= 1.6, < 2)
13
+ minitest (>= 5.1)
14
+ tzinfo (~> 2.0)
15
+ zeitwerk (~> 2.3)
16
16
  coderay (1.1.3)
17
- concurrent-ruby (1.1.7)
17
+ concurrent-ruby (1.1.8)
18
18
  diff-lcs (1.4.4)
19
- i18n (1.8.5)
19
+ i18n (1.8.10)
20
20
  concurrent-ruby (~> 1.0)
21
21
  method_source (1.0.0)
22
- minitest (5.14.2)
23
- pry (0.13.1)
22
+ minitest (5.14.4)
23
+ pry (0.14.1)
24
24
  coderay (~> 1.1)
25
25
  method_source (~> 1.0)
26
26
  rake (12.3.3)
@@ -28,24 +28,24 @@ GEM
28
28
  rspec-core (~> 3.10.0)
29
29
  rspec-expectations (~> 3.10.0)
30
30
  rspec-mocks (~> 3.10.0)
31
- rspec-core (3.10.0)
31
+ rspec-core (3.10.1)
32
32
  rspec-support (~> 3.10.0)
33
- rspec-expectations (3.10.0)
33
+ rspec-expectations (3.10.1)
34
34
  diff-lcs (>= 1.2.0, < 2.0)
35
35
  rspec-support (~> 3.10.0)
36
- rspec-mocks (3.10.0)
36
+ rspec-mocks (3.10.1)
37
37
  diff-lcs (>= 1.2.0, < 2.0)
38
38
  rspec-support (~> 3.10.0)
39
- rspec-support (3.10.0)
39
+ rspec-support (3.10.1)
40
40
  rspec_junit_formatter (0.4.1)
41
41
  rspec-core (>= 2, < 4, != 2.12.0)
42
- thread_safe (0.3.6)
43
- tzinfo (1.2.8)
44
- thread_safe (~> 0.1)
42
+ tzinfo (2.0.4)
43
+ concurrent-ruby (~> 1.0)
45
44
  zeitwerk (2.4.2)
46
45
 
47
46
  PLATFORMS
48
47
  ruby
48
+ x86_64-darwin-19
49
49
 
50
50
  DEPENDENCIES
51
51
  bundler (~> 2.0)
@@ -56,4 +56,4 @@ DEPENDENCIES
56
56
  rspec_junit_formatter
57
57
 
58
58
  BUNDLED WITH
59
- 2.1.4
59
+ 2.2.4
data/README.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  # NxtRegistry
4
4
 
5
- `NxtRegistry` is a simple implementation of the container pattern. It allows you to register and resolve values in nested
6
- structures.
5
+ `NxtRegistry` is a simple container that allows you to register and resolve values in nested structures.
7
6
 
8
7
  ## Installation
9
8
 
@@ -82,6 +81,30 @@ OtherExample.registry(:errors).resolve(KeyError)
82
81
  OtherExample.registry(:country_codes).resolve(:germany)
83
82
  # => :de
84
83
  ```
84
+
85
+ ## Register Patterns
86
+
87
+ You can also register values with patterns as keys. Non pattern keys are always evaluated first and then patterns
88
+ will be tried to match by definition sequence.
89
+
90
+ ```ruby
91
+ class Example
92
+ extend NxtRegistry
93
+
94
+ registry :status_codes do
95
+ register(/\A4\d{2}\z/, 'Client errors')
96
+ register(/\A5.*\z/, 'Server errors')
97
+ register('422', 'Unprocessable Entity')
98
+ register(:'503', 'Internal Server Error')
99
+ end
100
+ end
101
+
102
+ Example.registry(:status_codes).resolve('503') # => "Internal Server Error"
103
+ Example.registry(:status_codes).resolve(503) # => "Internal Server Error"
104
+ Example.registry(:status_codes).resolve(422) # => "Unprocessable Entity"
105
+ Example.registry(:status_codes).resolve(404) # => "Client Errors"
106
+ ```
107
+
85
108
  ### Readers
86
109
 
87
110
  Access your defined registries with the `registry(:country_code)` method.
@@ -111,6 +134,23 @@ Nested.registry(:developers).resolve(:frontend, :igor)
111
134
  # => 'Igor'
112
135
  ```
113
136
 
137
+ #### Inherit options in nested registries
138
+
139
+ ```ruby
140
+ class Nested
141
+ extend NxtRegistry
142
+
143
+ registry :developers, default: 'options can be inherited' do
144
+ register(:frontend, inherit_options: true) do
145
+ register(:igor, 'Igor')
146
+ register(:ben, 'Ben')
147
+ end
148
+ end
149
+ end
150
+
151
+ Nested.registry(:developers).resolve(:frontend, :blank)
152
+ # => 'options can be inherited'
153
+ ```
114
154
 
115
155
  ### Defining specific nesting levels of a registry
116
156
 
@@ -164,12 +204,21 @@ Layer.registry(:path).from(:munich).to(:amsterdam).via(:train) # => 'train'
164
204
 
165
205
  *Note that this feature is also available for registries with a single level only.*
166
206
 
167
- ### Restrict attributes to a certain set
207
+ ### Restrict keys to a certain set
168
208
 
169
- Use `attrs` to restrict which attributes can be registered on a specific level.
209
+ Use `allowed_keys` to restrict which keys can be registered on a specific level.
170
210
 
171
211
  ```ruby
172
- registry :example, attrs: %w[one two three]
212
+ registry :example, allowed_keys: %w[one two three]
213
+ ```
214
+
215
+ ### Require a certain set of keys to be registered
216
+
217
+ Use `required_keys` to enforce a certain set of keys to be registered on a specific level. This is especially helpful
218
+ if you use registries in multiple places and you want to ensure they all register the same set of keys.
219
+
220
+ ```ruby
221
+ registry :example, required_keys: %w[one two three]
173
222
  ```
174
223
 
175
224
  ### Default values
@@ -208,19 +257,21 @@ registry.resolve(:one)
208
257
  # => 2020-01-02 23:56:18 +0100
209
258
  ```
210
259
 
211
- ### Resolver
260
+ ### Resolve callbacks
212
261
 
213
- You can register a resolver block if you want to lay hands on your values after they have been resolved.
214
- A resolver can be anything that implements `:call` to which the value is passed.
262
+ You can hook into the before and after resolver callbacks in case you need to lay hands on your values
263
+ before and / or after resolving. A callback can be anything that implements `:call` to which the value is passed.
215
264
 
216
265
  ```ruby
217
266
  registry :example do
218
- resolver ->(value) { value * 2 }
219
- register(:one, 1)
267
+ key_resolver ->(key) { key.strip }
268
+ resolver ->(value) { value.upcase }
269
+
270
+ register(:input, 'output')
220
271
  end
221
272
 
222
- registry.resolve(:one)
223
- # => 2
273
+ registry.resolve(' input ')
274
+ # => 'OUTPUT'
224
275
  ```
225
276
 
226
277
  ### Transform keys
data/lib/nxt_registry.rb CHANGED
@@ -1,7 +1,7 @@
1
+ require 'active_support'
1
2
  require 'active_support/core_ext'
2
3
  require 'nxt_registry/version'
3
4
  require 'nxt_registry/blank'
4
- require 'nxt_registry/attribute'
5
5
  require 'nxt_registry/errors'
6
6
  require 'nxt_registry/registry_builder'
7
7
  require 'nxt_registry/registry'
@@ -2,5 +2,7 @@ module NxtRegistry
2
2
  module Errors
3
3
  KeyAlreadyRegisteredError = Class.new(KeyError)
4
4
  KeyNotRegisteredError = Class.new(KeyError)
5
+ RequiredKeyMissing = Class.new(KeyError)
6
+ KeyNotAllowed = Class.new(KeyError)
5
7
  end
6
8
  end
@@ -8,14 +8,15 @@ module NxtRegistry
8
8
  @namespace = build_namespace
9
9
  @config = config
10
10
  @store = {}
11
- @attrs = nil
12
11
  @configured = false
12
+ @patterns = []
13
+ @config = config
14
+ @mutex = Mutex.new
13
15
 
14
- setup_defaults(options)
15
16
  configure(&config)
16
17
  end
17
18
 
18
- attr_reader :name
19
+ attr_reader :name, :mutex
19
20
  attr_accessor :configured
20
21
 
21
22
  def level(name, **options, &config)
@@ -38,27 +39,33 @@ module NxtRegistry
38
39
  end
39
40
 
40
41
  def registry(name, **options, &config)
41
- opts = options.merge(parent: self)
42
+ opts = conditionally_inherit_options(options)
42
43
  register(name, Registry.new(name, **opts, &config))
43
44
  end
44
45
 
45
46
  def registry!(name, **options, &config)
46
- opts = options.merge(parent: self)
47
+ opts = conditionally_inherit_options(options)
47
48
  register!(name, Registry.new(name, **opts, &config))
48
49
  end
49
50
 
50
- def attr(name)
51
- key = transformed_key(name)
52
- raise KeyError, "Attribute #{key} already registered in #{namespace}" if attrs[key]
51
+ def required_keys(*keys)
52
+ @required_keys ||= []
53
+ return @required_keys if keys.empty?
54
+
55
+ @required_keys += keys.map { |key| transformed_key(key) }
56
+ end
57
+
58
+ def allowed_keys(*keys)
59
+ @allowed_keys ||= []
60
+ return @allowed_keys if keys.empty?
53
61
 
54
- attrs[key] = Attribute.new(key, self)
62
+ @allowed_keys += keys.map { |key| transformed_key(key) }
55
63
  end
56
64
 
57
- def attrs(*args)
58
- @attrs ||= {}
59
- return @attrs unless args.any?
65
+ alias attrs allowed_keys # @deprecated
60
66
 
61
- args.each { |name| attr(name) }
67
+ def attr(key)
68
+ allowed_keys(key) # @deprecated
62
69
  end
63
70
 
64
71
  def register(key = Blank.new, value = Blank.new, **options, &block)
@@ -126,15 +133,18 @@ module NxtRegistry
126
133
  end
127
134
 
128
135
  def fetch(key, *args, &block)
129
- store.fetch(transformed_key(key), *args, &block)
136
+ key = matching_key(key)
137
+ store.fetch(key, *args, &block)
130
138
  end
131
139
 
132
140
  delegate :size, :values, :each, :freeze, to: :store
133
141
 
134
142
  def configure(&block)
143
+ setup_defaults(options)
135
144
  define_accessors
136
145
  define_interface
137
- attrs(*Array(options.fetch(:attrs, [])))
146
+ allowed_keys(*Array(options.fetch(:allowed_keys, [])))
147
+ required_keys(*Array(options.fetch(:required_keys, [])))
138
148
 
139
149
  if block.present?
140
150
  if block.arity == 1
@@ -144,6 +154,7 @@ module NxtRegistry
144
154
  end
145
155
  end
146
156
 
157
+ validate_required_keys_given
147
158
  self.configured = true
148
159
  end
149
160
 
@@ -151,34 +162,59 @@ module NxtRegistry
151
162
  "Registry[#{name}] -> #{store.to_s}"
152
163
  end
153
164
 
154
- alias_method :inspect, :to_s
165
+ alias inspect to_s
155
166
 
156
167
  private
157
168
 
158
- attr_reader :namespace, :parent, :config, :store, :options, :accessor
169
+ attr_reader :namespace, :parent, :config, :store, :options, :accessor, :patterns
159
170
  attr_accessor :is_leaf, :interface_defined
160
171
 
172
+ def conditionally_inherit_options(opts)
173
+ base = opts.delete(:inherit_options) ? options : {}
174
+ base.merge(opts).merge(parent: self)
175
+ end
176
+
177
+ def validate_required_keys_given
178
+ required_keys.each do |key|
179
+ next if store.key?(key)
180
+
181
+ raise Errors::RequiredKeyMissing, "Required key '#{key}' missing in #{self}"
182
+ end
183
+ end
184
+
161
185
  def is_leaf?
162
186
  @is_leaf
163
187
  end
164
188
 
165
189
  def __register(key, value, raise_on_key_already_registered: true)
166
- key = transformed_key(key)
190
+ mutex.synchronize do
191
+ key = if key.is_a?(Regexp)
192
+ patterns << key
193
+ key
194
+ else
195
+ transformed_key(key)
196
+ end
167
197
 
168
- raise ArgumentError, "Not allowed to register values in a registry that contains nested registries" unless is_leaf
169
- raise KeyError, "Keys are restricted to #{attrs.keys}" if attribute_not_allowed?(key)
198
+ raise ArgumentError, "Not allowed to register values in a registry that contains nested registries" unless is_leaf
199
+ raise KeyError, "Keys are restricted to #{allowed_keys}" if key_not_allowed?(key)
170
200
 
171
- on_key_already_registered && on_key_already_registered.call(key) if store[key] && raise_on_key_already_registered
201
+ on_key_already_registered && on_key_already_registered.call(key) if store[key] && raise_on_key_already_registered
172
202
 
173
- store[key] = value
203
+ store[key] = value
204
+ end
174
205
  end
175
206
 
176
207
  def __resolve(key, raise_on_key_not_registered: true)
208
+
177
209
  key = transformed_key(key)
178
210
 
179
211
  value = if is_leaf?
180
- if store.key?(key)
181
- store.fetch(key)
212
+ resolved_key = key_resolver.call(key)
213
+
214
+ if store.key?(resolved_key)
215
+ store.fetch(resolved_key)
216
+ elsif (pattern = matching_pattern(resolved_key))
217
+ store.fetch(pattern)
182
218
  else
183
219
  if is_a_blank?(default)
184
220
  return unless raise_on_key_not_registered
@@ -195,27 +231,40 @@ module NxtRegistry
195
231
  store[key] ||= default.call
196
232
  end
197
233
 
198
- value = if value.respond_to?(:call) && call && !value.is_a?(NxtRegistry::Registry)
199
- value.call(*[key].take(value.arity))
200
- else
201
- value
202
- end
234
+ value = call_or_value(value, key)
203
235
 
204
- if resolver
205
- resolver.call(value)
206
- else
207
- value
208
- end
236
+ resolver.call(value)
237
+ end
238
+
239
+ def matching_key(key)
240
+ key = transformed_key(key)
241
+ # if key is present it always wins over patterns
242
+ return key if store.key?(key)
243
+
244
+ matching_pattern(key) || key
245
+ end
246
+
247
+ def call_or_value(value, key)
248
+ return value unless call
249
+ return value if value.is_a?(NxtRegistry::Registry)
250
+ return value unless value.respond_to?(:call)
251
+
252
+ args = [key, value]
253
+ value.call(*args.take(value.arity))
254
+ end
255
+
256
+ def matching_pattern(key)
257
+ patterns.find { |pattern| key.match?(pattern) }
209
258
  end
210
259
 
211
260
  def define_interface
212
261
  return if interface_defined
213
262
 
214
- raise_invalid_accessor_name(accessor) if respond_to?(accessor)
263
+ raise_invalid_accessor_name(accessor) if respond_to?(accessor.to_s)
215
264
  accessor_with_bang = "#{accessor}!"
216
265
  raise_invalid_accessor_name(accessor_with_bang) if respond_to?(accessor_with_bang)
217
266
 
218
- define_singleton_method accessor do |key = Blank.new, value = Blank.new|
267
+ define_singleton_method accessor.to_s do |key = Blank.new, value = Blank.new|
219
268
  return self if is_a_blank?(key)
220
269
 
221
270
  key = transformed_key(key)
@@ -246,8 +295,9 @@ module NxtRegistry
246
295
  @default = options.fetch(:default) { Blank.new }
247
296
  @memoize = options.fetch(:memoize) { true }
248
297
  @call = options.fetch(:call) { true }
249
- @resolver = options.fetch(:resolver, false)
250
- @transform_keys = options.fetch(:transform_keys) { ->(key) { key.to_s } }
298
+ @resolver = options.fetch(:resolver, ->(val) { val })
299
+ @key_resolver = options.fetch(:key_resolver, ->(val) { val })
300
+ @transform_keys = options.fetch(:transform_keys) { ->(key) { key.is_a?(Regexp) ? key : key.to_s } }
251
301
  @accessor = options.fetch(:accessor) { name }
252
302
 
253
303
  @on_key_already_registered = options.fetch(:on_key_already_registered) { ->(key) { raise_key_already_registered_error(key) } }
@@ -255,27 +305,29 @@ module NxtRegistry
255
305
  end
256
306
 
257
307
  def define_accessors
258
- %w[default memoize call resolver transform_keys on_key_already_registered on_key_not_registered].each do |attribute|
308
+ %w[default memoize call resolver key_resolver transform_keys on_key_already_registered on_key_not_registered].each do |attribute|
259
309
  define_singleton_method attribute do |value = Blank.new, &block|
260
310
  value = block if block
261
311
 
262
312
  if is_a_blank?(value)
263
313
  instance_variable_get("@#{attribute}")
264
314
  else
315
+ options[attribute.to_sym] ||= value
265
316
  instance_variable_set("@#{attribute}", value)
266
317
  end
267
318
  end
268
319
 
269
320
  define_singleton_method "#{attribute}=" do |value|
321
+ options[attribute.to_sym] ||= value
270
322
  instance_variable_set("@#{attribute}", value)
271
323
  end
272
324
  end
273
325
  end
274
326
 
275
- def attribute_not_allowed?(key)
276
- return if attrs.empty?
327
+ def key_not_allowed?(key)
328
+ return if allowed_keys.empty?
277
329
 
278
- attrs.keys.exclude?(transformed_key(key))
330
+ allowed_keys.exclude?(transformed_key(key))
279
331
  end
280
332
 
281
333
  def resolve_default(key)
@@ -305,12 +357,6 @@ module NxtRegistry
305
357
  end
306
358
  end
307
359
 
308
- def initialize_copy(original)
309
- super
310
- @store = original.send(:store).deep_dup
311
- @options = original.send(:options).deep_dup
312
- end
313
-
314
360
  def build_namespace
315
361
  parent ? name.to_s.prepend("#{parent.send(:namespace)}.") : name.to_s
316
362
  end
@@ -326,5 +372,16 @@ module NxtRegistry
326
372
  def raise_invalid_accessor_name(name)
327
373
  raise ArgumentError, "#{self} already implements a method named: #{name}. Please choose a different accessor name"
328
374
  end
375
+
376
+ def initialize_copy(original)
377
+ super
378
+
379
+ @mutex = Mutex.new
380
+ containers = %i[store options]
381
+ variables = %i[patterns required_keys allowed_keys namespace on_key_already_registered on_key_not_registered]
382
+
383
+ containers.each { |c| instance_variable_set("@#{c}", original.send(c).deep_dup) }
384
+ variables.each { |v| instance_variable_set("@#{v}", original.send(v).dup) }
385
+ end
329
386
  end
330
387
  end
@@ -1,3 +1,3 @@
1
1
  module NxtRegistry
2
- VERSION = "0.3.4"
2
+ VERSION = '0.3.10'
3
3
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nxt_registry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Robecke
8
8
  - Nils Sommer
9
9
  - Raphael Kallensee
10
10
  - Lütfi Demirci
11
- autorequire:
11
+ autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2020-12-04 00:00:00.000000000 Z
14
+ date: 2021-06-10 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -83,7 +83,7 @@ dependencies:
83
83
  - - ">="
84
84
  - !ruby/object:Gem::Version
85
85
  version: '0'
86
- description:
86
+ description:
87
87
  email:
88
88
  - a.robecke@hellogetsafe.com
89
89
  - andreas@robecke.de
@@ -106,7 +106,6 @@ files:
106
106
  - bin/console
107
107
  - bin/setup
108
108
  - lib/nxt_registry.rb
109
- - lib/nxt_registry/attribute.rb
110
109
  - lib/nxt_registry/blank.rb
111
110
  - lib/nxt_registry/errors.rb
112
111
  - lib/nxt_registry/recursive_registry.rb
@@ -122,7 +121,7 @@ metadata:
122
121
  allowed_push_host: https://rubygems.org
123
122
  homepage_uri: https://github.com/nxt-insurance
124
123
  source_code_uri: https://github.com/nxt-insurance/nxt_registry
125
- post_install_message:
124
+ post_install_message:
126
125
  rdoc_options: []
127
126
  require_paths:
128
127
  - lib
@@ -137,8 +136,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
136
  - !ruby/object:Gem::Version
138
137
  version: '0'
139
138
  requirements: []
140
- rubygems_version: 3.0.3
141
- signing_key:
139
+ rubygems_version: 3.2.15
140
+ signing_key:
142
141
  specification_version: 4
143
142
  summary: nxt_registry is a simple implementation of the container pattern
144
143
  test_files: []
@@ -1,17 +0,0 @@
1
- module NxtRegistry
2
- class Attribute
3
- def initialize(name, registry, **options)
4
- @name = name
5
- @registry = registry
6
- @namespace = [name, registry.send(:namespace)].join('.')
7
- end
8
-
9
- def eql?(other)
10
- { name => registry.object_id } == { other.send(:name) => other.send(:registry).object_id }
11
- end
12
-
13
- private
14
-
15
- attr_reader :name, :registry
16
- end
17
- end