giga-clean-tool 0.0.1
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/config-5.6.1/CHANGELOG.md +332 -0
- data/config-5.6.1/CONTRIBUTING.md +38 -0
- data/config-5.6.1/LICENSE.md +26 -0
- data/config-5.6.1/README.md +615 -0
- data/config-5.6.1/config.gemspec +62 -0
- data/config-5.6.1/lib/config/configuration.rb +36 -0
- data/config-5.6.1/lib/config/dry_validation_requirements.rb +25 -0
- data/config-5.6.1/lib/config/error.rb +4 -0
- data/config-5.6.1/lib/config/integrations/heroku.rb +59 -0
- data/config-5.6.1/lib/config/integrations/rails/railtie.rb +38 -0
- data/config-5.6.1/lib/config/integrations/sinatra.rb +26 -0
- data/config-5.6.1/lib/config/options.rb +195 -0
- data/config-5.6.1/lib/config/rack/reloader.rb +15 -0
- data/config-5.6.1/lib/config/sources/env_source.rb +94 -0
- data/config-5.6.1/lib/config/sources/hash_source.rb +16 -0
- data/config-5.6.1/lib/config/sources/yaml_source.rb +32 -0
- data/config-5.6.1/lib/config/tasks/heroku.rake +7 -0
- data/config-5.6.1/lib/config/validation/error.rb +15 -0
- data/config-5.6.1/lib/config/validation/schema.rb +23 -0
- data/config-5.6.1/lib/config/validation/validate.rb +29 -0
- data/config-5.6.1/lib/config/version.rb +3 -0
- data/config-5.6.1/lib/config.rb +93 -0
- data/config-5.6.1/lib/generators/config/install_generator.rb +32 -0
- data/config-5.6.1/lib/generators/config/templates/config.rb +77 -0
- data/config-5.6.1/lib/generators/config/templates/settings/development.yml +0 -0
- data/config-5.6.1/lib/generators/config/templates/settings/production.yml +0 -0
- data/config-5.6.1/lib/generators/config/templates/settings/test.yml +0 -0
- data/config-5.6.1/lib/generators/config/templates/settings.local.yml +0 -0
- data/config-5.6.1/lib/generators/config/templates/settings.yml +0 -0
- data/giga-clean-tool.gemspec +12 -0
- metadata +71 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Config
|
|
2
|
+
# The main configuration backbone
|
|
3
|
+
class Configuration < Module
|
|
4
|
+
# Accepts configuration options,
|
|
5
|
+
# initializing a module that can be used to extend
|
|
6
|
+
# the necessary class with the provided config methods
|
|
7
|
+
def initialize(**attributes)
|
|
8
|
+
attributes.each do |name, default|
|
|
9
|
+
define_reader(name, default)
|
|
10
|
+
define_writer(name)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def define_reader(name, default)
|
|
17
|
+
variable = :"@#{name}"
|
|
18
|
+
|
|
19
|
+
define_method(name) do
|
|
20
|
+
if instance_variable_defined?(variable)
|
|
21
|
+
instance_variable_get(variable)
|
|
22
|
+
else
|
|
23
|
+
default
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def define_writer(name)
|
|
29
|
+
variable = :"@#{name}"
|
|
30
|
+
|
|
31
|
+
define_method("#{name}=") do |value|
|
|
32
|
+
instance_variable_set(variable, value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Config
|
|
4
|
+
module DryValidationRequirements
|
|
5
|
+
VERSIONS = ['~> 1.0', '>= 1.0.0'].freeze
|
|
6
|
+
|
|
7
|
+
def self.load_dry_validation!
|
|
8
|
+
return if defined?(@load_dry_validation)
|
|
9
|
+
|
|
10
|
+
begin
|
|
11
|
+
require 'dry/validation/version'
|
|
12
|
+
version = Gem::Version.new(Dry::Validation::VERSION)
|
|
13
|
+
unless VERSIONS.all? { |req| Gem::Requirement.new(req).satisfied_by?(version) }
|
|
14
|
+
raise LoadError
|
|
15
|
+
end
|
|
16
|
+
rescue LoadError
|
|
17
|
+
raise ::Config::Error, "Could not find a dry-validation version matching requirements (#{VERSIONS.map(&:inspect) * ','})"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
require 'dry/validation'
|
|
21
|
+
@load_dry_validation = true
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
|
|
3
|
+
module Config
|
|
4
|
+
module Integrations
|
|
5
|
+
class Heroku < Struct.new(:app)
|
|
6
|
+
def invoke
|
|
7
|
+
puts 'Setting vars...'
|
|
8
|
+
heroku_command = "config:set #{vars}"
|
|
9
|
+
heroku(heroku_command)
|
|
10
|
+
puts 'Vars set:'
|
|
11
|
+
puts heroku_command
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def vars
|
|
15
|
+
# Load only local options to Heroku
|
|
16
|
+
Config.load_and_set_settings(
|
|
17
|
+
Rails.root.join("config", "#{Config.file_name}.local.yml").to_s,
|
|
18
|
+
Rails.root.join("config", Config.dir_name, "#{environment}.local.yml").to_s,
|
|
19
|
+
Rails.root.join("config", "environments", "#{environment}.local.yml").to_s
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
out = ''
|
|
23
|
+
dotted_hash = to_dotted_hash Kernel.const_get(Config.const_name).to_hash, {}, Config.const_name
|
|
24
|
+
dotted_hash.each {|key, value| out += " #{key}=#{value} "}
|
|
25
|
+
out
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def environment
|
|
29
|
+
heroku("run 'echo $RAILS_ENV'").chomp[/(\w+)\z/]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def heroku(command)
|
|
33
|
+
with_app = app ? " --app #{app}" : ""
|
|
34
|
+
`heroku #{command}#{with_app}`
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def `(command)
|
|
38
|
+
Bundler.with_clean_env { super }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_dotted_hash(source, target = {}, namespace = nil)
|
|
42
|
+
prefix = "#{namespace}." if namespace
|
|
43
|
+
case source
|
|
44
|
+
when Hash
|
|
45
|
+
source.each do |key, value|
|
|
46
|
+
to_dotted_hash(value, target, "#{prefix}#{key}")
|
|
47
|
+
end
|
|
48
|
+
when Array
|
|
49
|
+
source.each_with_index do |value, index|
|
|
50
|
+
to_dotted_hash(value, target, "#{prefix}#{index}")
|
|
51
|
+
end
|
|
52
|
+
else
|
|
53
|
+
target[namespace] = source
|
|
54
|
+
end
|
|
55
|
+
target
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Config
|
|
2
|
+
module Integrations
|
|
3
|
+
module Rails
|
|
4
|
+
class Railtie < ::Rails::Railtie
|
|
5
|
+
def preload
|
|
6
|
+
# Manually load the custom initializer before everything else
|
|
7
|
+
initializer = ::Rails.root.join('config', 'initializers', 'config.rb')
|
|
8
|
+
require initializer if File.exist?(initializer)
|
|
9
|
+
|
|
10
|
+
# Parse the settings before any of the initializers
|
|
11
|
+
Config.load_and_set_settings(
|
|
12
|
+
Config.setting_files(::Rails.root.join('config'), Config.environment.nil? ? ::Rails.env : Config.environment.to_sym)
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Load rake tasks (eg. Heroku)
|
|
17
|
+
rake_tasks do
|
|
18
|
+
Dir[File.join(File.dirname(__FILE__), '../tasks/*.rake')].each { |f| load f }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
config.before_configuration { preload }
|
|
22
|
+
|
|
23
|
+
# Development environment should reload settings on every request
|
|
24
|
+
if ::Rails.env.development?
|
|
25
|
+
initializer :config_reload_on_development do
|
|
26
|
+
ActiveSupport.on_load :action_controller_base do
|
|
27
|
+
if ::Rails::VERSION::MAJOR >= 4
|
|
28
|
+
prepend_before_action { ::Config.reload! }
|
|
29
|
+
else
|
|
30
|
+
prepend_before_filter { ::Config.reload! }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "config/rack/reloader"
|
|
2
|
+
|
|
3
|
+
module Config
|
|
4
|
+
# provide helper to register within your Sinatra app
|
|
5
|
+
#
|
|
6
|
+
# set :root, File.dirname(__FILE__)
|
|
7
|
+
# register Config
|
|
8
|
+
#
|
|
9
|
+
def self.registered(app)
|
|
10
|
+
app.configure do |inner_app|
|
|
11
|
+
|
|
12
|
+
env = inner_app.environment || ENV["RACK_ENV"]
|
|
13
|
+
root = inner_app.root
|
|
14
|
+
|
|
15
|
+
# use Padrino settings if applicable
|
|
16
|
+
if defined?(Padrino)
|
|
17
|
+
env = Padrino.env if Padrino.respond_to?(:env)
|
|
18
|
+
root = Padrino.root if Padrino.respond_to?(:root)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Config.load_and_set_settings(Config.setting_files(File.join(root, 'config'), env))
|
|
22
|
+
|
|
23
|
+
inner_app.use(::Config::Rack::Reloader) if inner_app.development?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
require 'config/validation/validate'
|
|
3
|
+
|
|
4
|
+
module Config
|
|
5
|
+
class Options < OpenStruct
|
|
6
|
+
include Enumerable
|
|
7
|
+
include Validation::Validate
|
|
8
|
+
|
|
9
|
+
def keys
|
|
10
|
+
marshal_dump.keys
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def empty?
|
|
14
|
+
marshal_dump.empty?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add_source!(source)
|
|
18
|
+
# handle yaml file paths
|
|
19
|
+
source = (Sources::YAMLSource.new(source)) if source.is_a?(String) || source.is_a?(Pathname)
|
|
20
|
+
source = (Sources::HashSource.new(source)) if source.is_a?(Hash)
|
|
21
|
+
|
|
22
|
+
@config_sources ||= []
|
|
23
|
+
@config_sources << source
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def prepend_source!(source)
|
|
27
|
+
source = (Sources::YAMLSource.new(source)) if source.is_a?(String) || source.is_a?(Pathname)
|
|
28
|
+
source = (Sources::HashSource.new(source)) if source.is_a?(Hash)
|
|
29
|
+
|
|
30
|
+
@config_sources ||= []
|
|
31
|
+
@config_sources.unshift(source)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# look through all our sources and rebuild the configuration
|
|
35
|
+
def reload!
|
|
36
|
+
conf = {}
|
|
37
|
+
@config_sources.each do |source|
|
|
38
|
+
source_conf = source.load
|
|
39
|
+
|
|
40
|
+
if conf.empty?
|
|
41
|
+
conf = source_conf
|
|
42
|
+
else
|
|
43
|
+
DeepMerge.deep_merge!(
|
|
44
|
+
source_conf,
|
|
45
|
+
conf,
|
|
46
|
+
preserve_unmergeables: false,
|
|
47
|
+
knockout_prefix: Config.knockout_prefix,
|
|
48
|
+
overwrite_arrays: Config.overwrite_arrays,
|
|
49
|
+
merge_nil_values: Config.merge_nil_values,
|
|
50
|
+
merge_hash_arrays: Config.merge_hash_arrays
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# swap out the contents of the OStruct with a hash (need to recursively convert)
|
|
56
|
+
marshal_load(__convert(conf).marshal_dump)
|
|
57
|
+
|
|
58
|
+
validate!
|
|
59
|
+
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
alias :load! :reload!
|
|
64
|
+
|
|
65
|
+
def reload_from_files(*files)
|
|
66
|
+
Config.load_and_set_settings(files)
|
|
67
|
+
reload!
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def to_hash
|
|
71
|
+
result = {}
|
|
72
|
+
marshal_dump.each do |k, v|
|
|
73
|
+
if v.instance_of? Config::Options
|
|
74
|
+
result[k] = v.to_hash
|
|
75
|
+
elsif v.instance_of? Array
|
|
76
|
+
result[k] = descend_array(v)
|
|
77
|
+
else
|
|
78
|
+
result[k] = v
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
result
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
alias :to_h :to_hash
|
|
85
|
+
|
|
86
|
+
def each(*args, &block)
|
|
87
|
+
marshal_dump.each(*args, &block)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def to_json(*args)
|
|
91
|
+
require "json" unless defined?(JSON)
|
|
92
|
+
to_hash.to_json(*args)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def as_json(options = nil)
|
|
96
|
+
to_hash.as_json(options)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def merge!(hash)
|
|
100
|
+
current = to_hash
|
|
101
|
+
DeepMerge.deep_merge!(
|
|
102
|
+
hash.dup,
|
|
103
|
+
current,
|
|
104
|
+
preserve_unmergeables: false,
|
|
105
|
+
knockout_prefix: Config.knockout_prefix,
|
|
106
|
+
overwrite_arrays: Config.overwrite_arrays,
|
|
107
|
+
merge_nil_values: Config.merge_nil_values,
|
|
108
|
+
merge_hash_arrays: Config.merge_hash_arrays
|
|
109
|
+
)
|
|
110
|
+
marshal_load(__convert(current).marshal_dump)
|
|
111
|
+
self
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Some keywords that don't play nicely with OpenStruct
|
|
115
|
+
SETTINGS_RESERVED_NAMES = %w[select collect test count zip min max exit! table].freeze
|
|
116
|
+
|
|
117
|
+
# Some keywords that don't play nicely with Rails 7.*
|
|
118
|
+
RAILS_RESERVED_NAMES = %w[maximum minimum].freeze
|
|
119
|
+
|
|
120
|
+
# An alternative mechanism for property access.
|
|
121
|
+
# This let's you do foo['bar'] along with foo.bar.
|
|
122
|
+
def [](param)
|
|
123
|
+
return super if SETTINGS_RESERVED_NAMES.include?(param)
|
|
124
|
+
return super if RAILS_RESERVED_NAMES.include?(param)
|
|
125
|
+
public_send("#{param}")
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def []=(param, value)
|
|
129
|
+
send("#{param}=", value)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
SETTINGS_RESERVED_NAMES.each do |name|
|
|
133
|
+
define_method name do
|
|
134
|
+
self[name]
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
RAILS_RESERVED_NAMES.each do |name|
|
|
139
|
+
define_method name do
|
|
140
|
+
self[name]
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def key?(key)
|
|
145
|
+
@table.key?(key)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def has_key?(key)
|
|
149
|
+
@table.has_key?(key)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def method_missing(method_name, *args)
|
|
153
|
+
if Config.fail_on_missing && method_name !~ /.*(?==\z)/m
|
|
154
|
+
raise KeyError, "key not found: #{method_name.inspect}" unless key?(method_name)
|
|
155
|
+
end
|
|
156
|
+
super
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def respond_to_missing?(*args)
|
|
160
|
+
super
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
protected
|
|
164
|
+
|
|
165
|
+
def descend_array(array)
|
|
166
|
+
array.map do |value|
|
|
167
|
+
if value.instance_of? Config::Options
|
|
168
|
+
value.to_hash
|
|
169
|
+
elsif value.instance_of? Array
|
|
170
|
+
descend_array(value)
|
|
171
|
+
else
|
|
172
|
+
value
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Recursively converts Hashes to Options (including Hashes inside Arrays)
|
|
178
|
+
def __convert(h) #:nodoc:
|
|
179
|
+
s = self.class.new
|
|
180
|
+
|
|
181
|
+
h.each do |k, v|
|
|
182
|
+
k = k.to_s if !k.respond_to?(:to_sym) && k.respond_to?(:to_s)
|
|
183
|
+
|
|
184
|
+
if v.is_a?(Hash)
|
|
185
|
+
v = v["type"] == "hash" ? v["contents"] : __convert(v)
|
|
186
|
+
elsif v.is_a?(Array)
|
|
187
|
+
v = v.collect { |e| e.instance_of?(Hash) ? __convert(e) : e }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
s[k] = v
|
|
191
|
+
end
|
|
192
|
+
s
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Config
|
|
2
|
+
module Sources
|
|
3
|
+
# Allows settings to be loaded from a "flat" hash with string keys, like ENV.
|
|
4
|
+
class EnvSource
|
|
5
|
+
attr_reader :prefix
|
|
6
|
+
attr_reader :separator
|
|
7
|
+
attr_reader :converter
|
|
8
|
+
attr_reader :parse_values
|
|
9
|
+
attr_reader :parse_arrays
|
|
10
|
+
|
|
11
|
+
def initialize(env,
|
|
12
|
+
prefix: Config.env_prefix || Config.const_name,
|
|
13
|
+
separator: Config.env_separator,
|
|
14
|
+
converter: Config.env_converter,
|
|
15
|
+
parse_values: Config.env_parse_values,
|
|
16
|
+
parse_arrays: Config.env_parse_arrays)
|
|
17
|
+
@env = env
|
|
18
|
+
@prefix = prefix.to_s.split(separator)
|
|
19
|
+
@separator = separator
|
|
20
|
+
@converter = converter
|
|
21
|
+
@parse_values = parse_values
|
|
22
|
+
@parse_arrays = parse_arrays
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def load
|
|
26
|
+
return {} if @env.nil? || @env.empty?
|
|
27
|
+
|
|
28
|
+
hash = Hash.new
|
|
29
|
+
|
|
30
|
+
@env.each do |variable, value|
|
|
31
|
+
keys = variable.to_s.split(separator)
|
|
32
|
+
|
|
33
|
+
next if keys.shift(prefix.size) != prefix
|
|
34
|
+
|
|
35
|
+
keys.map! { |key|
|
|
36
|
+
case converter
|
|
37
|
+
when :downcase then
|
|
38
|
+
key.downcase
|
|
39
|
+
when nil then
|
|
40
|
+
key
|
|
41
|
+
else
|
|
42
|
+
raise "Invalid ENV variables name converter: #{converter}"
|
|
43
|
+
end
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
leaf = keys[0...-1].inject(hash) { |h, key|
|
|
47
|
+
h[key] ||= {}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
unless leaf.is_a?(Hash)
|
|
51
|
+
conflicting_key = (prefix + keys[0...-1]).join(separator)
|
|
52
|
+
raise "Environment variable #{variable} conflicts with variable #{conflicting_key}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
leaf[keys.last] = parse_values ? __value(value) : value
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
parse_arrays ? convert_hashes_to_arrays(hash) : hash
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
def convert_hashes_to_arrays(hash)
|
|
63
|
+
hash.each_with_object({}) do |(key, value), new_hash|
|
|
64
|
+
if value.is_a?(Hash)
|
|
65
|
+
value = convert_hashes_to_arrays(value)
|
|
66
|
+
if consecutive_numeric_keys?(value.keys)
|
|
67
|
+
new_hash[key] = value.keys.sort_by(&:to_i).map { |k| value[k] }
|
|
68
|
+
else
|
|
69
|
+
new_hash[key] = value
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
new_hash[key] = value
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def consecutive_numeric_keys?(keys)
|
|
78
|
+
keys.map(&:to_i).sort == (0...keys.size).to_a && keys.all? { |k| k == k.to_i.to_s }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Try to convert string to a correct type
|
|
82
|
+
def __value(v)
|
|
83
|
+
case v
|
|
84
|
+
when 'false'
|
|
85
|
+
false
|
|
86
|
+
when 'true'
|
|
87
|
+
true
|
|
88
|
+
else
|
|
89
|
+
Integer(v) rescue Float(v) rescue v
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'erb'
|
|
3
|
+
|
|
4
|
+
module Config
|
|
5
|
+
module Sources
|
|
6
|
+
class YAMLSource
|
|
7
|
+
attr_accessor :path
|
|
8
|
+
attr_reader :evaluate_erb
|
|
9
|
+
|
|
10
|
+
def initialize(path, evaluate_erb: Config.evaluate_erb_in_yaml)
|
|
11
|
+
@path = path.to_s
|
|
12
|
+
@evaluate_erb = !!evaluate_erb
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# returns a config hash from the YML file
|
|
16
|
+
def load
|
|
17
|
+
if @path and File.exist?(@path)
|
|
18
|
+
file_contents = File.read(@path)
|
|
19
|
+
file_contents = ERB.new(file_contents).result if evaluate_erb
|
|
20
|
+
result = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(file_contents) : YAML.load(file_contents)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
result || {}
|
|
24
|
+
|
|
25
|
+
rescue Psych::SyntaxError => e
|
|
26
|
+
raise "YAML syntax error occurred while parsing #{@path}. " \
|
|
27
|
+
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
|
|
28
|
+
"Error: #{e.message}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require_relative "../error"
|
|
2
|
+
|
|
3
|
+
module Config
|
|
4
|
+
module Validation
|
|
5
|
+
class Error < ::Config::Error
|
|
6
|
+
|
|
7
|
+
def self.format(v_res)
|
|
8
|
+
v_res.errors.group_by(&:path).map do |path, messages|
|
|
9
|
+
"#{' ' * 2}#{path.join('.')}: #{messages.map(&:text).join('; ')}"
|
|
10
|
+
end.join("\n")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require_relative '../dry_validation_requirements'
|
|
2
|
+
require_relative '../error'
|
|
3
|
+
|
|
4
|
+
module Config
|
|
5
|
+
module Validation
|
|
6
|
+
module Schema
|
|
7
|
+
# Assigns schema configuration option
|
|
8
|
+
def schema=(value)
|
|
9
|
+
@schema = value
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def schema(&block)
|
|
13
|
+
if block_given?
|
|
14
|
+
# Delay require until optional schema validation is requested
|
|
15
|
+
Config::DryValidationRequirements.load_dry_validation!
|
|
16
|
+
@schema = Dry::Schema.define(&block)
|
|
17
|
+
else
|
|
18
|
+
@schema
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'config/validation/error'
|
|
2
|
+
|
|
3
|
+
module Config
|
|
4
|
+
module Validation
|
|
5
|
+
module Validate
|
|
6
|
+
def validate!
|
|
7
|
+
return unless Config.validation_contract || Config.schema
|
|
8
|
+
|
|
9
|
+
Config::DryValidationRequirements.load_dry_validation!
|
|
10
|
+
|
|
11
|
+
validate_using!(Config.validation_contract)
|
|
12
|
+
validate_using!(Config.schema)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def validate_using!(validator)
|
|
18
|
+
if validator
|
|
19
|
+
result = validator.call(to_hash)
|
|
20
|
+
|
|
21
|
+
return if result.success?
|
|
22
|
+
|
|
23
|
+
error = Config::Validation::Error.format(result)
|
|
24
|
+
raise Config::Validation::Error, "Config validation failed:\n\n#{error}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|