global 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|