ant-configs 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3751bc63b15af75e17648d9ed4443be2d6545cad6beb0da3e58d8513d5e4e6d1
4
+ data.tar.gz: e2e94fdf9d552a555440ab484c9b7123735f4c09eb89e8902bcced7718875935
5
+ SHA512:
6
+ metadata.gz: 2a235e95bd96bd4c4de75a36920ed7e7d087528723935a7b9644fbe687df554443fded7db5782936e53bba87cfc811db18386e94c5060d6c852a81c7724bbf07
7
+ data.tar.gz: 586fe8e2c4441b9d112830b5ea922c7c7e66c0dda5eb752b0cf1697c97279ca50c429ed9116355820aba953a00f4aa63ce71063ea9b6135e44a37dbc355d181f
@@ -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,18 @@
1
+ module Ant
2
+ module Configuration
3
+ module Autoconfigs
4
+ extend Ant::DRY::ResourceInjector
5
+
6
+ def self.from_config!(adapter, config, loading_path = '.')
7
+ require_relative "autoconfigs/#{loading_path}/#{adapter}"
8
+
9
+ factory = resource(adapter)
10
+ factory.from_config(config)
11
+ end
12
+ end
13
+
14
+ def self.auto_load!
15
+ Ant::Configuration::ServiceManager.auto_load!
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ant
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,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ant
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Aws
7
+ class S3
8
+ include Ant::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 { |k, _| %i[bucket test_connection].include? k }
18
+ )
19
+ @connection = ::Aws::S3::Bucket.new(
20
+ name: config['bucket'],
21
+ client: @client
22
+ )
23
+ end
24
+
25
+ def sanity_check
26
+ false
27
+ end
28
+
29
+ def raw
30
+ @connection
31
+ end
32
+ end
33
+ end
34
+ register('s3', Aws::S3)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ant
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Aws
7
+ class Sqs
8
+ include Ant::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 { |k, _| %i[queue test_connection].include? k }
18
+ )
19
+ @connection = ::Aws::SQS::Queue.new(
20
+ url: @client.get_queue_url(queue_name: @config['queue'])[:queue_url],
21
+ client: @client
22
+ )
23
+ end
24
+
25
+ def sanity_check
26
+ false
27
+ end
28
+
29
+ def raw
30
+ @connection
31
+ end
32
+ end
33
+ end
34
+ register('sqs', Aws::Sqs)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ module Ant
2
+ module Configuration
3
+ module Autoconfigs
4
+ class Features
5
+ include Ant::Configuration::Utils
6
+
7
+ def self.from_config(config)
8
+ new(config)
9
+ end
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ @connection = Ant::Configuration::FeatureFlag.new(symbolize(config))
14
+ end
15
+
16
+ def [](key)
17
+ @connection[key]
18
+ end
19
+ end
20
+
21
+ register('features', Features)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ant
4
+ module Configuration
5
+ module Autoconfigs
6
+ class Logger
7
+ def self.from_config(config)
8
+ require 'ant/logger'
9
+ new(config)
10
+ end
11
+
12
+ def initialize(config)
13
+ @config = config
14
+ Ant::Logger::LogMethods.global_config = Ant::Logger::Config.new(config)
15
+ end
16
+
17
+ def sanity_check
18
+ true
19
+ end
20
+
21
+ def raw
22
+ @logger
23
+ end
24
+ end
25
+
26
+ register('logger', Logger)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module Ant
2
+ module Configuration
3
+ module Autoconfigs
4
+ class RESTClient
5
+ include Ant::Configuration::Utils
6
+
7
+ def self.from_config(config)
8
+ require 'ant/client'
9
+ new(config)
10
+ end
11
+
12
+ def initialize(config)
13
+ @config = config
14
+ @connection = Ant::Client::RESTClient.new(symbolize(config))
15
+ end
16
+
17
+ def sanity_check
18
+ true
19
+ end
20
+
21
+ def raw
22
+ @connection
23
+ end
24
+ end
25
+
26
+ register('rest_client', RESTClient)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ant
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 sanity_check
18
+ @connection.fetch('select 1 + 1;')
19
+ end
20
+
21
+ def raw
22
+ @connection
23
+ end
24
+
25
+ def help
26
+ CONFIG_KEYS
27
+ end
28
+ end
29
+
30
+ register('sequel', Sequel)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ant/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 Ant
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 => ex
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
+ ex.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
+ private
96
+
97
+ # Looks for keys having the default placeholder, which are meant to be
98
+ # missing configurations
99
+ def missing_configs(hash = @configs, path = [])
100
+ case hash
101
+ when Hash
102
+ hash.map { |k, v| missing_configs(v, path + [k]) }.flatten
103
+ when Array
104
+ hash.map.with_index { |e, i| missing_configs(e, path + [i]) }.flatten
105
+ else
106
+ hash == @default_placeholder ? [path.join('.')] : []
107
+ end
108
+ end
109
+
110
+ # Path to config files
111
+ def env_files
112
+ @env_vars['files'] && array_wrap(@env_vars['files'])
113
+ end
114
+
115
+ # Extract vars from env
116
+ def env_vars
117
+ Loaders::Env.new(@env_prefix, self).load!
118
+ end
119
+
120
+ # Extract vars from arg
121
+ def arg_vars
122
+ Loaders::Arg.new(@env_prefix, self).load!
123
+ end
124
+
125
+ # Helper method that loads configurations
126
+ def load_configs
127
+ load_default_files
128
+ load_config_files
129
+ @configs = recursive_merge(@configs, @env_vars)
130
+ @configs = recursive_merge(@configs, arg_vars)
131
+ end
132
+
133
+ # Helper method for loading default files
134
+ def load_default_files
135
+ load_files(@default_files)
136
+ end
137
+
138
+ # Helper method for loading config files
139
+ def load_config_files
140
+ @config_files && load_files(@config_files)
141
+ end
142
+
143
+ # Helper method for loading files into configurations
144
+ def load_files(files)
145
+ files.each do |file|
146
+ config = Loaders::YAML.new(file, self).load!
147
+ @configs = recursive_merge(@configs, config)
148
+ end
149
+ end
150
+
151
+ def self.auto_load!
152
+ auto_configs = new(default_files: './config/autoconfig.yaml')
153
+ auto_configs.load_configs!
154
+ auto_configs = auto_configs['autoconfig']
155
+ configs = new(
156
+ default_files: (auto_configs['default_files'] || []) + (auto_configs['files'] || []) + ['./config/autoconfig.yaml'],
157
+ default_placeholder: auto_configs['default_placeholder'],
158
+ accept_default_keys: auto_configs['accept_default_keys'],
159
+ env_prefix: auto_configs['env_prefix']
160
+ )
161
+ configs.load_configs!
162
+ configs
163
+ end
164
+
165
+ # Exception raised when a configuration was not set
166
+ class MissingConfigs < StandardError
167
+ # Keys that were not loaded correctly
168
+ attr_reader :keys
169
+
170
+ def initialize(keys)
171
+ @keys = keys
172
+ super('There are keys missing to be configured')
173
+ end
174
+ end
175
+ end
176
+ end
177
+ 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 Ant
8
+ module Configuration
9
+ class FeatureFlag
10
+ extend Ant::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,30 @@
1
+ module Ant
2
+ module Configuration
3
+ class FeatureFlag
4
+ class ABTest
5
+ def initialize(configs)
6
+ @configs = configs
7
+ @threshold = normalize_thershold(configs['threshold'])
8
+
9
+ end
10
+
11
+ def normalize_thershold(value)
12
+ case value
13
+ when Integer
14
+ raise 'Value out of range' unless (0..100).cover?(value)
15
+
16
+ value.to_f / 100
17
+ when Float
18
+ raise 'Value out of range' unless (0..1).cover?(value)
19
+
20
+ value
21
+ end
22
+ end
23
+
24
+ def active?
25
+ rand <= @threshold
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ module Ant
2
+ module Configuration
3
+ class FeatureFlag
4
+ class Canarying < ABTest
5
+ def initialize(configs)
6
+ @enabled = configs
7
+ @initial_time = initial_time(configs['starting_hour'])
8
+ @threshold = normalize_thershold(configs['initial'] || 0)
9
+ @step = normalize_thershold(configs['step'])
10
+ @step_duration = configs['step_duration']
11
+ end
12
+
13
+ def active?
14
+ rand <= threshold_calculation
15
+ end
16
+
17
+ def threshold_calculation
18
+ @threshold + (Time.now - @initial_time).to_i / @step_duration.to_i * @step
19
+ end
20
+
21
+ def initial_time(time)
22
+ return Time.now if time.nil?
23
+
24
+ Time.parse(time)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ module Ant
2
+ module Configuration
3
+ class FeatureFlag
4
+ class Base
5
+ def initialize(configs)
6
+ @enabled = configs
7
+ end
8
+
9
+ def active?
10
+ @enabled
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ant
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 Ant::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 Ant
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 Ant::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 Ant
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,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TODO: Comming soon!
4
+
5
+ require 'ant/core'
6
+
7
+ module Ant
8
+ module Configuration
9
+ # Allow to autoload configurations into ruby.
10
+ # It allows to implement new plugins.
11
+ class ServiceManager
12
+ extend Ant::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)
22
+ @services[cathegory][name].raw
23
+ end
24
+
25
+ def all_services
26
+ @services
27
+ end
28
+
29
+ def configure!
30
+ plugins = self.class.resources(:plugins)
31
+ plugins.each do |plug, type|
32
+ next if @configs[plug].nil?
33
+
34
+ case type
35
+ when 'unique'
36
+ @services[plug] = Ant::Configuration::Autoconfigs.from_config!(plug, @configs[plug])
37
+ else
38
+ load_service!(plug, @configs[plug])
39
+ end
40
+ end
41
+ end
42
+
43
+ def load_service!(name, keys)
44
+ return if keys&.empty?
45
+
46
+ services = {}
47
+ keys.each do |service, config|
48
+ services[service] = build_service(name, config)
49
+ end
50
+
51
+ @services[name] = services
52
+ end
53
+
54
+ def build_service(name, config)
55
+ Ant::Configuration::Autoconfigs.from_config!(name, config, @plugin_subdir)
56
+ end
57
+
58
+ def self.auto_load!
59
+ configs = Ant::Configuration::ConfigurationManager.auto_load!
60
+ services = new(configs)
61
+ services.configure!
62
+ services
63
+ end
64
+
65
+ # The type unique is for global configurations as multi is for a hash
66
+ # containing all the objects to be created
67
+ def self.register_plugin(name, type = 'multi')
68
+ register(:plugins, name, type)
69
+ end
70
+
71
+ def features
72
+ @services['features']
73
+ end
74
+
75
+ register_plugin('aws', 'unique')
76
+ register_plugin('logger', 'unique')
77
+ register_plugin('sequel')
78
+ register_plugin('rest_client')
79
+ register_plugin('features', 'unique')
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ant
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 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 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(/(?<!\\),/).map { |str| str.gsub('\,', ',') }
69
+ strings.size == 1 ? strings.first : strings
70
+ end
71
+
72
+ def symbolize(hash)
73
+ hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ant
4
+ module Configuration
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ant-configs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gilberto Vargas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-10-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ant-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: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mocha
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack-minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.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.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rdoc
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '6.1'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '6.1'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.16'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.16'
125
+ - !ruby/object:Gem::Dependency
126
+ name: webmock
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.5'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.5'
139
+ description: Provides helpers for making configs easy
140
+ email:
141
+ - tachoguitar@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - lib/ant/configs.rb
147
+ - lib/ant/configs/autoconfigs.rb
148
+ - lib/ant/configs/autoconfigs/aws.rb
149
+ - lib/ant/configs/autoconfigs/aws/s3.rb
150
+ - lib/ant/configs/autoconfigs/aws/sqs.rb
151
+ - lib/ant/configs/autoconfigs/features.rb
152
+ - lib/ant/configs/autoconfigs/logger.rb
153
+ - lib/ant/configs/autoconfigs/nanoservice.rb
154
+ - lib/ant/configs/autoconfigs/rest_client.rb
155
+ - lib/ant/configs/autoconfigs/sequel.rb
156
+ - lib/ant/configs/configuration_manager.rb
157
+ - lib/ant/configs/feature_flag.rb
158
+ - lib/ant/configs/feature_flag/ab_test.rb
159
+ - lib/ant/configs/feature_flag/canarying.rb
160
+ - lib/ant/configs/feature_flag/feature_flag.rb
161
+ - lib/ant/configs/loaders/arg.rb
162
+ - lib/ant/configs/loaders/env.rb
163
+ - lib/ant/configs/loaders/yaml.rb
164
+ - lib/ant/configs/service_manager.rb
165
+ - lib/ant/configs/utils.rb
166
+ - lib/ant/configs/version.rb
167
+ homepage: https://github.com/tachomex/ant
168
+ licenses:
169
+ - MIT
170
+ metadata: {}
171
+ post_install_message:
172
+ rdoc_options: []
173
+ require_paths:
174
+ - lib
175
+ required_ruby_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ required_rubygems_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ requirements: []
186
+ rubygems_version: 3.0.3
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: Config Manager for Ant framework
190
+ test_files: []