kybus-configs 0.2.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f5670f8007db375cb7baf70d1ff2cb3ace9512564b85913d1e292b3596c707da
4
+ data.tar.gz: 12bc3dde94ad80d08887fe48d3e3e844ebe11989b434c18e62966956fef0ee3b
5
+ SHA512:
6
+ metadata.gz: f31753afabe20367535967d13e787edacbc7f3e14c9749c88268da5b8d760be4f84573840108d69516f6fb31737672d6c74ae4205d12c9ad0fb5d670538009b7
7
+ data.tar.gz: 924cb8cd8173904945000b326467089cff9d9755a73d71d812496fb17aaba2f89e0ee5150fad791aeefe489eeaec623645d8830a1ba26c374d8c15466e739428
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configs/configuration_manager'
4
+ require_relative 'configs/service_manager'
5
+ require_relative 'configs/autoconfigs'
6
+ require_relative 'configs/version'
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Autoconfigs
6
+ extend Kybus::DRY::ResourceInjector
7
+
8
+ def self.from_config!(adapter, config, loading_path = '.')
9
+ require_relative "autoconfigs/#{loading_path}/#{adapter}"
10
+
11
+ factory = resource(adapter)
12
+ factory.from_config(config)
13
+ end
14
+ end
15
+
16
+ def self.auto_load!
17
+ Kybus::Configuration::ServiceManager.auto_load!
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Aws < ServiceManager
7
+ def self.from_config(config)
8
+ aws = new(config, 'aws/')
9
+ aws.configure!
10
+ aws.all_services
11
+ end
12
+
13
+ register_plugin('sqs')
14
+ register_plugin('s3')
15
+ end
16
+
17
+ register('aws', Aws)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Aws
7
+ class S3
8
+ include Kybus::Configuration::Utils
9
+ def self.from_config(config)
10
+ require 'aws-sdk-s3'
11
+ new(config)
12
+ end
13
+
14
+ def initialize(config)
15
+ @config = config
16
+ @client = ::Aws::S3::Client.new(
17
+ symbolize(config).reject do |k, _|
18
+ %i[bucket test_connection].include?(k)
19
+ end
20
+ )
21
+ @connection = ::Aws::S3::Bucket.new(
22
+ name: config['bucket'],
23
+ client: @client
24
+ )
25
+ end
26
+
27
+ def raw
28
+ @connection
29
+ end
30
+ end
31
+ end
32
+ register('s3', Aws::S3)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Aws
7
+ class Sqs
8
+ include Kybus::Configuration::Utils
9
+ def self.from_config(config)
10
+ require 'aws-sdk-sqs'
11
+ new(config)
12
+ end
13
+
14
+ def initialize(config)
15
+ @config = config
16
+ @client = ::Aws::SQS::Client.new(
17
+ symbolize(config).reject do |k, _|
18
+ %i[queue test_connection].include? k
19
+ end
20
+ )
21
+ queue = @client.get_queue_url(queue_name: @config['queue'])
22
+ @connection = ::Aws::SQS::Queue.new(
23
+ url: queue[:queue_url],
24
+ client: @client
25
+ )
26
+ end
27
+
28
+ def raw
29
+ @connection
30
+ end
31
+ end
32
+ end
33
+ register('sqs', Aws::Sqs)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Features
7
+ include Kybus::Configuration::Utils
8
+
9
+ def self.from_config(config)
10
+ new(config)
11
+ end
12
+
13
+ def initialize(config)
14
+ @config = config
15
+ @connection = Kybus::Configuration::FeatureFlag.new(symbolize(config))
16
+ end
17
+
18
+ def [](key)
19
+ @connection[key]
20
+ end
21
+ end
22
+
23
+ register('features', Features)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Logger
7
+ def self.from_config(config)
8
+ require 'kybus/logger'
9
+ new(config)
10
+ end
11
+
12
+ def initialize(config)
13
+ @config = config
14
+ Kybus::Logger::LogMethods.global_config = Kybus::Logger::Config.new(config)
15
+ @logger = Object.new
16
+ @logger.extend Kybus::Logger
17
+ end
18
+
19
+ def raw
20
+ @logger
21
+ end
22
+ end
23
+
24
+ register('logger', Logger)
25
+ end
26
+ end
27
+ end
File without changes
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Autoconfigs
6
+ class RESTClient
7
+ include Kybus::Configuration::Utils
8
+
9
+ def self.from_config(config)
10
+ require 'kybus/client'
11
+ new(config)
12
+ end
13
+
14
+ def initialize(config)
15
+ @config = config
16
+ @connection = Kybus::Client::RESTClient.new(symbolize(config))
17
+ end
18
+
19
+ def raw
20
+ @connection
21
+ end
22
+ end
23
+
24
+ register('rest_client', RESTClient)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Sequel
7
+ def self.from_config(config)
8
+ require 'sequel'
9
+ new(config)
10
+ end
11
+
12
+ def initialize(config)
13
+ @config = config
14
+ @connection = ::Sequel.connect(config['endpoint'], config)
15
+ end
16
+
17
+ def raw
18
+ @connection
19
+ end
20
+ end
21
+
22
+ register('sequel', Sequel)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kybus/core'
4
+ require_relative 'feature_flag'
5
+ require_relative 'utils'
6
+ require_relative 'loaders/yaml'
7
+ require_relative 'loaders/env'
8
+ require_relative 'loaders/arg'
9
+
10
+ module Kybus
11
+ module Configuration
12
+ # This class provides a module for loading configurations from 4 sources:
13
+ # - YAML default files
14
+ # - YAML files
15
+ # - ENV vars
16
+ # - ARGV values
17
+ # Using yaml defaults:
18
+ # TODO: Add docs
19
+ # Using yaml files
20
+ # TODO: Add docs
21
+ # Using env vars:
22
+ # TODO: Add docs
23
+ # Using arg vars:
24
+ # TODO: Add docs
25
+ class ConfigurationManager
26
+ include Utils
27
+ # [String(Array)] A path to default yaml configs.
28
+ attr_reader :default_files
29
+ # [String] The value provided by default. It should mean this \
30
+ # value is missing on configurations.
31
+ attr_reader :default_placeholder
32
+ # With this enabled all array will be concatenated instead of replaced.
33
+ attr_reader :append_arrays
34
+ # Use this configuration when you don't want your configs to be validated.
35
+ attr_reader :accept_default_keys
36
+ # The prefix used to find env strings and args.
37
+ attr_reader :env_prefix
38
+
39
+ def initialize(default_files:,
40
+ default_placeholder: nil,
41
+ append_arrays: false,
42
+ env_prefix: nil,
43
+ accept_default_keys: false)
44
+ @default_files = array_wrap(default_files)
45
+ @default_placeholder = default_placeholder || 'REPLACE_ME'
46
+ @append_arrays = append_arrays
47
+ @env_prefix = env_prefix || 'CONFIG'
48
+ @accept_default_keys = accept_default_keys
49
+ @configs = {}
50
+ @env_vars = env_vars
51
+ @config_files = env_files
52
+ end
53
+
54
+ # Loads the configurations from all the possible sources. It will raise an
55
+ # exception when it is required to validate that no default placeholder
56
+ # is present on the configs.
57
+ def load_configs!
58
+ load_configs
59
+ missing_keys = missing_configs
60
+ return if missing_keys.empty? || @accept_default_keys
61
+
62
+ raise MissingConfigs, missing_keys
63
+ end
64
+
65
+ # Use this when you require the application to do not start when something
66
+ # is missing and the error message should be displayed in stdout.
67
+ # This is helpful when you are launching your app and you need to trace
68
+ # any misconfiguration problem.
69
+ # :nocov: #
70
+ def pretty_load_configs!(terminate = true)
71
+ load_configs!
72
+ rescue MissingConfigs => e
73
+ puts 'You are missing some configs!'
74
+ puts 'Add them to a file and export the config env var:'
75
+ puts "$ export #{@env_prefix}_FILES='#{Dir.pwd}'/config/config.yaml"
76
+ puts 'Maybe you just need to add them to your existing files'
77
+ puts 'Missing configs:'
78
+ e.keys.each { |k| puts "- \"#{k}\"" }
79
+ exit(1) if terminate
80
+ end
81
+ # :nocov: #
82
+
83
+ # returns the object as a hash
84
+ # :nocov: #
85
+ def to_h
86
+ @configs
87
+ end
88
+ # :nocov: #
89
+
90
+ # provide a method for accessing configs
91
+ def [](key)
92
+ @configs[key]
93
+ end
94
+
95
+ def self.auto_load!
96
+ auto_configs = new(default_files: './config/autoconfig.yaml')
97
+ auto_configs.load_configs!
98
+ auto_configs = auto_configs['autoconfig']
99
+ configs = new(
100
+ default_files: (auto_configs['default_files'] || []) +
101
+ (auto_configs['files'] || []) +
102
+ ['./config/autoconfig.yaml'],
103
+ default_placeholder: auto_configs['default_placeholder'],
104
+ accept_default_keys: auto_configs['accept_default_keys'],
105
+ env_prefix: auto_configs['env_prefix']
106
+ )
107
+ configs.load_configs!
108
+ configs
109
+ end
110
+
111
+ private
112
+
113
+ # Looks for keys having the default placeholder, which are meant to be
114
+ # missing configurations
115
+ def missing_configs(hash = @configs, path = [])
116
+ case hash
117
+ when Hash
118
+ hash.map { |k, v| missing_configs(v, path + [k]) }.flatten
119
+ when Array
120
+ hash.map.with_index { |e, i| missing_configs(e, path + [i]) }.flatten
121
+ else
122
+ hash == @default_placeholder ? [path.join('.')] : []
123
+ end
124
+ end
125
+
126
+ # Path to config files
127
+ def env_files
128
+ @env_vars['files'] && array_wrap(@env_vars['files'])
129
+ end
130
+
131
+ # Extract vars from env
132
+ def env_vars
133
+ Loaders::Env.new(@env_prefix, self).load!
134
+ end
135
+
136
+ # Extract vars from arg
137
+ def arg_vars
138
+ Loaders::Arg.new(@env_prefix, self).load!
139
+ end
140
+
141
+ # Helper method that loads configurations
142
+ def load_configs
143
+ load_default_files
144
+ load_config_files
145
+ @configs = recursive_merge(@configs, @env_vars)
146
+ @configs = recursive_merge(@configs, arg_vars)
147
+ end
148
+
149
+ # Helper method for loading default files
150
+ def load_default_files
151
+ load_files(@default_files)
152
+ end
153
+
154
+ # Helper method for loading config files
155
+ def load_config_files
156
+ @config_files && load_files(@config_files)
157
+ end
158
+
159
+ # Helper method for loading files into configurations
160
+ def load_files(files)
161
+ files.each do |file|
162
+ config = Loaders::YAML.new(file, self).load!
163
+ @configs = recursive_merge(@configs, config)
164
+ end
165
+ end
166
+
167
+ # Exception raised when a configuration was not set
168
+ class MissingConfigs < StandardError
169
+ # Keys that were not loaded correctly
170
+ attr_reader :keys
171
+
172
+ def initialize(keys)
173
+ @keys = keys
174
+ super('There are keys missing to be configured')
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'feature_flag/feature_flag'
4
+ require_relative 'feature_flag/ab_test'
5
+ require_relative 'feature_flag/canarying'
6
+
7
+ module Kybus
8
+ module Configuration
9
+ class FeatureFlag
10
+ extend Kybus::DRY::ResourceInjector
11
+
12
+ def initialize(confs)
13
+ @flags = {}
14
+ confs.each do |name, conf|
15
+ @flags[name] = self.class.from_config(conf)
16
+ end
17
+ end
18
+
19
+ def [](key)
20
+ feature_flag = @flags[key]
21
+ raise KeyError, "#{key} is not configured as flag" unless feature_flag
22
+
23
+ feature_flag.active?
24
+ end
25
+
26
+ def self.from_config(conf)
27
+ case conf
28
+ when String, TrueClass, FalseClass
29
+ Base.new(conf)
30
+ when Hash
31
+ klass = resource(:custom_provider, conf['provider'])
32
+ klass.new(conf)
33
+ end
34
+ end
35
+
36
+ def self.register_provider(name, klass)
37
+ register(:custom_provider, name, klass)
38
+ end
39
+
40
+ register_provider('canarying', Canarying)
41
+ register_provider('ab_test', ABTest)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ class FeatureFlag
6
+ class ABTest
7
+ def initialize(configs)
8
+ @configs = configs
9
+ @threshold = normalize_thershold(configs['threshold'])
10
+ end
11
+
12
+ def normalize_thershold(value)
13
+ case value
14
+ when Integer
15
+ raise 'Value out of range' unless (0..100).cover?(value)
16
+
17
+ value.to_f / 100
18
+ when Float
19
+ raise 'Value out of range' unless (0..1).cover?(value)
20
+
21
+ value
22
+ end
23
+ end
24
+
25
+ def active?
26
+ rand <= @threshold
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ class FeatureFlag
6
+ class Canarying < ABTest
7
+ def initialize(configs)
8
+ @enabled = configs
9
+ @initial_time = initial_time(configs['starting_hour'])
10
+ @threshold = normalize_thershold(configs['initial'] || 0)
11
+ @step = normalize_thershold(configs['step'])
12
+ @step_duration = configs['step_duration']
13
+ end
14
+
15
+ def active?
16
+ rand <= threshold_calculation
17
+ end
18
+
19
+ def threshold_calculation
20
+ @threshold + (Time.now - @initial_time).to_i / @step_duration.to_i * @step
21
+ end
22
+
23
+ def initial_time(time)
24
+ return Time.now if time.nil?
25
+
26
+ Time.parse(time)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ class FeatureFlag
6
+ class Base
7
+ def initialize(configs)
8
+ @enabled = configs
9
+ end
10
+
11
+ def active?
12
+ @enabled
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Loaders
6
+ # This class allows to load configurations from ARGV
7
+ # It requires that all the vars are named with a common prefix
8
+ # It uses '__' as a delimiter to allow nested configurations
9
+ # - If the arg contains an '=' sym it will take the left string as key
10
+ # and the right as the value
11
+ # - If the arg does not contain an '=' it will take the next arg as value
12
+ # - If the next arg to an arg is also an arg, it will parse it as a flag.
13
+ # - Also if the last element of ARGV is an arg it will
14
+ # be parsed as a flag.
15
+ # === Examples
16
+ # --config_env_value=3 => { 'env_value' => '3' }
17
+ # --config_env_value 3 => { 'env_value' => '3' }
18
+ # --config_env_obj__value 3 => { "env_obj" => { 'value' => '3' } }
19
+ # --config_flag --config_value 3 => { 'flag' => 'true', value => '3' }
20
+ class Arg
21
+ include Kybus::Configuration::Utils
22
+ def initialize(env_prefix, manager, array = ARGV)
23
+ @env_prefix = env_prefix.downcase
24
+ @manager = manager
25
+ @array = array
26
+ end
27
+
28
+ # Parses configurations from array and returns the value as a hash
29
+ def load!
30
+ configs = {}
31
+ @array.each_with_index do |obj, idx|
32
+ next unless obj.start_with?('--' + @env_prefix)
33
+
34
+ value = extract_value(obj, idx + 1)
35
+ key = obj.split('=').first
36
+ .sub(/^--#{@env_prefix}_?/, '')
37
+ .downcase.split('__')
38
+ recursive_set(configs, key, split_env_string(value))
39
+ end
40
+ configs
41
+ end
42
+
43
+ # Parses a string as described above
44
+ def extract_value(string, idx)
45
+ if string.include?('=')
46
+ string.split('=')[1]
47
+ elsif @array.size == idx
48
+ 'true'
49
+ elsif @array[idx].start_with?('--')
50
+ 'true'
51
+ else
52
+ @array[idx]
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ module Loaders
6
+ # This class allows to load configurations from ENV
7
+ # It requires that all the vars are named with a common prefix
8
+ # It uses '__' as a delimiter to allow nested configurations
9
+ # === Examples
10
+ # export CONFIG_ENV_VALUE=3 => { 'env_value' => '3' }
11
+ # export CONFIG_ENV_OBJ__VALUE=3 => { "env_obj" => { 'value' => '3' } }
12
+ class Env
13
+ include Kybus::Configuration::Utils
14
+ def initialize(env_prefix, manager, conf = ENV.to_h)
15
+ @env_prefix = env_prefix
16
+ @manager = manager
17
+ @env = conf
18
+ end
19
+
20
+ # Parses the configurations from ENV
21
+ def load!
22
+ @env.select { |str, _| str.start_with?(@env_prefix) }
23
+ .each_with_object({}) do |(k, v), h|
24
+ # Remove ENV prefix and convert to downcase, then split by '__'
25
+ clean_key = k.sub(/^#{@env_prefix}_?/, '').downcase.split('__')
26
+ # recursively create the objects to set the config where it
27
+ # should be
28
+ recursive_set(h, clean_key, split_env_string(v))
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Kybus
6
+ module Configuration
7
+ module Loaders
8
+ # Class for loading yaml files
9
+ class YAML
10
+ def initialize(path, manager)
11
+ @path = path
12
+ @manager = manager
13
+ end
14
+
15
+ # Parses and returns the file as a hash
16
+ def load!
17
+ ::YAML.load_file(@path)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TODO: Comming soon!
4
+
5
+ require 'kybus/core'
6
+
7
+ module Kybus
8
+ module Configuration
9
+ # Allow to autoload configurations into ruby.
10
+ # It allows to implement new plugins.
11
+ class ServiceManager
12
+ extend Kybus::DRY::ResourceInjector
13
+ attr_reader :configs
14
+
15
+ def initialize(configs, plugin_subdir = '.')
16
+ @configs = configs
17
+ @services = {}
18
+ @plugin_subdir = plugin_subdir
19
+ end
20
+
21
+ def services(cathegory, name = nil?, sub = nil)
22
+ service = @services[cathegory]
23
+ service = service[name] if service.is_a?(Hash) && name
24
+ service = service[sub] if service.is_a?(Hash) && sub
25
+ service.raw
26
+ end
27
+
28
+ def all_services
29
+ @services
30
+ end
31
+
32
+ def configure!
33
+ plugins = self.class.resources(:plugins)
34
+ plugins.each do |plug, type|
35
+ next if @configs[plug].nil?
36
+
37
+ case type
38
+ when 'unique'
39
+ @services[plug] = Kybus::Configuration::Autoconfigs.from_config!(plug, @configs[plug])
40
+ else
41
+ load_service!(plug, @configs[plug])
42
+ end
43
+ end
44
+ end
45
+
46
+ def load_service!(name, keys)
47
+ return if keys&.empty?
48
+
49
+ services = {}
50
+ keys.each do |service, config|
51
+ services[service] = build_service(name, config)
52
+ end
53
+
54
+ @services[name] = services
55
+ end
56
+
57
+ def build_service(name, config)
58
+ Kybus::Configuration::Autoconfigs.from_config!(name, config, @plugin_subdir)
59
+ end
60
+
61
+ def self.auto_load!
62
+ configs = Kybus::Configuration::ConfigurationManager.auto_load!
63
+ services = new(configs)
64
+ services.configure!
65
+ services
66
+ end
67
+
68
+ # The type unique is for global configurations as multi is for a hash
69
+ # containing all the objects to be created
70
+ def self.register_plugin(name, type = 'multi')
71
+ register(:plugins, name, type)
72
+ end
73
+
74
+ def features
75
+ @services['features']
76
+ end
77
+
78
+ register_plugin('aws', 'unique')
79
+ register_plugin('logger', 'unique')
80
+ register_plugin('sequel')
81
+ register_plugin('rest_client')
82
+ register_plugin('features', 'unique')
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ # This module provides some operations with strings and hashes that
6
+ # are commonly used while parsing or merging strings
7
+ module Utils
8
+ # Ensures that a value is an array. If it is not an array it wraps it
9
+ # inside an array
10
+ # ==== Examples
11
+ # array_wrap(1) => [1]
12
+ # array_wrap([1, 2, 3]) => [1, 2, 3]
13
+ def array_wrap(value)
14
+ value.is_a?(Array) ? value : [value]
15
+ end
16
+
17
+ # Merges two hashes into one, but if a key is also a hash it will merge
18
+ # it too. This is applied recursively
19
+ # ==== Examples
20
+ # a = { a: 1, b: { c: 2 } }
21
+ # b = { b: { d: 3 }, e: 4}
22
+ # recursive_merge(a, b) => { a: 1, b: { c: 2, d: 3 }, e: 4}
23
+ # rubocop: disable Metrics/AbcSize
24
+ def recursive_merge(receiver, sender)
25
+ return receiver if sender.nil?
26
+
27
+ result = receiver.dup
28
+ (receiver.keys + sender.keys).each do |key|
29
+ value = if receiver[key].is_a?(Hash)
30
+ recursive_merge(receiver[key], sender[key])
31
+ elsif receiver[key].is_a?(Array)
32
+ # TODO: Enable merging arrays
33
+ sender[key]
34
+ else
35
+ sender[key]
36
+ end
37
+ result[key] = value if value
38
+ end
39
+ result
40
+ end
41
+ # rubocop: enable Metrics/AbcSize
42
+
43
+ # Takes a hash, an array and a value
44
+ # It will traverse recursively into the hash and create a key inside the
45
+ # hash using the string inside key
46
+ # === Examples
47
+ # recursive_set({}, %w[hello key], 3) => { 'hello' => { 'key' => 3 } }
48
+ def recursive_set(hash, key, value)
49
+ current = key[0]
50
+ if key.size == 1
51
+ hash[current] = value
52
+ hash
53
+ else
54
+ hash[current] ||= {}
55
+ recursive_set(hash[current], key[1..-1], value)
56
+ end
57
+ end
58
+
59
+ # This method is used to parse values from vars passed from ENV or ARGV.
60
+ # Currently all the values are passed as either string or Array of String.
61
+ # The delimiter used is ',' and it allows to escape it
62
+ # === Examples
63
+ # split_env_string('hello') => 'hello'
64
+ # split_env_string('hello, world') => ['hello', 'world']
65
+ # split_env_string('hello\, world') => 'hello, world'
66
+ def split_env_string(string)
67
+ # TODO: parse type of string
68
+ strings = string.split(/(?<!\\),/)
69
+ .map { |str| str.gsub('\,', ',') }
70
+ .map { |str| parse_type(str) }
71
+ strings.size == 1 ? strings.first : strings
72
+ end
73
+
74
+ def parse_type(string)
75
+ case string.downcase
76
+ when 'true'
77
+ true
78
+ when 'false'
79
+ false
80
+ else
81
+ string
82
+ end
83
+ end
84
+
85
+ def symbolize(hash)
86
+ hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Configuration
5
+ VERSION = '0.2.0'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,288 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kybus-configs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Gilberto Vargas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-06-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kybus-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kybus-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: kybus-logger
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aws-sdk-s3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: aws-sdk-sqs
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: httparty
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '5.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '5.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: mocha
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.8'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.8'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.10'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.10'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rack-minitest
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '10.0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '10.0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rdoc
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '6.1'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '6.1'
181
+ - !ruby/object:Gem::Dependency
182
+ name: sequel
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: simplecov
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '0.16'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '0.16'
209
+ - !ruby/object:Gem::Dependency
210
+ name: sqlite3
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: webmock
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - "~>"
228
+ - !ruby/object:Gem::Version
229
+ version: '3.5'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: '3.5'
237
+ description: Provides helpers for making configs easy
238
+ email:
239
+ - tachoguitar@gmail.com
240
+ executables: []
241
+ extensions: []
242
+ extra_rdoc_files: []
243
+ files:
244
+ - lib/kybus/configs.rb
245
+ - lib/kybus/configs/autoconfigs.rb
246
+ - lib/kybus/configs/autoconfigs/aws.rb
247
+ - lib/kybus/configs/autoconfigs/aws/s3.rb
248
+ - lib/kybus/configs/autoconfigs/aws/sqs.rb
249
+ - lib/kybus/configs/autoconfigs/features.rb
250
+ - lib/kybus/configs/autoconfigs/logger.rb
251
+ - lib/kybus/configs/autoconfigs/nanoservice.rb
252
+ - lib/kybus/configs/autoconfigs/rest_client.rb
253
+ - lib/kybus/configs/autoconfigs/sequel.rb
254
+ - lib/kybus/configs/configuration_manager.rb
255
+ - lib/kybus/configs/feature_flag.rb
256
+ - lib/kybus/configs/feature_flag/ab_test.rb
257
+ - lib/kybus/configs/feature_flag/canarying.rb
258
+ - lib/kybus/configs/feature_flag/feature_flag.rb
259
+ - lib/kybus/configs/loaders/arg.rb
260
+ - lib/kybus/configs/loaders/env.rb
261
+ - lib/kybus/configs/loaders/yaml.rb
262
+ - lib/kybus/configs/service_manager.rb
263
+ - lib/kybus/configs/utils.rb
264
+ - lib/kybus/configs/version.rb
265
+ homepage: https://github.com/tachomex/kybus
266
+ licenses:
267
+ - MIT
268
+ metadata: {}
269
+ post_install_message:
270
+ rdoc_options: []
271
+ require_paths:
272
+ - lib
273
+ required_ruby_version: !ruby/object:Gem::Requirement
274
+ requirements:
275
+ - - ">="
276
+ - !ruby/object:Gem::Version
277
+ version: '0'
278
+ required_rubygems_version: !ruby/object:Gem::Requirement
279
+ requirements:
280
+ - - ">="
281
+ - !ruby/object:Gem::Version
282
+ version: '0'
283
+ requirements: []
284
+ rubygems_version: 3.1.4
285
+ signing_key:
286
+ specification_version: 4
287
+ summary: Config Manager for Kybus framework
288
+ test_files: []