https://github.com/nxt-insurance/nxt_registry
A simple registry to implement the container pattern
https://github.com/nxt-insurance/nxt_registry
container registry ruby ruby-on-rails
Last synced: about 1 month ago
JSON representation
A simple registry to implement the container pattern
- Host: GitHub
- URL: https://github.com/nxt-insurance/nxt_registry
- Owner: nxt-insurance
- License: mit
- Created: 2019-12-25T22:44:44.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2025-04-22T00:01:46.000Z (about 2 months ago)
- Last Synced: 2025-05-08T21:12:55.050Z (about 1 month ago)
- Topics: container, registry, ruby, ruby-on-rails
- Language: Ruby
- Homepage:
- Size: 104 KB
- Stars: 17
- Watchers: 14
- Forks: 0
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
README
[](https://circleci.com/gh/nxt-insurance/nxt_registry)
# NxtRegistry
`NxtRegistry` is a simple container that allows you to register and resolve values in nested structures.
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'nxt_registry'
```And then execute:
$ bundle
Or install it yourself as:
$ gem install nxt_registry
## Usage
### Simple use case
## Instance Level
If you simply need a single global instance of a registry include `NxtRegistry::Singleton`:
```ruby
class Example
include NxtRegistry::Singleton
registry do
register(:ruby, 'Stone')
register(:python, 'Snake')
register(:javascript, 'undefined')
end
endExample.resolve(:ruby) # => 'Stone'
```Alternatively you can simply create instances of `NxtRegistry::Registry`:
```ruby
registry = NxtRegistry::Registry.new do
register(:andy, 'Andy')
register(:anthony, 'Anthony')
register(:aki, 'Aki')
endregistry.resolve(:aki) # => 'Aki'
```
## Class Level
You can also add registries on the class level simply by extending your class with `NxtRegistry`
```ruby
class OtherExample
extend NxtRegistry
registry(:errors) do
register(KeyError, ->(error) { puts 'KeyError handler' } )
register(ArgumentError, ->(error) { puts 'ArgumentError handler' } )
endregistry(:country_codes) do
register(:germany, :de)
register(:england, :uk)
register(:france, :fr)
end
endOtherExample.registry(:errors).resolve(KeyError)
# KeyError handler
# => nil
OtherExample.registry(:country_codes).resolve(:germany)
# => :de
```## Register Patterns
You can also register values with patterns as keys. Non pattern keys are always evaluated first and then patterns
will be tried to match by definition sequence.```ruby
class Example
extend NxtRegistry
registry :status_codes do
register(/\A4\d{2}\z/, 'Client errors')
register(/\A5.*\z/, 'Server errors')
register('422', 'Unprocessable Entity')
register(:'503', 'Internal Server Error')
end
endExample.registry(:status_codes).resolve('503') # => "Internal Server Error"
Example.registry(:status_codes).resolve(503) # => "Internal Server Error"
Example.registry(:status_codes).resolve(422) # => "Unprocessable Entity"
Example.registry(:status_codes).resolve(404) # => "Client Errors"
```### Readers
Access your defined registries with the `registry(:country_code)` method.
### Nesting registries
You can also simply nest registries like so:
```ruby
class Nested
extend NxtRegistryregistry :developers do
register(:frontend) do
register(:igor, 'Igor')
register(:ben, 'Ben')
end
register(:backend) do
register(:rapha, 'Rapha')
register(:aki, 'Aki')
end
end
endNested.registry(:developers).resolve(:frontend, :igor)
# => 'Igor'
```#### Inherit options in nested registries
```ruby
class Nested
extend NxtRegistry
registry :developers, default: 'options can be inherited' do
register(:frontend, inherit_options: true) do
register(:igor, 'Igor')
register(:ben, 'Ben')
end
end
endNested.registry(:developers).resolve(:frontend, :blank)
# => 'options can be inherited'
```### Defining specific nesting levels of a registry
Another feature of `NxtRegistry` is that you can define the nesting levels for a registry. Levels allow you to dynamically
register values within the defined levels. This means that on any level the registry will resolve to another registry and
you can register values into a deeply nested structure.```ruby
class Layer
extend NxtRegistry
registry :from do
level :to do
level :via
end
end
end# On every upper level every resolve returns a registry
Layer.registry(:from) # => Registry[from]
Layer.registry(:from).resolve(:munich) # => Registry[to] -> {}
Layer.registry(:from).resolve(:amsterdam) # => Registry[to] -> {}
Layer.registry(:from).resolve(:any_key) # => Registry[to] -> {}
Layer.registry(:from).resolve(:munich, :amsterdam) # => Registry[via] -> {}# Register a value on the bottom level
Layer.registry(:from).resolve(:munich, :amsterdam).register(:train, -> { 'train' })
# Resolve the complete path
Layer.registry(:from).resolve(:munich, :amsterdam, :train) # => 'train'
```For registries with multiple levels the normal syntax for registering and resolving becomes quite weird and unreadable. This is why
every registry can be accessed through it's name or a custom accessor. The above example then can be simplified as follows.```ruby
class Layer
extend NxtRegistry
registry :path, accessor: :from do # registry named path, can be accessed with .from(...)
level :to do
level :via
end
end
end# Register a value
Layer.registry(:path).from(:munich).to(:amsterdam).via(:train, -> { 'train' })
# Resolve the complete path
Layer.registry(:path).from(:munich).to(:amsterdam).via(:train) # => 'train'
```*Note that this feature is also available for registries with a single level only.*
### Restrict keys to a certain set
Use `allowed_keys` to restrict which keys can be registered on a specific level.
```ruby
registry :example, allowed_keys: %w[one two three]
```### Require a certain set of keys to be registered
Use `required_keys` to enforce a certain set of keys to be registered on a specific level. This is especially helpful
if you use registries in multiple places and you want to ensure they all register the same set of keys.```ruby
registry :example, required_keys: %w[one two three]
```### Default values
Use `default` to register a default value that will be resolved in case an attribute was not registered.
```ruby
registry :example, default: ->(value) { 'default' }
```### Blocks
When you register a block value that can be called, it will automatically be called when you resolve the value.
If that's not what you want, you can configure your registry (on each level) not to call blocks directly by defining `call false````ruby
registry :example, call: false do
register(:one, ->(value) { 'Not called when resolved' } )
end
```### Memoize
Values are memoized per default. Switch it off with `memoize: false`
```ruby
registry :example, memoize: false do
register(:one, -> { Time.current } )
endregistry.resolve(:one)
# => 2020-01-02 23:56:15 +0100
registry.resolve(:one)
# => 2020-01-02 23:56:17 +0100
registry.resolve(:one)
# => 2020-01-02 23:56:18 +0100
```**IMPORTANT**: whenever you want your value to be evaluated anew every time it is resolved, you should always wrap it in a lambda.
For example, if you're resolving an ENV variable you should do it this way:
```ruby
registry :example do
register(:env_variable, -> { ENV['FEATURE_FLAG'] })
end
```In this case config can be reloaded on the fly, and tests can also overwrite feature flags, for example.
### Resolve callbacks
You can hook into the before and after resolver callbacks in case you need to lay hands on your values
before and / or after resolving. A callback can be anything that implements `:call` to which the value is passed.```ruby
registry :example do
key_resolver ->(key) { key.strip }
resolver ->(value) { value.upcase }
register(:input, 'output')
endregistry.resolve(' input ')
# => 'OUTPUT'
```### Transform keys
`NxtRegistry` uses a plain ruby hash to store values internally. Per default all keys used are transformed with `&:to_s`.
Thus you can use symbols or strings to register and resolve values. If it's not what you want, switch it off with
`transform_keys false` or define your own key transformer by assigning a block to transform_keys:
`transform_keys ->(key) { key.upcase }````ruby
registry :example do
transform_keys ->(key) { key.to_s.downcase }
register(:bombshell, 'hanna')
endregistry.resolve('BOMBSHELL')
# => 'hanna'
```### Customize registry errors
You can also customize what kind of errors are being raised in case a of a key was not registered or was already registered.
by providing blocks or a handler responding to :call for `on_key_already_registered` and `on_key_already_registered`## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/nxt_registry.
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).