global 1.1.0 → 2.0.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 +4 -4
- data/.rubocop.yml +29 -10
- data/.ruby-version +1 -1
- data/.travis.yml +4 -10
- data/CODE_OF_CONDUCT.md +74 -0
- data/README.md +217 -68
- data/bin/console +15 -0
- data/global.gemspec +5 -10
- data/lib/global.rb +1 -1
- data/lib/global/backend/aws_parameter_store.rb +110 -0
- data/lib/global/backend/filesystem.rb +88 -0
- data/lib/global/backend/gcp_secret_manager.rb +137 -0
- data/lib/global/base.rb +38 -84
- data/lib/global/configuration.rb +9 -2
- data/lib/global/version.rb +1 -1
- data/spec/global/backend/aws_parameter_store_spec.rb +48 -0
- data/spec/global/backend/gcp_secret_manager_spec.rb +39 -0
- data/spec/global_spec.rb +5 -35
- data/spec/merge_backends_spec.rb +23 -0
- data/spec/spec_helper.rb +5 -2
- metadata +48 -29
- data/app/assets/javascripts/global-js.js.erb +0 -2
- data/lib/global/engine.rb +0 -84
- data/spec/global/global_js_spec.rb +0 -116
- data/spec/support/javascript_helper.rb +0 -27
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'global'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/global.gemspec
CHANGED
@@ -9,10 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ['Railsware LLC']
|
10
10
|
s.email = 'contact@railsware.com'
|
11
11
|
|
12
|
-
s.
|
13
|
-
|
14
|
-
s.description = 'Simple way to load your configs from yaml'
|
15
|
-
s.summary = 'Simple way to load your configs from yaml'
|
12
|
+
s.description = 'Simple way to load your configs from yaml/aws/gcp'
|
13
|
+
s.summary = 'Simple way to load your configs from yaml/aws/gcp'
|
16
14
|
|
17
15
|
s.files = `git ls-files`.split("\n")
|
18
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -22,15 +20,12 @@ Gem::Specification.new do |s|
|
|
22
20
|
s.homepage = 'https://github.com/railsware/global'
|
23
21
|
s.licenses = ['MIT']
|
24
22
|
|
23
|
+
s.add_development_dependency 'aws-sdk-ssm', '~> 1'
|
24
|
+
s.add_development_dependency 'google-cloud-secret_manager', '~> 0'
|
25
25
|
s.add_development_dependency 'rake', '~> 12.3.1'
|
26
26
|
s.add_development_dependency 'rspec', '>= 3.0'
|
27
|
-
s.add_development_dependency 'rubocop', '~> 0.
|
27
|
+
s.add_development_dependency 'rubocop', '~> 0.81.0'
|
28
28
|
s.add_development_dependency 'simplecov', '~> 0.16.1'
|
29
|
-
if defined?(JRUBY_VERSION)
|
30
|
-
s.add_development_dependency 'therubyrhino', '>= 0'
|
31
|
-
else
|
32
|
-
s.add_development_dependency 'therubyracer', '>= 0'
|
33
|
-
end
|
34
29
|
|
35
30
|
s.add_runtime_dependency 'activesupport', '>= 2.0'
|
36
31
|
end
|
data/lib/global.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'date'
|
3
4
|
require 'yaml'
|
4
5
|
|
5
6
|
require 'active_support/core_ext/hash/indifferent_access'
|
@@ -7,7 +8,6 @@ require 'active_support/core_ext/hash/deep_merge'
|
|
7
8
|
|
8
9
|
require 'global/configuration'
|
9
10
|
require 'global/base'
|
10
|
-
require 'global/engine' if defined?(Rails)
|
11
11
|
require 'global/version'
|
12
12
|
|
13
13
|
module Global
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Global
|
4
|
+
module Backend
|
5
|
+
# Loads Global configuration from the AWS Systems Manager Parameter Store
|
6
|
+
# https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html
|
7
|
+
#
|
8
|
+
# This backend requires the `aws-sdk` or `aws-sdk-ssm` gem, so make sure to add it to your Gemfile.
|
9
|
+
#
|
10
|
+
# Available options:
|
11
|
+
# - `prefix` (required): the prefix in Parameter Store; all parameters within the prefix will be loaded;
|
12
|
+
# make sure to add a trailing slash, if you want it
|
13
|
+
# see https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-su-organize.html
|
14
|
+
# - `client`: pass you own Aws::SSM::Client instance, or alternatively set:
|
15
|
+
# - `aws_options`: credentials and other AWS configuration options that are passed to AWS::SSM::Client.new
|
16
|
+
# see https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/SSM/Client.html#initialize-instance_method
|
17
|
+
# If AWS access is already configured through environment variables,
|
18
|
+
# you don't need to pass the credentials explicitly.
|
19
|
+
#
|
20
|
+
# For Rails:
|
21
|
+
# - the `prefix` is optional and defaults to `/[Rails enviroment]/[Name of the app class]/`,
|
22
|
+
# for example: `/production/MyApp/`
|
23
|
+
# - to use a different app name, pass `app_name`,
|
24
|
+
# for example: `backend :aws_parameter_store, app_name: 'new_name_for_my_app'`
|
25
|
+
class AwsParameterStore
|
26
|
+
|
27
|
+
PATH_SEPARATOR = '/'
|
28
|
+
|
29
|
+
def initialize(options = {})
|
30
|
+
require_aws_gem
|
31
|
+
init_prefix(options)
|
32
|
+
init_client(options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def load
|
36
|
+
build_configuration_from_parameters(load_all_parameters_from_ssm)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def require_aws_gem
|
42
|
+
require 'aws-sdk-ssm'
|
43
|
+
rescue LoadError
|
44
|
+
begin
|
45
|
+
require 'aws-sdk'
|
46
|
+
rescue LoadError
|
47
|
+
raise 'Either the `aws-sdk-ssm` or `aws-sdk` gem must be installed.'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def init_prefix(options)
|
52
|
+
@prefix = if defined?(Rails)
|
53
|
+
options.fetch(:prefix) do
|
54
|
+
environment = Rails.env.to_s
|
55
|
+
app_name = options.fetch(:app_name) { Rails.application.class.module_parent_name }
|
56
|
+
"/#{environment}/#{app_name}/"
|
57
|
+
end
|
58
|
+
else
|
59
|
+
options.fetch(:prefix)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def init_client(options)
|
64
|
+
if options.key?(:client)
|
65
|
+
@ssm = options[:client]
|
66
|
+
else
|
67
|
+
aws_options = options.fetch(:aws_options, {})
|
68
|
+
@ssm = Aws::SSM::Client.new(aws_options)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def load_all_parameters_from_ssm
|
73
|
+
response = load_parameters_from_ssm
|
74
|
+
all_parameters = response.parameters
|
75
|
+
loop do
|
76
|
+
break unless response.next_token
|
77
|
+
|
78
|
+
response = load_parameters_from_ssm(response.next_token)
|
79
|
+
all_parameters.concat(response.parameters)
|
80
|
+
end
|
81
|
+
|
82
|
+
all_parameters
|
83
|
+
end
|
84
|
+
|
85
|
+
def load_parameters_from_ssm(next_token = nil)
|
86
|
+
@ssm.get_parameters_by_path(
|
87
|
+
path: @prefix,
|
88
|
+
recursive: true,
|
89
|
+
with_decryption: true,
|
90
|
+
next_token: next_token
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
# builds a nested configuration hash from the array of parameters from SSM
|
95
|
+
def build_configuration_from_parameters(parameters)
|
96
|
+
configuration = {}
|
97
|
+
parameters.each do |parameter|
|
98
|
+
parameter_parts = parameter.name[@prefix.length..-1].split(PATH_SEPARATOR).map(&:to_sym)
|
99
|
+
param_container = parameter_parts[0..-2].reduce(configuration) do |container, part|
|
100
|
+
container[part] ||= {}
|
101
|
+
end
|
102
|
+
param_container[parameter_parts[-1]] = parameter.value
|
103
|
+
end
|
104
|
+
|
105
|
+
configuration
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Global
|
4
|
+
module Backend
|
5
|
+
# Loads Global configuration from the filesystem
|
6
|
+
#
|
7
|
+
# Available options:
|
8
|
+
# - `directory` (required): the directory with config files
|
9
|
+
# - `environment` (required): the environment to load
|
10
|
+
# - `yaml_whitelist_classes`: the set of classes that are permitted to unmarshal from the configuration files
|
11
|
+
#
|
12
|
+
# For Rails:
|
13
|
+
# - the `directory` is optional and defaults to `config/global`
|
14
|
+
# - the `environment` is optional and defaults to the current Rails environment
|
15
|
+
class Filesystem
|
16
|
+
|
17
|
+
FILE_ENV_SPLIT = '.'
|
18
|
+
YAML_EXT = '.yml'
|
19
|
+
|
20
|
+
def initialize(options = {})
|
21
|
+
if defined?(Rails)
|
22
|
+
@path = options.fetch(:path) { Rails.root.join('config', 'global').to_s }
|
23
|
+
@environment = options.fetch(:environment) { Rails.env.to_s }
|
24
|
+
else
|
25
|
+
@path = options.fetch(:path)
|
26
|
+
@environment = options.fetch(:environment)
|
27
|
+
end
|
28
|
+
@yaml_whitelist_classes = options.fetch(:yaml_whitelist_classes, [])
|
29
|
+
end
|
30
|
+
|
31
|
+
def load
|
32
|
+
load_from_path(@path)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def load_from_path(path)
|
38
|
+
load_from_file(path).deep_merge(load_from_directory(path))
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_from_file(path)
|
42
|
+
config = {}
|
43
|
+
|
44
|
+
if File.exist?(file = "#{path}#{YAML_EXT}")
|
45
|
+
configurations = load_yml_file(file)
|
46
|
+
config = get_config_by_key(configurations, 'default')
|
47
|
+
config.deep_merge!(get_config_by_key(configurations, @environment))
|
48
|
+
if File.exist?(env_file = "#{path}#{FILE_ENV_SPLIT}#{@environment}#{YAML_EXT}")
|
49
|
+
config.deep_merge!(load_yml_file(env_file) || {})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
config
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_config_by_key(config, key)
|
57
|
+
return {} if config.empty?
|
58
|
+
|
59
|
+
config[key.to_sym] || config[key.to_s] || {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def load_yml_file(file)
|
63
|
+
YAML.safe_load(
|
64
|
+
ERB.new(IO.read(file)).result,
|
65
|
+
[Date, Time, DateTime, Symbol].concat(@yaml_whitelist_classes),
|
66
|
+
[], true
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_from_directory(path)
|
71
|
+
config = {}
|
72
|
+
|
73
|
+
if File.directory?(path)
|
74
|
+
Dir["#{path}/*"].each do |entry|
|
75
|
+
namespace = File.basename(entry, YAML_EXT)
|
76
|
+
next if namespace.include? FILE_ENV_SPLIT # skip files with dot(s) in name
|
77
|
+
|
78
|
+
file_with_path = File.join(File.dirname(entry), File.basename(entry, YAML_EXT))
|
79
|
+
config.deep_merge!(namespace => load_from_path(file_with_path))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
config
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Global
|
4
|
+
module Backend
|
5
|
+
# Loads Global configuration from the Google Cloud Secret Manager
|
6
|
+
# https://cloud.google.com/secret-manager/docs
|
7
|
+
#
|
8
|
+
# This backend requires the `google-cloud-secret_manager` gem, so make sure to add it to your Gemfile.
|
9
|
+
#
|
10
|
+
# Available options:
|
11
|
+
# - `project_id` (required): Google Cloud project name
|
12
|
+
# - `prefix` (required): the prefix in Secret Manager; all parameters within the prefix will be loaded;
|
13
|
+
# make sure to add a undescore, if you want it
|
14
|
+
# see https://cloud.google.com/secret-manager/docs/overview
|
15
|
+
# - `client`: pass you own Google::Cloud::SecretManager instance, or alternatively set:
|
16
|
+
# - `gcp_options`: credentials and other Google cloud configuration options
|
17
|
+
# that are passed to Google::Cloud::SecretManager.configure
|
18
|
+
# see https://googleapis.dev/ruby/google-cloud-secret_manager/latest/index.html
|
19
|
+
# If Google Cloud access is already configured through environment variables,
|
20
|
+
# you don't need to pass the credentials explicitly.
|
21
|
+
#
|
22
|
+
# For Rails:
|
23
|
+
# - the `prefix` is optional and defaults to `[Rails enviroment]-[Name of the app class]-`,
|
24
|
+
# for example: `production-myapp-`
|
25
|
+
# - to use a different app name, pass `app_name`,
|
26
|
+
# for example: `backend :gcp_secret_manager, app_name: 'new_name_for_my_app'`
|
27
|
+
class GcpSecretManager
|
28
|
+
|
29
|
+
GCP_SEPARATOR = '/'
|
30
|
+
PATH_SEPARATOR = '-'
|
31
|
+
|
32
|
+
def initialize(options = {})
|
33
|
+
@project_id = options.fetch(:project_id)
|
34
|
+
require_gcp_gem
|
35
|
+
init_prefix(options)
|
36
|
+
init_client(options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def load
|
40
|
+
pages = load_all_parameters_from_gcsm
|
41
|
+
|
42
|
+
configuration = {}
|
43
|
+
pages.each do |page|
|
44
|
+
configuration.deep_merge!(build_configuration_from_page(page))
|
45
|
+
end
|
46
|
+
|
47
|
+
configuration
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def require_gcp_gem
|
53
|
+
require 'google/cloud/secret_manager'
|
54
|
+
rescue LoadError
|
55
|
+
raise 'The `google-cloud-secret_manager` gem must be installed.'
|
56
|
+
end
|
57
|
+
|
58
|
+
def init_prefix(options)
|
59
|
+
@prefix = if defined?(Rails)
|
60
|
+
options.fetch(:prefix) do
|
61
|
+
environment = Rails.env.to_s
|
62
|
+
app_name = options.fetch(:app_name) { Rails.application.class.module_parent_name }
|
63
|
+
"#{environment}-#{app_name}-"
|
64
|
+
end
|
65
|
+
else
|
66
|
+
options.fetch(:prefix)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def init_client(options)
|
71
|
+
if options.key?(:client)
|
72
|
+
@gcsm = options[:client]
|
73
|
+
else
|
74
|
+
gcp_options = options.fetch(:gcp_options, {})
|
75
|
+
@gcsm = Google::Cloud::SecretManager.secret_manager_service do |config|
|
76
|
+
config.credentials = gcp_options[:credentials] if gcp_options[:credentials]
|
77
|
+
config.timeout = gcp_options[:timeout] if gcp_options[:timeout]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def load_all_parameters_from_gcsm
|
83
|
+
response = load_parameters_from_gcsm
|
84
|
+
all_pages = [response]
|
85
|
+
loop do
|
86
|
+
break if response.next_page_token.empty?
|
87
|
+
|
88
|
+
response = load_parameters_from_gcsm(response.next_page_token)
|
89
|
+
all_pages << response
|
90
|
+
end
|
91
|
+
|
92
|
+
all_pages
|
93
|
+
end
|
94
|
+
|
95
|
+
def load_parameters_from_gcsm(next_token = nil)
|
96
|
+
@gcsm.list_secrets(
|
97
|
+
parent: @gcsm.project_path(project: @project_id),
|
98
|
+
page_size: 25_000,
|
99
|
+
page_token: next_token
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
# builds a nested configuration hash from the array of parameters from Secret Manager
|
104
|
+
def build_configuration_from_page(page)
|
105
|
+
configuration = {}
|
106
|
+
|
107
|
+
page.each do |parameter|
|
108
|
+
key_name = get_gcp_key_name(parameter)
|
109
|
+
next unless key_name.start_with?(@prefix)
|
110
|
+
|
111
|
+
parameter_parts = key_name[@prefix.length..-1].split(PATH_SEPARATOR).map(&:to_sym)
|
112
|
+
param_container = parameter_parts[0..-2].reduce(configuration) do |container, part|
|
113
|
+
container[part] ||= {}
|
114
|
+
end
|
115
|
+
param_container[parameter_parts[-1]] = get_latest_key_value(key_name)
|
116
|
+
end
|
117
|
+
|
118
|
+
configuration
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_gcp_key_name(parameter)
|
122
|
+
parameter.name.split(GCP_SEPARATOR).last
|
123
|
+
end
|
124
|
+
|
125
|
+
def get_latest_key_value(key_name)
|
126
|
+
name = @gcsm.secret_version_path(
|
127
|
+
project: @project_id,
|
128
|
+
secret: key_name,
|
129
|
+
secret_version: 'latest'
|
130
|
+
)
|
131
|
+
version = @gcsm.access_secret_version(name: name)
|
132
|
+
version.payload.data
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/lib/global/base.rb
CHANGED
@@ -6,19 +6,21 @@ require 'json'
|
|
6
6
|
module Global
|
7
7
|
module Base
|
8
8
|
|
9
|
-
FILE_ENV_SPLIT = '.'
|
10
|
-
YAML_EXT = '.yml'
|
11
|
-
|
12
9
|
extend self
|
13
10
|
|
14
|
-
attr_writer :environment, :config_directory, :namespace, :except, :only, :yaml_whitelist_classes
|
15
|
-
|
16
11
|
def configure
|
17
12
|
yield self
|
18
13
|
end
|
19
14
|
|
20
15
|
def configuration
|
21
|
-
|
16
|
+
raise 'Backend must be defined' unless @backends
|
17
|
+
|
18
|
+
@configuration ||= begin
|
19
|
+
configuration_hash = @backends.reduce({}) do |configuration, backend|
|
20
|
+
configuration.deep_merge(backend.load.with_indifferent_access)
|
21
|
+
end
|
22
|
+
Configuration.new(configuration_hash)
|
23
|
+
end
|
22
24
|
end
|
23
25
|
|
24
26
|
def reload!
|
@@ -26,93 +28,45 @@ module Global
|
|
26
28
|
configuration
|
27
29
|
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
@yaml_whitelist_classes ||= []
|
51
|
-
end
|
52
|
-
|
53
|
-
def generate_js(options = {})
|
54
|
-
current_namespace = options[:namespace] || namespace
|
55
|
-
|
56
|
-
js_options = { except: except, only: only }.merge(options)
|
57
|
-
"window.#{current_namespace} = #{configuration.filter(js_options).to_json}"
|
58
|
-
end
|
59
|
-
|
60
|
-
protected
|
61
|
-
|
62
|
-
def load_configuration(dir, env)
|
63
|
-
config = load_from_file(dir, env)
|
64
|
-
config.deep_merge!(load_from_directory(dir, env))
|
65
|
-
Configuration.new(config)
|
66
|
-
end
|
67
|
-
|
68
|
-
def load_from_file(dir, env)
|
69
|
-
config = {}
|
70
|
-
|
71
|
-
if File.exist?(file = "#{dir}#{YAML_EXT}")
|
72
|
-
configurations = load_yml_file(file)
|
73
|
-
config = get_config_by_key(configurations, 'default')
|
74
|
-
config.deep_merge!(get_config_by_key(configurations, env))
|
75
|
-
if File.exist?(env_file = "#{dir}#{FILE_ENV_SPLIT}#{env}#{YAML_EXT}")
|
76
|
-
config.deep_merge!(load_yml_file(env_file) || {})
|
77
|
-
end
|
31
|
+
# Add a backend to load configuration from.
|
32
|
+
#
|
33
|
+
# You can define several backends; they will all be loaded
|
34
|
+
# and the configuration hashes will be merged.
|
35
|
+
#
|
36
|
+
# Configure with either:
|
37
|
+
# Global.backend :filesystem, directory: 'config', environment: Rails.env
|
38
|
+
# or:
|
39
|
+
# Global.backend YourConfigurationBackend.new
|
40
|
+
#
|
41
|
+
# backend configuration classes MUST have a `load` method that returns a configuration Hash
|
42
|
+
def backend(backend, options = {})
|
43
|
+
@backends ||= []
|
44
|
+
if backend.is_a?(Symbol)
|
45
|
+
require "global/backend/#{backend}"
|
46
|
+
backend_class = Global::Backend.const_get(camel_case(backend.to_s))
|
47
|
+
@backends.push backend_class.new(options)
|
48
|
+
elsif backend.respond_to?(:load)
|
49
|
+
@backends.push backend
|
50
|
+
else
|
51
|
+
raise 'Backend must be either a Global::Backend class or a symbol'
|
78
52
|
end
|
79
|
-
|
80
|
-
config
|
81
|
-
end
|
82
|
-
|
83
|
-
def get_config_by_key(config, key)
|
84
|
-
config[key.to_sym] || config[key.to_s] || {}
|
85
|
-
end
|
86
|
-
|
87
|
-
def load_yml_file(file)
|
88
|
-
YAML.safe_load(
|
89
|
-
ERB.new(IO.read(file)).result,
|
90
|
-
[Date, Time, DateTime, Symbol].concat(yaml_whitelist_classes),
|
91
|
-
[], true
|
92
|
-
)
|
93
53
|
end
|
94
54
|
|
95
|
-
|
96
|
-
config = {}
|
97
|
-
|
98
|
-
if File.directory?(dir)
|
99
|
-
Dir["#{dir}/*"].each do |entry|
|
100
|
-
namespace = File.basename(entry, YAML_EXT)
|
101
|
-
next if namespace.include? FILE_ENV_SPLIT # skip files with dot(s) in name
|
102
|
-
file_with_path = File.join(File.dirname(entry), File.basename(entry, YAML_EXT))
|
103
|
-
config.deep_merge!(namespace => load_configuration(file_with_path, env))
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
config
|
108
|
-
end
|
55
|
+
protected
|
109
56
|
|
110
57
|
def respond_to_missing?(method, include_private = false)
|
111
58
|
configuration.key?(method) || super
|
112
59
|
end
|
113
60
|
|
114
61
|
def method_missing(method, *args, &block)
|
115
|
-
configuration.key?(method) ? configuration
|
62
|
+
configuration.key?(method) ? configuration.get_configuration_value(method) : super
|
63
|
+
end
|
64
|
+
|
65
|
+
# from Bundler::Thor::Util.camel_case
|
66
|
+
def camel_case(str)
|
67
|
+
return str if str !~ /_/ && str =~ /[A-Z]+.*/
|
68
|
+
|
69
|
+
str.split('_').map(&:capitalize).join
|
116
70
|
end
|
117
71
|
|
118
72
|
end
|