nxt_registry 0.1.5 → 0.3.2
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/.circleci/config.yml +56 -0
- data/.editorconfig +15 -0
- data/CHANGELOG.md +41 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +21 -18
- data/README.md +127 -55
- data/lib/nxt_registry.rb +34 -9
- data/lib/nxt_registry/recursive_registry.rb +1 -1
- data/lib/nxt_registry/registry.rb +101 -44
- data/lib/nxt_registry/{nested_registry_builder.rb → registry_builder.rb} +1 -1
- data/lib/nxt_registry/version.rb +1 -1
- data/nxt_registry.gemspec +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07e0435f3a696a2f8c653524b7d69e82445645ea852a3747e66079042844f8c7
|
4
|
+
data.tar.gz: 3377e35d63ec213f0008987aabdd459171c0e6e2a7dca0b2cd9624fc0d6feac3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 488ac1e21b87244d2ba97d5d767fba560bfa011a74cfebd78601d50509ddad7de9cfdb33d4ea6b1857f0c3ef9a7955d82ec40e9f4e6be64487b596341ed02dcd
|
7
|
+
data.tar.gz: 7cad3b456a2075274febf29ff6d25327c713e521a13d541d2640b9acb9344677f3ae89cfd9cdfdf72c3f8c316c4d8f67c4b9718100b74a84dc4c0669e84c1e13
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
2
|
+
#
|
3
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
4
|
+
#
|
5
|
+
version: 2
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
docker:
|
9
|
+
# specify the version you desire here
|
10
|
+
- image: circleci/ruby:2.6.5-node
|
11
|
+
environment:
|
12
|
+
BUNDLER_VERSION: 2.1.4
|
13
|
+
|
14
|
+
working_directory: ~/repo
|
15
|
+
|
16
|
+
steps:
|
17
|
+
- checkout
|
18
|
+
|
19
|
+
# Download and cache dependencies
|
20
|
+
- restore_cache:
|
21
|
+
keys:
|
22
|
+
- v1-dependencies-{{ checksum "Gemfile.lock" }}
|
23
|
+
|
24
|
+
- run: gem install bundler --version $BUNDLER_VERSION
|
25
|
+
|
26
|
+
- run:
|
27
|
+
name: install dependencies
|
28
|
+
command: |
|
29
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
30
|
+
|
31
|
+
- save_cache:
|
32
|
+
paths:
|
33
|
+
- ./vendor/bundle
|
34
|
+
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
|
35
|
+
|
36
|
+
# run tests!
|
37
|
+
- run:
|
38
|
+
name: run tests
|
39
|
+
command: |
|
40
|
+
mkdir /tmp/test-results
|
41
|
+
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
|
42
|
+
circleci tests split --split-by=timings)"
|
43
|
+
|
44
|
+
bundle exec rspec \
|
45
|
+
--format progress \
|
46
|
+
--format RspecJunitFormatter \
|
47
|
+
--out /tmp/test-results/rspec.xml \
|
48
|
+
--format progress \
|
49
|
+
$TEST_FILES
|
50
|
+
|
51
|
+
# collect reports
|
52
|
+
- store_test_results:
|
53
|
+
path: /tmp/test-results
|
54
|
+
- store_artifacts:
|
55
|
+
path: /tmp/test-results
|
56
|
+
destination: test-results
|
data/.editorconfig
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# EditorConfig is awesome: http://EditorConfig.org
|
2
|
+
|
3
|
+
# top-most EditorConfig file
|
4
|
+
root = true
|
5
|
+
|
6
|
+
# Unix-style newlines with a newline ending every file
|
7
|
+
[*]
|
8
|
+
end_of_line = lf
|
9
|
+
insert_final_newline = true
|
10
|
+
charset = utf-8
|
11
|
+
|
12
|
+
[*.{rb,yml,json,rake}]
|
13
|
+
indent_style = space
|
14
|
+
indent_size = 2
|
15
|
+
trim_trailing_whitespace = true
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# v0.3.2 2020-09-29
|
2
|
+
|
3
|
+
- Fix interface definition
|
4
|
+
|
5
|
+
# v0.3.1 2020-09-23
|
6
|
+
|
7
|
+
- Allow to define custom accessors for registries
|
8
|
+
|
9
|
+
# v0.3.0 2020-09-10
|
10
|
+
|
11
|
+
### Breaking Changes
|
12
|
+
|
13
|
+
- Toggled interface for resolve(!) and register(!)
|
14
|
+
- Allow to register values in nested registries
|
15
|
+
- Rename nested method into level
|
16
|
+
- Provide registry readers
|
17
|
+
- Remove Singleton extension
|
18
|
+
- Allow to resolve paths
|
19
|
+
- Pass key to default block when it takes an argument
|
20
|
+
|
21
|
+
[Compare v0.2.1...v0.3.0](https://github.com/nxt-insurance/nxt_registry/compare/v0.2.1...v0.3.0)
|
22
|
+
|
23
|
+
# v0.2.1 2020-08-14
|
24
|
+
|
25
|
+
### Fixed
|
26
|
+
|
27
|
+
- Fixed Registry#fetch to raise key error in case of missing key
|
28
|
+
|
29
|
+
[Compare v0.2.0...v0.2.1](https://github.com/nxt-insurance/nxt_registry/compare/v0.2.0...v0.2.1)
|
30
|
+
|
31
|
+
|
32
|
+
# v0.2.0 2020-07-24
|
33
|
+
|
34
|
+
### Added
|
35
|
+
|
36
|
+
- [internal] Added NxtRegistry::Singleton
|
37
|
+
- Added NxtRegistry::Singleton interface
|
38
|
+
- Make name optional and have object_id.to_s as default name
|
39
|
+
- Fix readme
|
40
|
+
|
41
|
+
[Compare v0.1.5...v0.2.0](https://github.com/nxt-insurance/nxt_registry/compare/v0.1.5...v0.2.0)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,46 +1,48 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
nxt_registry (0.
|
4
|
+
nxt_registry (0.3.2)
|
5
5
|
activesupport
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activesupport (6.0.
|
10
|
+
activesupport (6.0.3.3)
|
11
11
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
12
|
i18n (>= 0.7, < 2)
|
13
13
|
minitest (~> 5.1)
|
14
14
|
tzinfo (~> 1.1)
|
15
|
-
zeitwerk (~> 2.2)
|
16
|
-
coderay (1.1.
|
17
|
-
concurrent-ruby (1.1.
|
18
|
-
diff-lcs (1.
|
19
|
-
i18n (1.8.
|
15
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
16
|
+
coderay (1.1.3)
|
17
|
+
concurrent-ruby (1.1.7)
|
18
|
+
diff-lcs (1.4.4)
|
19
|
+
i18n (1.8.5)
|
20
20
|
concurrent-ruby (~> 1.0)
|
21
|
-
method_source (0.
|
22
|
-
minitest (5.14.
|
23
|
-
pry (0.
|
24
|
-
coderay (~> 1.1
|
25
|
-
method_source (~>
|
21
|
+
method_source (1.0.0)
|
22
|
+
minitest (5.14.2)
|
23
|
+
pry (0.13.1)
|
24
|
+
coderay (~> 1.1)
|
25
|
+
method_source (~> 1.0)
|
26
26
|
rake (12.3.3)
|
27
27
|
rspec (3.9.0)
|
28
28
|
rspec-core (~> 3.9.0)
|
29
29
|
rspec-expectations (~> 3.9.0)
|
30
30
|
rspec-mocks (~> 3.9.0)
|
31
|
-
rspec-core (3.9.
|
32
|
-
rspec-support (~> 3.9.
|
33
|
-
rspec-expectations (3.9.
|
31
|
+
rspec-core (3.9.2)
|
32
|
+
rspec-support (~> 3.9.3)
|
33
|
+
rspec-expectations (3.9.2)
|
34
34
|
diff-lcs (>= 1.2.0, < 2.0)
|
35
35
|
rspec-support (~> 3.9.0)
|
36
36
|
rspec-mocks (3.9.1)
|
37
37
|
diff-lcs (>= 1.2.0, < 2.0)
|
38
38
|
rspec-support (~> 3.9.0)
|
39
|
-
rspec-support (3.9.
|
39
|
+
rspec-support (3.9.3)
|
40
|
+
rspec_junit_formatter (0.4.1)
|
41
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
40
42
|
thread_safe (0.3.6)
|
41
|
-
tzinfo (1.2.
|
43
|
+
tzinfo (1.2.7)
|
42
44
|
thread_safe (~> 0.1)
|
43
|
-
zeitwerk (2.
|
45
|
+
zeitwerk (2.4.0)
|
44
46
|
|
45
47
|
PLATFORMS
|
46
48
|
ruby
|
@@ -51,6 +53,7 @@ DEPENDENCIES
|
|
51
53
|
pry
|
52
54
|
rake (~> 12)
|
53
55
|
rspec (~> 3.0)
|
56
|
+
rspec_junit_formatter
|
54
57
|
|
55
58
|
BUNDLED WITH
|
56
59
|
2.1.4
|
data/README.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
[](https://circleci.com/gh/nxt-insurance/nxt_registry)
|
2
|
+
|
1
3
|
# NxtRegistry
|
2
4
|
|
3
|
-
NxtRegistry is a simple implementation of the container pattern. It allows you to register and resolve values in nested
|
4
|
-
structures
|
5
|
+
`NxtRegistry` is a simple implementation of the container pattern. It allows you to register and resolve values in nested
|
6
|
+
structures.
|
5
7
|
|
6
8
|
## Installation
|
7
9
|
|
@@ -21,80 +23,148 @@ Or install it yourself as:
|
|
21
23
|
|
22
24
|
## Usage
|
23
25
|
|
26
|
+
### Simple use case
|
27
|
+
|
28
|
+
## Instance Level
|
29
|
+
|
30
|
+
To use `NxtRegistry` on an instance level simply include it and build registries like so:
|
31
|
+
|
24
32
|
```ruby
|
25
33
|
class Example
|
26
34
|
include NxtRegistry
|
27
35
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
nested :via, memoize: true, call: true, default: -> { [] } do
|
33
|
-
attrs %i[train car plane horse] # restrict the attributes that can be registered
|
34
|
-
resolver ->(value) { value } # do something with your registered value here
|
35
|
-
transform_keys ->(key) { key.upcase } # transform keys
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
36
|
+
registry :languages do
|
37
|
+
register(:ruby, 'Stone')
|
38
|
+
register(:python, 'Snake')
|
39
|
+
register(:javascript, 'undefined')
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
example = Example.new
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
# Register an array with a value by calling the accessor with a key, value pair
|
48
|
-
example.passengers.from(:a).to(:b).via(:train, ['Andy']) # => ['Andy']
|
49
|
-
|
50
|
-
# In case you try to register the same key again you will get an error
|
51
|
-
example.passengers.from(:a).to(:b).via(:train, ['Andy'])
|
52
|
-
# => NxtRegistry::Errors::KeyAlreadyRegisteredError
|
53
|
-
# (Key 'train' already registered in registry 'from.to.via')
|
54
|
-
# NxtRegistry::Errors::KeyAlreadyRegisteredError inherits from KeyError
|
55
|
-
|
56
|
-
# You can force values on the registry by using the bang method
|
57
|
-
example.passengers.from(:a).to(:b).via!(:train, ['Andreas'])
|
44
|
+
example.registry(:languages).resolve(:ruby) # => 'Stone'
|
45
|
+
```
|
58
46
|
|
59
|
-
|
60
|
-
# (meaning no KeyNotRegisteredError will be raised when nothing was registered)
|
61
|
-
example.passengers.from(:a).to(:b).via!(:train)
|
62
|
-
# Since there is a default defined for this registry, it does not make any sense
|
63
|
-
# since there is always a value. But you get the point...
|
47
|
+
Alternatively you can also create instances of `NxtRegistry::Registry`
|
64
48
|
|
65
|
-
|
66
|
-
|
67
|
-
|
49
|
+
```ruby
|
50
|
+
registry = NxtRegistry::Registry.new do
|
51
|
+
register(:andy, 'Andy')
|
52
|
+
register(:anthony, 'Anthony')
|
53
|
+
register(:aki, 'Aki')
|
54
|
+
end
|
68
55
|
|
69
|
-
#
|
70
|
-
example.passengers.from(:a).to(:b).via(:car) << 'Lütfi' # => ['Lütif']
|
71
|
-
example.passengers.from(:a).to(:b).via(:plane) += %w[Nils Rapha]
|
72
|
-
example.passengers.from(:a).to(:b).via(:plane) # => ['Nils', 'Rapha']
|
56
|
+
registry.resolve(:aki) # => 'Aki'
|
73
57
|
|
74
58
|
```
|
75
59
|
|
60
|
+
## Class Level
|
61
|
+
|
62
|
+
You can add registries on the class level simply by extending your class with `NxtRegistry`
|
76
63
|
|
77
64
|
```ruby
|
78
65
|
class OtherExample
|
79
66
|
extend NxtRegistry
|
80
|
-
|
81
|
-
|
82
|
-
REGISTRY = registry(:errors) do
|
83
|
-
# procs are called directly if not defined otherwise
|
67
|
+
|
68
|
+
registry(:errors) do
|
84
69
|
register(KeyError, ->(error) { puts 'KeyError handler' } )
|
85
70
|
register(ArgumentError, ->(error) { puts 'ArgumentError handler' } )
|
86
71
|
end
|
72
|
+
|
73
|
+
registry(:country_codes) do
|
74
|
+
register(:germany, :de)
|
75
|
+
register(:england, :uk)
|
76
|
+
register(:france, :fr)
|
77
|
+
end
|
87
78
|
end
|
88
79
|
|
89
|
-
|
90
|
-
# level where you want to register or resolve values. Equivalently to the named interface you can
|
91
|
-
# use register! and resolve! to softly resolve or forcfully register values.
|
92
|
-
OtherExample::REGISTRY.resolve(KeyError)
|
80
|
+
OtherExample.registry(:errors).resolve(KeyError)
|
93
81
|
# KeyError handler
|
94
82
|
# => nil
|
83
|
+
OtherExample.registry(:country_codes).resolve(:germany)
|
84
|
+
# => :de
|
85
|
+
```
|
86
|
+
### Readers
|
87
|
+
|
88
|
+
Access your defined registries with the `registry(:country_code)` method.
|
89
|
+
|
90
|
+
### Nesting registries
|
91
|
+
|
92
|
+
You can also simply nest registries like so:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
class Nested
|
96
|
+
extend NxtRegistry
|
97
|
+
|
98
|
+
registry :developers do
|
99
|
+
register(:frontend) do
|
100
|
+
register(:igor, 'Igor')
|
101
|
+
register(:ben, 'Ben')
|
102
|
+
end
|
103
|
+
|
104
|
+
register(:backend) do
|
105
|
+
register(:rapha, 'Rapha')
|
106
|
+
register(:aki, 'Aki')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
Nested.registry(:developers).resolve(:frontend, :igor)
|
112
|
+
# => 'Igor'
|
113
|
+
```
|
114
|
+
|
115
|
+
|
116
|
+
### Defining specific nesting levels of a registry
|
117
|
+
|
118
|
+
Another feature of `NxtRegistry` is that you can define the nesting levels for a registry. Levels allow you to dynamically
|
119
|
+
register values within the defined levels. This means that on any level the registry will resolve to another registry and
|
120
|
+
you can register values into a deeply nested structure.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
class Layer
|
124
|
+
extend NxtRegistry
|
125
|
+
|
126
|
+
registry :from do
|
127
|
+
level :to do
|
128
|
+
level :via
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# On every upper level every resolve returns a registry
|
134
|
+
Layer.registry(:from) # => Registry[from]
|
135
|
+
Layer.registry(:from).resolve(:munich) # => Registry[to] -> {}
|
136
|
+
Layer.registry(:from).resolve(:amsterdam) # => Registry[to] -> {}
|
137
|
+
Layer.registry(:from).resolve(:any_key) # => Registry[to] -> {}
|
138
|
+
Layer.registry(:from).resolve(:munich, :amsterdam) # => Registry[via] -> {}
|
139
|
+
|
140
|
+
# Register a value on the bottom level
|
141
|
+
Layer.registry(:from).resolve(:munich, :amsterdam).register(:train, -> { 'train' })
|
142
|
+
# Resolve the complete path
|
143
|
+
Layer.registry(:from).resolve(:munich, :amsterdam, :train) # => 'train'
|
144
|
+
```
|
145
|
+
|
146
|
+
For registries with multiple levels the normal syntax for registering and resolving becomes quite weird and unreadable. This is why
|
147
|
+
every registry can be accessed through it's name or a custom accessor. The above example then can be simplified as follows.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class Layer
|
151
|
+
extend NxtRegistry
|
152
|
+
|
153
|
+
registry :path, accessor: :from do # registry named path, can be accessed with .from(...)
|
154
|
+
level :to do
|
155
|
+
level :via
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
95
159
|
|
160
|
+
# Register a value
|
161
|
+
Layer.registry(:path).from(:munich).to(:amsterdam).via(:train, -> { 'train' })
|
162
|
+
# Resolve the complete path
|
163
|
+
Layer.registry(:path).from(:munich).to(:amsterdam).via(:train) # => 'train'
|
96
164
|
```
|
97
165
|
|
166
|
+
*Note that this feature is also available for registries with a single level only.*
|
167
|
+
|
98
168
|
### Restrict attributes to a certain set
|
99
169
|
|
100
170
|
Use `attrs` to restrict which attributes can be registered on a specific level.
|
@@ -113,7 +183,7 @@ registry :example, default: ->(value) { 'default' }
|
|
113
183
|
|
114
184
|
### Blocks
|
115
185
|
|
116
|
-
When you register a block value that can be called,
|
186
|
+
When you register a block value that can be called, it will automatically be called when you resolve the value.
|
117
187
|
If that's not what you want, you can configure your registry (on each level) not to call blocks directly by defining `call false`
|
118
188
|
|
119
189
|
```ruby
|
@@ -124,7 +194,7 @@ end
|
|
124
194
|
|
125
195
|
### Memoize
|
126
196
|
|
127
|
-
Values are memoized per default. Switch it off with `
|
197
|
+
Values are memoized per default. Switch it off with `memoize: false`
|
128
198
|
|
129
199
|
```ruby
|
130
200
|
registry :example, memoize: false do
|
@@ -141,7 +211,8 @@ registry.resolve(:one)
|
|
141
211
|
|
142
212
|
### Resolver
|
143
213
|
|
144
|
-
You can register a resolver block if you want to lay hands on your values after they have been resolved.
|
214
|
+
You can register a resolver block if you want to lay hands on your values after they have been resolved.
|
215
|
+
A resolver can be anything that implements `:call` to which the value is passed.
|
145
216
|
|
146
217
|
```ruby
|
147
218
|
registry :example do
|
@@ -155,9 +226,10 @@ registry.resolve(:one)
|
|
155
226
|
|
156
227
|
### Transform keys
|
157
228
|
|
158
|
-
NxtRegistry uses a plain ruby hash to store values internally. Per default all keys used are transformed with `&:to_s`.
|
159
|
-
Thus you can use symbols or strings to register and resolve values. If it's not what you want, switch it off with
|
160
|
-
or define your own key transformer by assigning a block to transform_keys:
|
229
|
+
`NxtRegistry` uses a plain ruby hash to store values internally. Per default all keys used are transformed with `&:to_s`.
|
230
|
+
Thus you can use symbols or strings to register and resolve values. If it's not what you want, switch it off with
|
231
|
+
`transform_keys false` or define your own key transformer by assigning a block to transform_keys:
|
232
|
+
`transform_keys ->(key) { key.upcase }`
|
161
233
|
|
162
234
|
```ruby
|
163
235
|
registry :example do
|
@@ -172,7 +244,7 @@ registry.resolve('BOMBSHELL')
|
|
172
244
|
### Customize registry errors
|
173
245
|
|
174
246
|
You can also customize what kind of errors are being raised in case a of a key was not registered or was already registered.
|
175
|
-
by providing
|
247
|
+
by providing blocks or a handler responding to :call for `on_key_already_registered` and `on_key_already_registered`
|
176
248
|
|
177
249
|
## Development
|
178
250
|
|
data/lib/nxt_registry.rb
CHANGED
@@ -1,18 +1,43 @@
|
|
1
1
|
require 'active_support/core_ext'
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
2
|
+
require 'nxt_registry/version'
|
3
|
+
require 'nxt_registry/blank'
|
4
|
+
require 'nxt_registry/attribute'
|
5
|
+
require 'nxt_registry/errors'
|
6
|
+
require 'nxt_registry/registry_builder'
|
7
|
+
require 'nxt_registry/registry'
|
8
|
+
require 'nxt_registry/recursive_registry'
|
9
9
|
|
10
10
|
module NxtRegistry
|
11
11
|
def registry(name, **options, &config)
|
12
|
-
Registry
|
12
|
+
build_registry(Registry, name, **options, &config)
|
13
13
|
end
|
14
14
|
|
15
15
|
def recursive_registry(name, **options, &config)
|
16
|
-
RecursiveRegistry
|
16
|
+
build_registry(RecursiveRegistry, name, **options, &config)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def build_registry(registry_class, name, **options, &config)
|
22
|
+
if registries.key?(name)
|
23
|
+
registry = registries.fetch(name)
|
24
|
+
if registry.configured
|
25
|
+
registry
|
26
|
+
else
|
27
|
+
raise_unconfigured_registry_accessed(name)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
registry = registry_class.new(name, **options, &config)
|
31
|
+
registries[name] ||= registry
|
32
|
+
registry
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def raise_unconfigured_registry_accessed(name)
|
37
|
+
raise ArgumentError, "The registry #{name} must be configured before accessed!"
|
38
|
+
end
|
39
|
+
|
40
|
+
def registries
|
41
|
+
@registries ||= {}
|
17
42
|
end
|
18
43
|
end
|
@@ -18,7 +18,7 @@ module NxtRegistry
|
|
18
18
|
attr_reader :options, :config, :level
|
19
19
|
|
20
20
|
def set_nested_builder_as_default
|
21
|
-
self.default =
|
21
|
+
self.default = RegistryBuilder.new do
|
22
22
|
RecursiveRegistry.new("level_#{(level + 1)}", **options.merge(level: (level + 1)), &config)
|
23
23
|
end
|
24
24
|
end
|
@@ -1,40 +1,52 @@
|
|
1
1
|
module NxtRegistry
|
2
2
|
class Registry
|
3
|
-
def initialize(name, **options, &config)
|
3
|
+
def initialize(name = object_id.to_s, **options, &config)
|
4
|
+
@options = options
|
4
5
|
@name = name
|
5
6
|
@parent = options[:parent]
|
6
7
|
@is_leaf = true
|
7
|
-
@namespace =
|
8
|
+
@namespace = build_namespace
|
8
9
|
@config = config
|
9
|
-
@options = options
|
10
10
|
@store = {}
|
11
11
|
@attrs = nil
|
12
|
+
@configured = false
|
12
13
|
|
13
14
|
setup_defaults(options)
|
14
15
|
configure(&config)
|
15
16
|
end
|
16
17
|
|
17
18
|
attr_reader :name
|
19
|
+
attr_accessor :configured
|
18
20
|
|
19
|
-
def
|
20
|
-
# TODO: Ensure that nesting is included in defined attrs
|
21
|
+
def level(name, **options, &config)
|
21
22
|
options = options.merge(parent: self)
|
22
23
|
|
23
|
-
if
|
24
|
+
if is_a_blank?(default)
|
24
25
|
self.is_leaf = false
|
25
26
|
|
26
|
-
self.default =
|
27
|
+
self.default = RegistryBuilder.new do
|
27
28
|
Registry.new(name, **options, &config)
|
28
29
|
end
|
29
30
|
|
31
|
+
# Call the builder once to guarantee we do not create a registry with a broken setup
|
30
32
|
default.call
|
31
|
-
elsif default.is_a?(
|
32
|
-
raise ArgumentError,
|
33
|
+
elsif default.is_a?(RegistryBuilder)
|
34
|
+
raise ArgumentError, 'Multiple nestings on the same level'
|
33
35
|
else
|
34
|
-
raise ArgumentError,
|
36
|
+
raise ArgumentError, 'Default values cannot be defined on registries that nest others'
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
40
|
+
def registry(name, **options, &config)
|
41
|
+
opts = options.merge(parent: self)
|
42
|
+
register(name, Registry.new(name, **opts, &config))
|
43
|
+
end
|
44
|
+
|
45
|
+
def registry!(name, **options, &config)
|
46
|
+
opts = options.merge(parent: self)
|
47
|
+
register!(name, Registry.new(name, **opts, &config))
|
48
|
+
end
|
49
|
+
|
38
50
|
def attr(name)
|
39
51
|
key = transformed_key(name)
|
40
52
|
raise KeyError, "Attribute #{key} already registered in #{namespace}" if attrs[key]
|
@@ -49,20 +61,40 @@ module NxtRegistry
|
|
49
61
|
args.each { |name| attr(name) }
|
50
62
|
end
|
51
63
|
|
52
|
-
def register(key, value)
|
53
|
-
|
64
|
+
def register(key = Blank.new, value = Blank.new, **options, &block)
|
65
|
+
if block_given?
|
66
|
+
if is_a_blank?(value)
|
67
|
+
registry(key, **options, &block)
|
68
|
+
else
|
69
|
+
raise_register_argument_error
|
70
|
+
end
|
71
|
+
else
|
72
|
+
__register(key, value, raise_on_key_already_registered: true)
|
73
|
+
end
|
54
74
|
end
|
55
75
|
|
56
|
-
def register!(key, value)
|
57
|
-
|
76
|
+
def register!(key = Blank.new, value = Blank.new, **options, &block)
|
77
|
+
if block_given?
|
78
|
+
if is_a_blank?(value)
|
79
|
+
registry!(key, **options, &block)
|
80
|
+
else
|
81
|
+
raise_register_argument_error
|
82
|
+
end
|
83
|
+
else
|
84
|
+
__register(key, value, raise_on_key_already_registered: false)
|
85
|
+
end
|
58
86
|
end
|
59
87
|
|
60
|
-
def resolve(
|
61
|
-
|
88
|
+
def resolve!(*keys)
|
89
|
+
keys.inject(self) do |current_registry, key|
|
90
|
+
current_registry.send(:__resolve, key, raise_on_key_not_registered: true)
|
91
|
+
end
|
62
92
|
end
|
63
93
|
|
64
|
-
def resolve
|
65
|
-
|
94
|
+
def resolve(*keys)
|
95
|
+
keys.inject(self) do |current_registry, key|
|
96
|
+
current_registry.send(:__resolve, key, raise_on_key_not_registered: false) || break
|
97
|
+
end
|
66
98
|
end
|
67
99
|
|
68
100
|
def to_h
|
@@ -70,11 +102,11 @@ module NxtRegistry
|
|
70
102
|
end
|
71
103
|
|
72
104
|
def [](key)
|
73
|
-
|
105
|
+
resolve!(key)
|
74
106
|
end
|
75
107
|
|
76
108
|
def []=(key, value)
|
77
|
-
|
109
|
+
register(key, value)
|
78
110
|
end
|
79
111
|
|
80
112
|
def keys
|
@@ -93,11 +125,11 @@ module NxtRegistry
|
|
93
125
|
store.exclude?(transformed_key(key))
|
94
126
|
end
|
95
127
|
|
96
|
-
def fetch(key,
|
97
|
-
store.fetch(transformed_key(key),
|
128
|
+
def fetch(key, *args, &block)
|
129
|
+
store.fetch(transformed_key(key), *args, &block)
|
98
130
|
end
|
99
131
|
|
100
|
-
delegate :size, :values, :each, to: :store
|
132
|
+
delegate :size, :values, :each, :freeze, to: :store
|
101
133
|
|
102
134
|
def configure(&block)
|
103
135
|
define_accessors
|
@@ -111,6 +143,8 @@ module NxtRegistry
|
|
111
143
|
instance_exec(&block)
|
112
144
|
end
|
113
145
|
end
|
146
|
+
|
147
|
+
self.configured = true
|
114
148
|
end
|
115
149
|
|
116
150
|
def to_s
|
@@ -121,44 +155,43 @@ module NxtRegistry
|
|
121
155
|
|
122
156
|
private
|
123
157
|
|
124
|
-
attr_reader :namespace, :parent, :config, :store, :options
|
125
|
-
attr_accessor :is_leaf
|
158
|
+
attr_reader :namespace, :parent, :config, :store, :options, :accessor
|
159
|
+
attr_accessor :is_leaf, :interface_defined
|
126
160
|
|
127
161
|
def is_leaf?
|
128
162
|
@is_leaf
|
129
163
|
end
|
130
164
|
|
131
|
-
def __register(key, value,
|
165
|
+
def __register(key, value, raise_on_key_already_registered: true)
|
132
166
|
key = transformed_key(key)
|
133
167
|
|
134
168
|
raise ArgumentError, "Not allowed to register values in a registry that contains nested registries" unless is_leaf
|
135
169
|
raise KeyError, "Keys are restricted to #{attrs.keys}" if attribute_not_allowed?(key)
|
136
170
|
|
137
|
-
on_key_already_registered && on_key_already_registered.call(key) if store[key] &&
|
171
|
+
on_key_already_registered && on_key_already_registered.call(key) if store[key] && raise_on_key_already_registered
|
138
172
|
|
139
173
|
store[key] = value
|
140
174
|
end
|
141
175
|
|
142
|
-
def __resolve(key,
|
176
|
+
def __resolve(key, raise_on_key_not_registered: true)
|
143
177
|
key = transformed_key(key)
|
144
178
|
|
145
179
|
value = if is_leaf?
|
146
|
-
if store
|
180
|
+
if store.key?(key)
|
147
181
|
store.fetch(key)
|
148
182
|
else
|
149
|
-
if
|
150
|
-
return unless
|
183
|
+
if is_a_blank?(default)
|
184
|
+
return unless raise_on_key_not_registered
|
151
185
|
|
152
186
|
on_key_not_registered && on_key_not_registered.call(key)
|
153
187
|
else
|
154
|
-
value = resolve_default
|
188
|
+
value = resolve_default(key)
|
155
189
|
return value unless memoize
|
156
190
|
|
157
191
|
store[key] ||= value
|
158
192
|
end
|
159
193
|
end
|
160
194
|
else
|
161
|
-
# Call nested registry builder when we are not a leaf
|
162
195
|
store[key] ||= default.call
|
163
196
|
end
|
164
197
|
|
@@ -176,29 +209,37 @@ module NxtRegistry
|
|
176
209
|
end
|
177
210
|
|
178
211
|
def define_interface
|
179
|
-
|
180
|
-
|
212
|
+
return if interface_defined
|
213
|
+
|
214
|
+
raise_invalid_accessor_name(accessor) if respond_to?(accessor)
|
215
|
+
accessor_with_bang = "#{accessor}!"
|
216
|
+
raise_invalid_accessor_name(accessor_with_bang) if respond_to?(accessor_with_bang)
|
217
|
+
|
218
|
+
define_singleton_method accessor do |key = Blank.new, value = Blank.new|
|
219
|
+
return self if is_a_blank?(key)
|
181
220
|
|
182
221
|
key = transformed_key(key)
|
183
222
|
|
184
|
-
if
|
223
|
+
if is_a_blank?(value)
|
185
224
|
resolve(key)
|
186
225
|
else
|
187
226
|
register(key, value)
|
188
227
|
end
|
189
228
|
end
|
190
229
|
|
191
|
-
define_singleton_method
|
192
|
-
return self if
|
230
|
+
define_singleton_method accessor_with_bang do |key = Blank.new, value = Blank.new|
|
231
|
+
return self if is_a_blank?(key)
|
193
232
|
|
194
233
|
key = transformed_key(key)
|
195
234
|
|
196
|
-
if
|
235
|
+
if is_a_blank?(value)
|
197
236
|
resolve!(key)
|
198
237
|
else
|
199
238
|
register!(key, value)
|
200
239
|
end
|
201
240
|
end
|
241
|
+
|
242
|
+
self.interface_defined = true
|
202
243
|
end
|
203
244
|
|
204
245
|
def setup_defaults(options)
|
@@ -207,6 +248,7 @@ module NxtRegistry
|
|
207
248
|
@call = options.fetch(:call) { true }
|
208
249
|
@resolver = options.fetch(:resolver, false)
|
209
250
|
@transform_keys = options.fetch(:transform_keys) { ->(key) { key.to_s } }
|
251
|
+
@accessor = options.fetch(:accessor) { name }
|
210
252
|
|
211
253
|
@on_key_already_registered = options.fetch(:on_key_already_registered) { ->(key) { raise_key_already_registered_error(key) } }
|
212
254
|
@on_key_not_registered = options.fetch(:on_key_not_registered) { ->(key) { raise_key_not_registered_error(key) } }
|
@@ -215,10 +257,9 @@ module NxtRegistry
|
|
215
257
|
def define_accessors
|
216
258
|
%w[default memoize call resolver transform_keys on_key_already_registered on_key_not_registered].each do |attribute|
|
217
259
|
define_singleton_method attribute do |value = Blank.new, &block|
|
218
|
-
# TODO: Allowing a block does not make sense for memoize and call?!
|
219
260
|
value = block if block
|
220
261
|
|
221
|
-
if
|
262
|
+
if is_a_blank?(value)
|
222
263
|
instance_variable_get("@#{attribute}")
|
223
264
|
else
|
224
265
|
instance_variable_set("@#{attribute}", value)
|
@@ -237,9 +278,9 @@ module NxtRegistry
|
|
237
278
|
attrs.keys.exclude?(transformed_key(key))
|
238
279
|
end
|
239
280
|
|
240
|
-
def resolve_default
|
281
|
+
def resolve_default(key)
|
241
282
|
if call && default.respond_to?(:call)
|
242
|
-
default.call
|
283
|
+
default.arity > 0 ? default.call(key) : default.call
|
243
284
|
else
|
244
285
|
default
|
245
286
|
end
|
@@ -256,7 +297,7 @@ module NxtRegistry
|
|
256
297
|
def transformed_key(key)
|
257
298
|
@transformed_key ||= {}
|
258
299
|
@transformed_key[key] ||= begin
|
259
|
-
if transform_keys && !
|
300
|
+
if transform_keys && !is_a_blank?(key)
|
260
301
|
transform_keys.call(key)
|
261
302
|
else
|
262
303
|
key
|
@@ -269,5 +310,21 @@ module NxtRegistry
|
|
269
310
|
@store = original.send(:store).deep_dup
|
270
311
|
@options = original.send(:options).deep_dup
|
271
312
|
end
|
313
|
+
|
314
|
+
def build_namespace
|
315
|
+
parent ? name.to_s.prepend("#{parent.send(:namespace)}.") : name.to_s
|
316
|
+
end
|
317
|
+
|
318
|
+
def raise_register_argument_error
|
319
|
+
raise ArgumentError, 'Either provide a key value pair or a block to register'
|
320
|
+
end
|
321
|
+
|
322
|
+
def is_a_blank?(value)
|
323
|
+
value.is_a?(Blank)
|
324
|
+
end
|
325
|
+
|
326
|
+
def raise_invalid_accessor_name(name)
|
327
|
+
raise ArgumentError, "#{self} already implements a method named: #{name}. Please choose a different accessor name"
|
328
|
+
end
|
272
329
|
end
|
273
330
|
end
|
data/lib/nxt_registry/version.rb
CHANGED
data/nxt_registry.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = "nxt_registry"
|
7
7
|
spec.version = NxtRegistry::VERSION
|
8
8
|
spec.authors = ["Andreas Robecke", "Nils Sommer", "Raphael Kallensee", "Lütfi Demirci"]
|
9
|
-
spec.email = [
|
9
|
+
spec.email = ['a.robecke@hellogetsafe.com', 'andreas@robecke.de']
|
10
10
|
|
11
11
|
spec.summary = %q{nxt_registry is a simple implementation of the container pattern}
|
12
12
|
spec.homepage = "https://github.com/nxt-insurance"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nxt_registry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Robecke
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2020-
|
14
|
+
date: 2020-09-30 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: activesupport
|
@@ -85,15 +85,19 @@ dependencies:
|
|
85
85
|
version: '0'
|
86
86
|
description:
|
87
87
|
email:
|
88
|
-
- a.robecke@
|
88
|
+
- a.robecke@hellogetsafe.com
|
89
|
+
- andreas@robecke.de
|
89
90
|
executables: []
|
90
91
|
extensions: []
|
91
92
|
extra_rdoc_files: []
|
92
93
|
files:
|
94
|
+
- ".circleci/config.yml"
|
95
|
+
- ".editorconfig"
|
93
96
|
- ".gitignore"
|
94
97
|
- ".rakeTasks"
|
95
98
|
- ".rspec"
|
96
99
|
- ".travis.yml"
|
100
|
+
- CHANGELOG.md
|
97
101
|
- Gemfile
|
98
102
|
- Gemfile.lock
|
99
103
|
- LICENSE.txt
|
@@ -105,9 +109,9 @@ files:
|
|
105
109
|
- lib/nxt_registry/attribute.rb
|
106
110
|
- lib/nxt_registry/blank.rb
|
107
111
|
- lib/nxt_registry/errors.rb
|
108
|
-
- lib/nxt_registry/nested_registry_builder.rb
|
109
112
|
- lib/nxt_registry/recursive_registry.rb
|
110
113
|
- lib/nxt_registry/registry.rb
|
114
|
+
- lib/nxt_registry/registry_builder.rb
|
111
115
|
- lib/nxt_registry/version.rb
|
112
116
|
- nxt_registry.gemspec
|
113
117
|
homepage: https://github.com/nxt-insurance
|