Skip to content

Commit

Permalink
[importing] .import_settings / #export_settings
Browse files Browse the repository at this point in the history
  • Loading branch information
0exp committed Sep 23, 2019
1 parent eb178ab commit 35eb4d9
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 46 deletions.
2 changes: 1 addition & 1 deletion lib/qonfig.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module Qonfig
require_relative 'qonfig/dsl'
require_relative 'qonfig/data_set'
require_relative 'qonfig/configurable'
require_relative 'qonfig/importing'
require_relative 'qonfig/imports'
require_relative 'qonfig/plugins'

# @api public
Expand Down
45 changes: 45 additions & 0 deletions lib/qonfig/data_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,51 @@ def validate!
thread_safe_access { validator.validate! }
end

# @option all_variants [Boolean]
# @return [Array<String>]
#
# @api public
# @since 0.17.0
def keys(all_variants: false)
thread_safe_access { settings.__keys__(all_variants: all_variants) }
end

# @return [Array<String>]
#
# @api public
# @since 0.17.0
def root_keys
thread_safe_access { settings.__root_keys__ }
end

# @return [void]
#
# @api public
# @since 0.17.0
def export_settings(
exportable_object,
*exported_setting_keys,
prefix: Qonfig::Imports::Importer::EMPTY_PREFIX,
raw: false,
mappings: Qonfig::Imports::Importer::EMPTY_MAPPINGS
)
thread_safe_access do
unless exportable_object.is_a?(Module)
exportable_object = exportable_object.singleton_class
end

Qonfig::Imports::Importer.import!(
exportable_object,
self,
*exported_setting_keys,
prefix: prefix,
raw: raw,
mappings: mappings
)
end
end


private

# @return [Qonfig::Validator]
Expand Down
14 changes: 7 additions & 7 deletions lib/qonfig/importing.rb → lib/qonfig/imports.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

# @api public
# @since 0.17.0
module Qonfig::Importing
require_relative 'importing/importer'
module Qonfig::Imports
require_relative 'imports/importer'

class << self
# @param base_klass [Class]
Expand All @@ -12,13 +12,13 @@ class << self
# @api private
# @since 0.17.0
def included(base_klass)
base_klass.extend(DSL)
base_klass.extend(ClassMethods)
end
end

