Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file data resolvers #120

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
# Changelog
All notable changes to this project will be documented in this file.

## Unreleased
### Added
- Added file data resolver functionality:
- Added resolver for `file`
- Added resolver for `vault`
- Added DSL methods for defining custom resolvers
- Added support for kv storage for vault
- Fix using of non-kv storage

```ruby
Qonfig.define_resolver(:https) do |file_path|
Net::HTTP.get(URI("https:://#{file_path}"))
AnotherRegularDude marked this conversation as resolved.
Show resolved Hide resolved
end

class Config < Qonfig::DataSet
load_from_yaml "https://yamlhost.com/cool.yaml"
end
```

## [0.27.0] - 2022-01-12
### Changed
- Drop Ruby 2.5 support.
Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2049,6 +2049,8 @@ end
- **Daily work**
- [Save to JSON file](#save-to-json-file) (`save_to_json`)
- [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
- **Define file data resolvers**
- [Working with file data resolvers](#working-with-file-data-resolvers)

---

Expand Down Expand Up @@ -3155,6 +3157,35 @@ dynamic: 10

---

### Working with file data resolvers

You can define custom file data resolver

```ruby
Qonfig.define_resolver(:http) do |file_path|
final_url = URI("http://#{file_path}")
Net::HTTP.get(final_url)
rescue SocketError => error
raise Qonfig::FileNotFoundError, error.message
end

class AppConfig < Qonfig::DataSet
load_from_yaml "http://content-holder.com/settings.yml"
end
```

Also, you can set this file data resolver to default resolver.

```ruby
Qonfig.set_default_resolver :http

class AppConfig < Qonfig::DataSet
load_from_yaml "content-holder.com/settings.yml" # same as previous example
end
```

---

### Plugins

- [toml](#plugins-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`);
Expand Down Expand Up @@ -3290,6 +3321,9 @@ config = Config.new
- depends on `vault` gem ([link](https://github.com/hashicorp/vault-ruby)) (tested on `>= 0.1`);
- provides `.load_from_vault` (works in `.load_from_yaml` manner ([doc](#load-from-yaml-file)));
- provides `.expose_vault` (works in `.expose_yaml` manner ([doc](#expose-yaml)));
- provides custom file data resolver `vault://path/to/file/key.yml`;
- you can use version option to use specific version of config in kv storage (dsl and resolver);
- kv storage is used by default, but you can use logical storage by setting `use_kv` to `false`;

```ruby
# 1) require external dependency
Expand Down Expand Up @@ -3323,6 +3357,9 @@ Qonfig.plugin(:vault)
- External validation class with an importing api for better custom validations;
- Setting value changement trace (in `anyway_config` manner);
- Instantiation and reloading callbacks;
- Add supported formats for resolvers: for example, if you use vault resolver, you can't use json format;
- Refactor params passed to methods: use value objects instead;
- Refactor loaders: use base class for them;

## Build

Expand Down
14 changes: 14 additions & 0 deletions lib/qonfig.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module Qonfig
require_relative 'qonfig/imports'
require_relative 'qonfig/plugins'
require_relative 'qonfig/compacted'
require_relative 'qonfig/file_data_resolving'

# @api public
# @since 0.4.0
Expand All @@ -31,6 +32,10 @@ module Qonfig
# @since 0.20.0
extend Validation::PredefinitionMixin

# @api public
# @since 0.26.0
extend FileDataResolving::Mixin

# @since 0.20.0
define_validator(:integer) { |value| value.is_a?(Integer) }
# @since 0.20.0
Expand Down Expand Up @@ -60,6 +65,15 @@ module Qonfig
# @since 0.20.0
define_validator(:not_nil) { |value| value == nil }

# @since 0.26.0
define_resolver(:file) do |file_path|
::File.read(file_path)
rescue Errno::ENOENT => error
raise Qonfig::FileNotFoundError, error.message
end
# @since 0.26.0
set_default_resolver :file

# @since 0.12.0
register_plugin('toml', Qonfig::Plugins::TOML)
# @since 0.19.0
Expand Down
10 changes: 9 additions & 1 deletion lib/qonfig/commands/definition/load_from_json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,22 @@ class Qonfig::Commands::Definition::LoadFromJSON < Qonfig::Commands::Base
# @sicne 0.5.0
attr_reader :strict

# @return [Hash]
#
# @api private
# @since 0.26.0
attr_reader :file_resolve_options

# @param file_path [String, Pathname]
# @option strict [Boolean]
# @option file_resolve_options [Hash]
#
# @api private
# @since 0.5.0
def initialize(file_path, strict: true)
def initialize(file_path, strict: true, file_resolve_options: {})
@file_path = file_path
@strict = strict
@file_resolve_options = file_resolve_options
end

# @param data_set [Qonfig::DataSet]
Expand Down
13 changes: 11 additions & 2 deletions lib/qonfig/commands/definition/load_from_yaml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,22 @@ class Qonfig::Commands::Definition::LoadFromYAML < Qonfig::Commands::Base
# @since 0.2.0
attr_reader :strict

# @return [Hash]
#
# @api private
# @since 0.26.0
attr_reader :file_resolve_options

# @param file_path [String, Pathname]
# @option strict [Boolean]
# @option file_resolve_options [Hash]
#
# @api private
# @since 0.2.0
def initialize(file_path, strict: true)
def initialize(file_path, strict: true, file_resolve_options: {})
@file_path = file_path
@strict = strict
@file_resolve_options = file_resolve_options
end

# @param data_set [Qonfig::DataSet]
Expand All @@ -37,7 +45,8 @@ def initialize(file_path, strict: true)
# @api private
# @since 0.2.0
def call(data_set, settings)
yaml_data = Qonfig::Loaders::YAML.load_file(file_path, fail_on_unexist: strict)
yaml_data = Qonfig::Loaders::YAML
.load_file(file_path, fail_on_unexist: strict, **file_resolve_options)

raise(
Qonfig::IncompatibleYAMLStructureError,
Expand Down
16 changes: 13 additions & 3 deletions lib/qonfig/commands/instantiation/values_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,30 @@ class Qonfig::Commands::Instantiation::ValuesFile < Qonfig::Commands::Base
# @since 0.17.0
attr_reader :expose

# @return [Hash]
#
# @api private
# @since 0.26.0
attr_reader :file_resolve_options

# @param file_path [String, Symbol, Pathname]
# @param caller_location [String]
# @option format [String, Symbol]
# @option strict [Boolean]
# @option expose [NilClass, String, Symbol]
# @option file_resolve_options [Hash]
# @return [void]
#
# @api private
# @since 0.17.0
# @version 0.22.0
# @version 0.26.0
def initialize(
file_path,
caller_location,
format: DEFAULT_FORMAT,
strict: DEFAULT_STRICT_BEHAVIOR,
expose: NO_EXPOSE
expose: NO_EXPOSE,
file_resolve_options: {}
)
prevent_incompatible_attributes!(file_path, format, strict, expose)

Expand All @@ -85,6 +93,7 @@ def initialize(
@format = format
@strict = strict
@expose = expose
@file_resolve_options = file_resolve_options
end

# @param data_set [Qonfig::DataSet]
Expand Down Expand Up @@ -117,7 +126,8 @@ def load_settings_values
# @api private
# @since 0.17.0
def load_from_file
Qonfig::Loaders.resolve(format).load_file(file_path, fail_on_unexist: strict).tap do |values|
load_options = { fail_on_unexist: strict, **file_resolve_options }
Qonfig::Loaders.resolve(format).load_file(file_path, **load_options).tap do |values|
raise(
Qonfig::IncompatibleDataStructureError,
'Setting values must be a hash-like structure'
Expand Down
26 changes: 21 additions & 5 deletions lib/qonfig/data_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def reload!(settings_map = {}, &configurations)
# @option format [String, Symbol]
# @option strict [Boolean]
# @option expose [NilClass, String, Symbol] Environment key
# @option **file_resolve_options [Hash]
# @param configurations [Block]
# @return [void]
#
Expand All @@ -109,17 +110,26 @@ def reload!(settings_map = {}, &configurations)
# @api public
# @since 0.17.0
# @version 0.22.0
def load_from_file(file_path, format: :dynamic, strict: true, expose: nil, &configurations)
def load_from_file(
file_path,
format: :dynamic,
strict: true,
expose: nil,
**file_resolve_options,
&configurations
)
thread_safe_access do
load_setting_values_from_file(
file_path, format: format, strict: strict, expose: expose, &configurations
file_path, format: format, strict: strict,
expose: expose, file_resolve_options: file_resolve_options, &configurations
)
end
end

# @param file_path [String, Symbol, Pathname]
# @option strict [Boolean]
# @option expose [NilClass, String, Symbol] Environment key
# @option **file_resolve_options [Hash]
# @param configurations [Block]
# @return [void]
#
Expand All @@ -128,8 +138,11 @@ def load_from_file(file_path, format: :dynamic, strict: true, expose: nil, &conf
# @api public
# @since 0.17.0
# @version 0.22.0
def load_from_yaml(file_path, strict: true, expose: nil, &configurations)
load_from_file(file_path, format: :yml, strict: strict, expose: expose, &configurations)
def load_from_yaml(file_path, strict: true, expose: nil, **file_resolve_options, &configurations)
load_from_file(
file_path, format: :yml, strict: strict,
expose: expose, **file_resolve_options, &configurations
)
end

# @param file_path [String, Symbol, Pathname]
Expand Down Expand Up @@ -521,6 +534,7 @@ def load!(settings_map = {}, &configurations)
# @option strict [Boolean]
# @option expose [NilClass, String, Symbol]
# @option callcer_location [NilClass, String]
# @option **file_resolve_options [Hash]
# @param configurations [Block]
# @return [void]
#
Expand All @@ -535,10 +549,12 @@ def load_setting_values_from_file(
strict: true,
expose: nil,
caller_location: nil,
file_resolve_options: {},
&configurations
)
Qonfig::Commands::Instantiation::ValuesFile.new(
file_path, caller_location, format: format, strict: strict, expose: expose
file_path, caller_location, format: format,
strict: strict, expose: expose, file_resolve_options: file_resolve_options
).call(self, settings)
apply_settings(&configurations)
end
Expand Down
12 changes: 8 additions & 4 deletions lib/qonfig/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,16 @@ def compose(data_set_klass)

# @param file_path [String, Pathname]
# @option strict [Boolean]
# @option **file_resolve_options [Hash]
# @return [void]
#
# @see Qonfig::Commands::Definition::LoadFromYAML
#
# @api public
# @since 0.2.0
def load_from_yaml(file_path, strict: true)
def load_from_yaml(file_path, strict: true, **file_resolve_options)
definition_commands << Qonfig::Commands::Definition::LoadFromYAML.new(
file_path, strict: strict
file_path, strict: strict, file_resolve_options: file_resolve_options
)
end

Expand Down Expand Up @@ -202,14 +203,17 @@ def load_from_env(convert_values: false, prefix: nil, trim_prefix: false)

# @param file_path [String, Pathname]
# @option strict [Boolean]
# @option **file_resolve_options [Hash]
# @return [void]
#
# @see Qonfig::Commands::Definition::LoadFromJSON
#
# @api public
# @since 0.5.0
def load_from_json(file_path, strict: true)
definition_commands << Qonfig::Commands::Definition::LoadFromJSON.new(file_path, strict: strict)
def load_from_json(file_path, strict: true, **file_resolve_options)
definition_commands << Qonfig::Commands::Definition::LoadFromJSON.new(
file_path, strict: strict, file_resolve_options: file_resolve_options
)
end

# @param file_path [String, Pathname]
Expand Down
8 changes: 8 additions & 0 deletions lib/qonfig/file_data_resolving.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

# @api private
# @since 0.26.0
module Qonfig::FileDataResolving
require_relative 'file_data_resolving/resolver'
require_relative 'file_data_resolving/mixin'
end
24 changes: 24 additions & 0 deletions lib/qonfig/file_data_resolving/mixin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

# @api public
# @since 0.26.0
module Qonfig::FileDataResolving::Mixin
# @param scheme_name [Symbol,String]
# @param block [Block]
# @return [void]
#
# @api public
# @since 0.26.0
def define_resolver(scheme_name, &block)
Qonfig::FileDataResolving::Resolver.add_resolver!(scheme_name, block)
end

# @param scheme_name [Symbol,String]
# @return [void]
#
# @api public
# @since 0.26.0
def set_default_resolver(scheme_name)
Qonfig::FileDataResolving::Resolver.set_default_resolver!(scheme_name)
end
end
Loading