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.
- checksums.yaml +7 -0
- data/lib/ant/configs.rb +6 -0
- data/lib/ant/configs/autoconfigs.rb +18 -0
- data/lib/ant/configs/autoconfigs/aws.rb +20 -0
- data/lib/ant/configs/autoconfigs/aws/s3.rb +37 -0
- data/lib/ant/configs/autoconfigs/aws/sqs.rb +37 -0
- data/lib/ant/configs/autoconfigs/features.rb +24 -0
- data/lib/ant/configs/autoconfigs/logger.rb +29 -0
- data/lib/ant/configs/autoconfigs/nanoservice.rb +0 -0
- data/lib/ant/configs/autoconfigs/rest_client.rb +29 -0
- data/lib/ant/configs/autoconfigs/sequel.rb +33 -0
- data/lib/ant/configs/configuration_manager.rb +177 -0
- data/lib/ant/configs/feature_flag.rb +44 -0
- data/lib/ant/configs/feature_flag/ab_test.rb +30 -0
- data/lib/ant/configs/feature_flag/canarying.rb +29 -0
- data/lib/ant/configs/feature_flag/feature_flag.rb +15 -0
- data/lib/ant/configs/loaders/arg.rb +58 -0
- data/lib/ant/configs/loaders/env.rb +34 -0
- data/lib/ant/configs/loaders/yaml.rb +22 -0
- data/lib/ant/configs/service_manager.rb +82 -0
- data/lib/ant/configs/utils.rb +77 -0
- data/lib/ant/configs/version.rb +7 -0
- metadata +190 -0
checksums.yaml
ADDED
|
@@ -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
|
data/lib/ant/configs.rb
ADDED
|
@@ -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
|
|
File without changes
|
|
@@ -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,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
|
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: []
|