# @api private
# @since 0.17.0
module DSL
module ClassMethods
# @param config [Qonfig::DataSet]
# @param setting_keys [Array<String,Symbol>]
# @option prefix [String, Symbol]
Expand All @@ -31,11 +31,11 @@ module DSL
def import_settings(
config,
*setting_keys,
prefix: Qonfig::Importing::Importer::EMPTY_PREFIX,
prefix: Qonfig::Imports::Importer::EMPTY_PREFIX,
raw: false,
mappings: Qonfig::Importing::Importer::EMPTY_MAPPINGS
mappings: Qonfig::Imports::Importer::EMPTY_MAPPINGS
)
Qonfig::Importing::Importer.import!(
Qonfig::Imports::Importer.import!(
self, config, *setting_keys, prefix: prefix, raw: raw, mappings: mappings
)
end
Expand Down
53 changes: 32 additions & 21 deletions lib/qonfig/importing/importer.rb → lib/qonfig/imports/importer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# @api private
# @since 0.17.0
class Qonfig::Importing::Importer
class Qonfig::Imports::Importer
# @return [String]
#
# @api private
Expand Down Expand Up @@ -81,34 +81,45 @@ def initialize(
# @api private
# @since 0.17.0
def import!
imported_settings_interface = Module.new {}

imported_config.deep_each_setting do |setting_key, _setting_value|

binding.pry
imported_settings_interface = Module.new

key_matchers.each do |key_matcher|
# NOTE: step one: check that exported key is exist
raise(
Qonfig::UnknownSettingError,
"Setting with <#{setting_key}> key does not exist!"
) unless key_matchers.any? { |matcher| matcher.match?(setting_key) }

setting_key_path_sequence = setting_key.split('.')
access_method_name = setting_key_path_sequence.last
access_method_name = "#{prefix}#{access_method_name}" unless prefix.empty?

imported_settings_interface.module_eval do
unless raw
define_method(access_method_name) do
imported_config.slice_value(*setting_key_path_sequence)
end
else
define_method(access_method_name) do
imported_config.dig(*setting_key_path_sequence)
"Setting with <#{key_matcher.scope_pattern}> key does not exist!"
) unless (imported_config.keys(all_variants: true).any? do |setting_key|
key_matcher.match?(setting_key)
end)

# NOTE: step two: import matched keys
imported_config.keys(all_variants: true).each do |setting_key|
next unless key_matcher.match?(setting_key)

setting_key_path_sequence = setting_key.split('.')
access_method_name = setting_key_path_sequence.last
access_method_name = "#{prefix}#{access_method_name}" unless prefix.empty?

imported_settings_interface.module_exec(raw, imported_config) do |raw, imported_config|
unless raw
# NOTE: get setting value as a real value
define_method(access_method_name) do
imported_config.slice_value(*setting_key_path_sequence)
end
else
# NOTE: get setting value (or Qonfig::Settings object)
define_method(access_method_name) do
imported_config.dig(*setting_key_path_sequence)
end
end
end
end
end

# NOTE:
# step three: include new interface
# (no prenend cuz we want to give a user an ability to define its own methods with the same
# names that provides our new generated interface)
seeded_klass.include(imported_settings_interface)
end

Expand Down
70 changes: 66 additions & 4 deletions lib/qonfig/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,11 @@ def __subset__(*keys)
# rubocop:disable Metrics/LineLength
def __to_hash__(transform_key: BASIC_SETTING_KEY_TRANSFORMER, transform_value: BASIC_SETTING_VALUE_TRANSFORMER)
unless transform_key.is_a?(Proc)
::Kernel.raise(Qonfig::IncorrectKeyTransformerError, 'Key transformer should be a proc')
::Kernel.raise(Qonfig::IncorrectKeyTransformerError, 'Key transformer should be a type of proc')
end

unless transform_value.is_a?(Proc)
::Kernel.raise(Qonfig::IncorrectValueTransformerError, 'Value transformer should be a proc')
::Kernel.raise(Qonfig::IncorrectValueTransformerError, 'Value transformer should be a type of proc')
end

__lock__.thread_safe_access do
Expand All @@ -208,6 +208,23 @@ def __to_hash__(transform_key: BASIC_SETTING_KEY_TRANSFORMER, transform_value: B
# rubocop:enable Metrics/LineLength
alias_method :__to_h__, :__to_hash__

# @option all_variants [Boolean]
# @return [Array<String>]
#
# @api private
# @since 0.17.0
def __keys__(all_variants: false)
__lock__.thread_safe_access { __setting_keys__(all_variants: all_variants) }
end

# @return [Array<String>]
#
# @api private
# @since 0.17.0
def __root_keys__
__lock__.thread_safe_access { __root_setting_keys__ }
end

# @return [void]
#
# @api private
Expand Down Expand Up @@ -280,8 +297,53 @@ def __is_a_setting__(value)
# @since 0.2.0
attr_reader :__lock__

# @option all_variants [Boolean]
# @return [Array<String>]
#
# @api private
# @since 0.17.0
def __setting_keys__(all_variants: false)
# NOTE: generate a set of keys return simple 'a.b.c.d'
setting_keys_set = Set.new.tap do |setting_keys|
__deep_each_key_value_pair__ do |setting_key, _setting_value|
setting_keys << setting_key
end
end

if all_variants
# NOTE:
# We have { a: { b: { c: { d : 1 } } } }
# Its mean that we have these keys:
# - 'a' # => returns { b: { c: { d: 1 } } }
# - 'a.b' # => returns { c: { d: 1 } }
# - 'a.b.c' # => returns { d: 1 }
# - 'a.b.c.d' # => returns 1
# So, get them all :)

setting_keys_set.each_with_object(Set.new) do |setting_key, varianted_setting_keys|
setting_key_paths = setting_key.split('.')
combination_size = setting_key_paths.size

combination_size.times do |merged_key_patterns_count|
sub_setting_key = setting_key_paths.slice(0..merged_key_patterns_count).join('.')
varianted_setting_keys << sub_setting_key
end
end
else
setting_keys_set
end
end

# @return [Array<String>]
#
# @api private
# @since 0.17.0
def __root_setting_keys__
__options__.keys
end

# @param block [Proc]
# @return [Enumerable]
# @return [Enumerator]
#
# @yield [setting_key, setting_value]
# @yieldparam key [String]
Expand All @@ -295,7 +357,7 @@ def __each_key_value_pair__(&block)

# @param initial_setting_key [String, NilClass]
# @param block [Proc]
# @return [Enumerable]
# @return [Enumerator]
#
# @yield [setting_key, setting_value]
# @yieldparam setting_key [String]
Expand Down
14 changes: 7 additions & 7 deletions lib/qonfig/settings/key_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class Qonfig::Settings::KeyMatcher
# @since 0.13.0
INFINITE_REGEXP_PATTERN = '\.*.*'

# @return [String]
#
# @api private
# @since 0.13.0
attr_reader :scope_pattern

# @param scope_pattern [String, Symbol]
# @return [void]
#
Expand All @@ -50,7 +56,7 @@ def initialize(scope_pattern)
scope_pattern = scope_pattern.to_s if scope_pattern.is_a?(Symbol)
raise Qonfig::ArgumentError unless scope_pattern.is_a?(String)

@scope_pattern = scope_pattern
@scope_pattern = scope_pattern.dup.freeze
@scope_pattern_size = count_scope_pattern_size(scope_pattern)
@pattern_matcher = build_pattern_matcher(scope_pattern)
end
Expand All @@ -73,12 +79,6 @@ def match?(setting_key_pattern)
# @since 0.13.0
attr_reader :pattern_matcher

# @return [String]
#
# @api private
# @since 0.13.0
attr_reader :scope_pattern

# @return [Integer, Float::INFINITY]
#
# @api private
Expand Down
17 changes: 11 additions & 6 deletions spec/features/importing_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,22 @@ class ImportedConfig < Qonfig::DataSet
ImpConfig = ImportedConfig.new

class UltraSimpleApplication
include Qonfig::Importing
include Qonfig::Imports

import_settings(ImpConfig,
'credentials.login',
'credentials.password',
'job_que.options',
prefix: 'config_')
import_settings(
ImpConfig,
'credentials.login',
'credentials.password',
'job_que.options',
prefix: 'config_'
)
end

app = UltraSimpleApplication.new
# binding.pry

simple_object = Object.new
ImpConfig.export_settings(simple_object, 'credentials.login', prefix: 'config_')
# binding.pry
end
end

0 comments on commit 35eb4d9

Please sign in to comment.