app_config_loader 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +190 -0
- data/Rakefile +4 -0
- data/app_config_loader.gemspec +24 -0
- data/lib/app_config_loader/config.rb +22 -0
- data/lib/app_config_loader/config_entry.rb +75 -0
- data/lib/app_config_loader/config_map.rb +90 -0
- data/lib/app_config_loader/config_with_indifferent_access.rb +25 -0
- data/lib/app_config_loader/errors.rb +5 -0
- data/lib/app_config_loader/loader.rb +77 -0
- data/lib/app_config_loader/parser.rb +33 -0
- data/lib/app_config_loader/version.rb +3 -0
- data/lib/app_config_loader.rb +29 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: af7599d6ae9f0d3b78dc84c98ba95af7eba9999d
|
4
|
+
data.tar.gz: 73403a0498d2f8b518cd1f33e6de5267d205938d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eb5b8805a947bf0bbe9aae295b3f8d0b04369631c6b78cbbc491ba62c4c697dff17375f80465a773a3aa44bea3aa1f4ad0dfa3c1168aea5ddd3790fa3a33bdf8
|
7
|
+
data.tar.gz: 29560b75a9d77fa13ea173de079eb0fbb966ab122df19de33103b3d23a6740edda43a4cc8ce8e6deccd5cb20cc746ef7b6b888c7f9116dbe28c15e6aa9ffd6bf
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 lscspirit
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# AppConfigLoader
|
2
|
+
|
3
|
+
A easy way to define application configurations using YAML in your Rails environment.
|
4
|
+
|
5
|
+
## Getting Started
|
6
|
+
|
7
|
+
Add the following line to your Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'app_config_loader'
|
11
|
+
```
|
12
|
+
|
13
|
+
Run the bundle command to install it.
|
14
|
+
|
15
|
+
Create a YAML file in the default directory `config/app_configs` to put your app config. For example:
|
16
|
+
|
17
|
+
```YAML
|
18
|
+
# config/app_configs/some_service.yml
|
19
|
+
|
20
|
+
'*.some_service':
|
21
|
+
'host': dev.someservice.com
|
22
|
+
|
23
|
+
'production.some_service':
|
24
|
+
'host': prod.someservice.com
|
25
|
+
```
|
26
|
+
|
27
|
+
Create an initializer `config/initializers/00_app_config.rb` to configure and initialize AppConfigLoader. Typically, you would want AppConfigLoader to be the first thing to be initialized so that you can access your app config values in the remaining Rails initialization process. This is why the initializer filename has the '00_' prefix.
|
28
|
+
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
# config/initializers/00_app_config.rb
|
32
|
+
|
33
|
+
APP_CONFIG = AppConfigLoader.load
|
34
|
+
```
|
35
|
+
|
36
|
+
From this point onward, you can access your app config values through `APP_CONFIG`.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
# when AppConfigLoader' env is not "production"
|
40
|
+
APP_CONFIG['some_service.host'] # => 'dev.someservice.com'
|
41
|
+
|
42
|
+
# when AppConfigLoader' env is set to "production"
|
43
|
+
APP_CONFIG['some_service.host'] # => 'prod.someservice.com'
|
44
|
+
```
|
45
|
+
|
46
|
+
## Configuration
|
47
|
+
|
48
|
+
You can configure where and how AppConfigLoader load your app config file(s).
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
APP_CONFIG = AppConfigLoader.load do |config|
|
52
|
+
config.use_domain = true
|
53
|
+
config.domain = 'us'
|
54
|
+
config.config_paths << '/path/to/additional_config.yml'
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
Below is a list of all the settings available:
|
59
|
+
|
60
|
+
* `config.env` - (String) app config environment. This determines which set of app config values to load. **Default:** `Rails.env`.
|
61
|
+
* `config.use_domain` - (Boolean) whether the app config key uses a domain. **Default:** `false`.
|
62
|
+
* `config.domain` - (String) app config domain. This determines which set of app config values to load. It is only applicable if `use_domain` is set to true. **Default:** `nil`
|
63
|
+
* `config.config_paths` - (Array<String>) a list of paths from where app config YAML files should be loaded. Path can either be a file or a directory. With a directory path, all YAML files within that directory will be loaded. **Default:** `['<rails root>/config/app_configs']`.
|
64
|
+
* `config.local_overrides` - (String) path to a local overrides app config file. Entries in this file take precedence over all values from other files. **Default:** `<rails root>/config/app_configs/local_overrides.yml`.
|
65
|
+
|
66
|
+
## Defining your App Configs
|
67
|
+
|
68
|
+
All app configs are defined as key-value entries in YAML format. A app config value can be any valid YAML value, except for Hash. The app config key must follow either of the format below depending on whether `use_domain` is enabled:
|
69
|
+
|
70
|
+
* `<env>.<key...>` - when `use_domain` is disabled (e.g. `production.some_service.host`)
|
71
|
+
* `<env>.<domain>.<key...>` - when `use_domain` is enabled (e.g. `production.us.some_service.host`)
|
72
|
+
|
73
|
+
#### Basic Example
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# app config in YAML file
|
77
|
+
'test.timeout': 3000
|
78
|
+
'test.some_service.host': 'dev.someservice.com'
|
79
|
+
|
80
|
+
'production.timeout': 500
|
81
|
+
'production.some_service.host': 'prod.someservice.com'
|
82
|
+
'production.some_service.port': 8000
|
83
|
+
|
84
|
+
# In Rails
|
85
|
+
APP_CONFIG = AppConfigLoader.load do |config|
|
86
|
+
config.env = 'test'
|
87
|
+
end
|
88
|
+
|
89
|
+
APP_CONFIG['timeout'] # => 3000
|
90
|
+
APP_CONFIG['some_service.host'] # => dev.someservice.com
|
91
|
+
APP_CONFIG['some_service.port'] # => nil
|
92
|
+
|
93
|
+
APP_CONFIG = AppConfigLoader.load do |config|
|
94
|
+
config.env = 'production'
|
95
|
+
end
|
96
|
+
|
97
|
+
APP_CONFIG['timeout'] # => 500
|
98
|
+
APP_CONFIG['some_service.host'] # => prod.someservice.com
|
99
|
+
APP_CONFIG['some_service.port'] # => 8000
|
100
|
+
```
|
101
|
+
|
102
|
+
#### Nested Definition
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
'production.some_service':
|
106
|
+
'host': 'prod.someservice.com'
|
107
|
+
'port': 8000
|
108
|
+
|
109
|
+
# In Rails
|
110
|
+
APP_CONFIG['some_service.host'] # => prod.someservice.com
|
111
|
+
APP_CONFIG['some_service.port'] # => 8000
|
112
|
+
```
|
113
|
+
|
114
|
+
#### Using Domain in Key
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
# app config in YAML file
|
118
|
+
'production.us.some_service.host': 'prod.someservice.com'
|
119
|
+
'production.hk.some_service.host': 'prod.someservice.com.hk'
|
120
|
+
|
121
|
+
# In Rails
|
122
|
+
APP_CONFIG = AppConfigLoader.load do |config|
|
123
|
+
config.env = 'production'
|
124
|
+
config.use_domain = true
|
125
|
+
config.domain = 'us'
|
126
|
+
end
|
127
|
+
|
128
|
+
APP_CONFIG['some_service.host'] # => prod.someservice.com
|
129
|
+
|
130
|
+
APP_CONFIG = AppConfigLoader.load do |config|
|
131
|
+
config.env = 'production'
|
132
|
+
config.use_domain = true
|
133
|
+
config.domain = 'hk'
|
134
|
+
end
|
135
|
+
|
136
|
+
APP_CONFIG['some_service.host'] # => prod.someservice.com.hk
|
137
|
+
```
|
138
|
+
|
139
|
+
#### Using Wildcard Env and Domain
|
140
|
+
|
141
|
+
You may use the wildcard `*` in place of the env and the domain. This allows you to specify app config entry that is applicable in any env or domain. An app config entry with a specific env or domain always takes precedence over ones with wildcard of the same key.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
# app config in YAML file
|
145
|
+
'*.some_service.host': 'dev.someservice.com'
|
146
|
+
'production.some_service.host': 'prod.someservice.com'
|
147
|
+
|
148
|
+
# In Rails
|
149
|
+
APP_CONFIG = AppConfigLoader.load do |config|
|
150
|
+
config.env = 'test'
|
151
|
+
end
|
152
|
+
|
153
|
+
APP_CONFIG['some_service.host'] # => dev.someservice.com
|
154
|
+
|
155
|
+
APP_CONFIG = AppConfigLoader.load do |config|
|
156
|
+
config.env = 'production'
|
157
|
+
end
|
158
|
+
|
159
|
+
APP_CONFIG['some_service.host'] # => prod.someservice.com
|
160
|
+
```
|
161
|
+
|
162
|
+
### Specificity
|
163
|
+
|
164
|
+
When there a multiple app config entries for the same key, the final value is resolved based upon the specificity of the entry. The entry with the highest specificity take precedence over all other entries. In the case when more than one entries have the same specificity, the entry that is last read will take precedence.
|
165
|
+
|
166
|
+
```YAML
|
167
|
+
'production.us.some_service.host': 'prod.someservice.com' # highest specificity
|
168
|
+
'*.us.some_service.host': 'prod.someservice.com'
|
169
|
+
'production.*.some_service.host': 'prod.someservice.com'
|
170
|
+
'*.*.some_service.host': 'prod.someservice.com' # lowest specificity
|
171
|
+
```
|
172
|
+
|
173
|
+
## Local Overrides File
|
174
|
+
|
175
|
+
The local overrides file provides a quick way to override any entries defined in regular app config files. Entries within this file are resolved according to the specificity rule. Once resolved, the resolved entries will override any existing entries regardless of specificity. This is meant for development or testing purposes. Typically, you would not want to check this file into your source repository.
|
176
|
+
|
177
|
+
## Contributors
|
178
|
+
|
179
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/lscspirit/app_config_loader. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
180
|
+
|
181
|
+
1. Fork it
|
182
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
183
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
184
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
185
|
+
5. Create new Pull Request
|
186
|
+
|
187
|
+
## Copyright
|
188
|
+
|
189
|
+
Copyright (c) 2016 Derrick Yeung, released under the MIT license.
|
190
|
+
See [MIT License](http://opensource.org/licenses/MIT) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'app_config_loader/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "app_config_loader"
|
8
|
+
spec.version = AppConfigLoader::VERSION
|
9
|
+
spec.authors = ["Derrick Yeung"]
|
10
|
+
spec.email = ["lscspirit@hotmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Application configuration using YAML}
|
13
|
+
spec.description = %q{A easy way to define application configurations using YAML in your Rails environment.}
|
14
|
+
spec.homepage = 'https://github.com/lscspirit/app_config_loader'
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "faker"
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AppConfigLoader
|
2
|
+
class Config
|
3
|
+
attr_accessor :env, :domain, :local_overrides
|
4
|
+
|
5
|
+
def config_paths
|
6
|
+
@config_paths ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def use_domain
|
10
|
+
@use_domain === true
|
11
|
+
end
|
12
|
+
alias_method :use_domain?, :use_domain
|
13
|
+
|
14
|
+
def use_domain=(use)
|
15
|
+
@use_domain = !!use
|
16
|
+
end
|
17
|
+
|
18
|
+
def domain
|
19
|
+
use_domain? ? @domain : nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module AppConfigLoader
|
2
|
+
class ConfigEntry
|
3
|
+
attr_reader :env, :domain, :key_components, :value
|
4
|
+
|
5
|
+
#
|
6
|
+
# Constructor
|
7
|
+
#
|
8
|
+
def initialize(full_key, value, use_domain = false)
|
9
|
+
@env, @domain, @key_components = parse_full_key full_key, use_domain
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Accessor
|
15
|
+
#
|
16
|
+
|
17
|
+
def key
|
18
|
+
@key_components.join('.')
|
19
|
+
end
|
20
|
+
|
21
|
+
def specificity
|
22
|
+
score = 0
|
23
|
+
score += 1 if @env != :any # add one point if env is specified
|
24
|
+
score += 10 if @domain != :any # add 10 points if domain is specified
|
25
|
+
score
|
26
|
+
end
|
27
|
+
|
28
|
+
def applicable?(target_env, target_domain = nil)
|
29
|
+
# first matches enviroment
|
30
|
+
return false if env != target_env && env != :any
|
31
|
+
|
32
|
+
# then matches domain
|
33
|
+
return false if target_domain && domain != :any && domain != target_domain
|
34
|
+
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(o)
|
39
|
+
o.class == self.class &&
|
40
|
+
o.env == self.env &&
|
41
|
+
o.domain == self.domain &&
|
42
|
+
o.key_components == self.key_components &&
|
43
|
+
o.value == self.value
|
44
|
+
end
|
45
|
+
alias_method :eql?, :==
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def parse_full_key(full_key, use_domain)
|
50
|
+
key_ary = full_key.split('.')
|
51
|
+
|
52
|
+
raise InvalidConfigKey, "invalid config key '#{full_key}': must have a env component" unless key_ary.length > 1
|
53
|
+
raise InvalidConfigKey, "invalid config key '#{full_key}': must have a domain component when 'use_domain' is enabled" unless !use_domain || key_ary.length > 2
|
54
|
+
|
55
|
+
begin
|
56
|
+
env = normalize_key_component key_ary[0], true
|
57
|
+
domain = use_domain ? normalize_key_component(key_ary[1], true) : :any
|
58
|
+
components = (use_domain ? key_ary[2..-1] : key_ary[1..-1]).map{ |k| normalize_key_component k }
|
59
|
+
rescue => ex
|
60
|
+
raise InvalidConfigKey, "invalid config key component in '#{full_key}': #{ex.message}"
|
61
|
+
end
|
62
|
+
|
63
|
+
return env, domain, components
|
64
|
+
end
|
65
|
+
|
66
|
+
def normalize_key_component(comp, wildcard = false)
|
67
|
+
if comp == '*'
|
68
|
+
raise 'wildcard is only allowed in the \'env\' and \'domain\' component' unless wildcard
|
69
|
+
:any
|
70
|
+
else
|
71
|
+
comp.to_s
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module AppConfigLoader
|
2
|
+
class ConfigMap
|
3
|
+
def initialize
|
4
|
+
@key_map = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(entry, overwrite = false)
|
8
|
+
begin
|
9
|
+
deep_add(@key_map, entry.key_components) do |existing|
|
10
|
+
if existing
|
11
|
+
# only override the existing entry if 'force' is true or
|
12
|
+
# the new entry has a higher specificity
|
13
|
+
overwrite || entry.specificity >= existing.specificity ? entry : existing
|
14
|
+
else
|
15
|
+
entry
|
16
|
+
end
|
17
|
+
end
|
18
|
+
rescue => ex
|
19
|
+
raise ConfigKeyConflict, "key conflict for '#{entry.key}': #{ex.message}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def <<(entry)
|
24
|
+
add entry, false
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(key)
|
28
|
+
components = key.split('.')
|
29
|
+
components.reduce(@key_map) do |parent, comp|
|
30
|
+
# return nil if the parent is not a Hash
|
31
|
+
break nil unless parent.is_a?(Hash)
|
32
|
+
parent[comp.to_sym]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
alias_method :[], :get
|
36
|
+
|
37
|
+
def to_a
|
38
|
+
deep_list @key_map
|
39
|
+
end
|
40
|
+
|
41
|
+
def merge(config_map)
|
42
|
+
config_map.to_a.each { |override| self.add override, true }
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def deep_add(parent, keys, &block)
|
48
|
+
current_key = keys[0].to_sym
|
49
|
+
|
50
|
+
if keys.count == 1
|
51
|
+
val = parent[current_key]
|
52
|
+
|
53
|
+
#
|
54
|
+
# the last key
|
55
|
+
#
|
56
|
+
|
57
|
+
# check if the key has child configuration
|
58
|
+
raise "key '#{current_key}' has at least one child config" if val.is_a?(Hash)
|
59
|
+
|
60
|
+
# yield to the block to determine what value to assign to the key
|
61
|
+
parent[current_key] = yield val
|
62
|
+
else
|
63
|
+
#
|
64
|
+
# has more keys
|
65
|
+
#
|
66
|
+
|
67
|
+
# check if the key already has a value assigned
|
68
|
+
raise "key '#{current_key}' already has a value assigned" unless val.nil? || val.is_a?(Hash)
|
69
|
+
|
70
|
+
# go to the next level
|
71
|
+
val = (parent[current_key] ||= {})
|
72
|
+
deep_add val, keys[1..-1], &block
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def deep_list(root)
|
77
|
+
list = []
|
78
|
+
|
79
|
+
root.each do |key, value|
|
80
|
+
if value.is_a?(Hash)
|
81
|
+
list += deep_list value
|
82
|
+
else
|
83
|
+
list << value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
list
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AppConfigLoader
|
2
|
+
class ConfigWithIndifferentAccess
|
3
|
+
def initialize(map, prefix = nil)
|
4
|
+
@config_map = map
|
5
|
+
@prefix = prefix
|
6
|
+
end
|
7
|
+
|
8
|
+
def get(key)
|
9
|
+
# append prefix to the key if needed
|
10
|
+
target_key = @prefix ? "#{@prefix}.#{key}" : key.to_s
|
11
|
+
|
12
|
+
# return either nil, the value or another ConfigWithIndifferentAccess depending on
|
13
|
+
# what is at the key
|
14
|
+
case (entry = @config_map[target_key])
|
15
|
+
when ConfigEntry
|
16
|
+
entry.value
|
17
|
+
when Hash
|
18
|
+
self.class.new @config_map, target_key
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
alias_method :[], :get
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module AppConfigLoader
|
2
|
+
class Loader
|
3
|
+
def initialize(config)
|
4
|
+
@config = config
|
5
|
+
@parser = Parser.new @config.use_domain
|
6
|
+
|
7
|
+
raise 'Environement is not set in AppConfigLoader::Config' unless @config.env
|
8
|
+
end
|
9
|
+
|
10
|
+
# Load app config entries from yml paths listed in the
|
11
|
+
# AppConfigLoader::Config's 'config_paths' and 'local_override' properties
|
12
|
+
def load
|
13
|
+
cfg_map = ConfigMap.new
|
14
|
+
|
15
|
+
# Reads the raw config entries from the file list configured
|
16
|
+
# in the @config object
|
17
|
+
expanded_paths.each do |path|
|
18
|
+
begin
|
19
|
+
raw_entries = parse_yml path
|
20
|
+
raw_entries.each { |e| cfg_map << e if e.applicable? @config.env, @config.domain }
|
21
|
+
rescue InvalidConfigKey => ex
|
22
|
+
raise InvalidConfigFile, "config key error '#{path}': #{ex.message}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if (override_path = local_override_path)
|
27
|
+
override_map = ConfigMap.new
|
28
|
+
|
29
|
+
begin
|
30
|
+
override_entries = parse_yml override_path
|
31
|
+
override_entries.each { |e| override_map.add(e, true) if e.applicable? @config.env, @config.domain }
|
32
|
+
|
33
|
+
# merges the override entries into the main config map
|
34
|
+
cfg_map.merge override_map
|
35
|
+
rescue InvalidConfigKey => ex
|
36
|
+
raise InvalidConfigFile, "config key error '#{local_override_path}': #{ex.message}"
|
37
|
+
rescue Errno::ENOENT
|
38
|
+
# ignore file not exists error as local_override file is optional
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
ConfigWithIndifferentAccess.new cfg_map
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def parse_yml(path)
|
48
|
+
@parser.parse ERB.new(File.read(path)).result
|
49
|
+
end
|
50
|
+
|
51
|
+
# Expands the list of path entries into absolute paths for each matching files
|
52
|
+
#
|
53
|
+
# @return [Array<String>] absolute paths of all entries in the @config.config_paths
|
54
|
+
def expanded_paths
|
55
|
+
@config.config_paths.reduce(Set.new) do |memo, path|
|
56
|
+
if File.directory? path
|
57
|
+
Dir.chdir(path) do
|
58
|
+
# add all files (recursively) within the directory
|
59
|
+
memo += Dir.glob('**/*').map { |f| File.absolute_path f, path }
|
60
|
+
end
|
61
|
+
elsif File.exists? path
|
62
|
+
# add the file to the list if it exists
|
63
|
+
memo << File.absolute_path(path)
|
64
|
+
else
|
65
|
+
# try to glob the entries and add the result to the list
|
66
|
+
memo += Dir.glob(path).map { |f| File.absolute_path f, path }
|
67
|
+
end
|
68
|
+
|
69
|
+
memo
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def local_override_path
|
74
|
+
@config.local_overrides ? File.absolute_path(@config.local_overrides) : nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module AppConfigLoader
|
4
|
+
class Parser
|
5
|
+
def initialize(use_domain = false)
|
6
|
+
@use_domain = use_domain
|
7
|
+
end
|
8
|
+
|
9
|
+
# Parse a YAML format text and returns flattened list of all key entries
|
10
|
+
#
|
11
|
+
# @return Array<KeyEntry> list of key entries
|
12
|
+
def parse(content)
|
13
|
+
flatten_keys YAML.load(content)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def flatten_keys(cfg, current_full_key = nil)
|
19
|
+
cfg.reduce([]) do |flattened, (key, value)|
|
20
|
+
raise InvalidConfigKey, 'config key component must be a string' unless key.is_a?(String)
|
21
|
+
|
22
|
+
this_key = current_full_key ? "#{current_full_key}.#{key}" : key
|
23
|
+
if value.is_a? Hash
|
24
|
+
flattened += flatten_keys value, this_key
|
25
|
+
else
|
26
|
+
flattened << ConfigEntry.new(this_key, value, @use_domain)
|
27
|
+
end
|
28
|
+
|
29
|
+
flattened
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'app_config_loader/errors'
|
2
|
+
require 'app_config_loader/config'
|
3
|
+
require 'app_config_loader/loader'
|
4
|
+
require 'app_config_loader/parser'
|
5
|
+
require 'app_config_loader/config_entry'
|
6
|
+
require 'app_config_loader/config_map'
|
7
|
+
require 'app_config_loader/config_with_indifferent_access'
|
8
|
+
|
9
|
+
module AppConfigLoader
|
10
|
+
def self.load(config = nil)
|
11
|
+
raise ArgumentError, 'config must be a AppConfigLoader::Config instance' unless config.nil? || config.is_a?(AppConfigLoader::Config)
|
12
|
+
|
13
|
+
active_cfg = config || default_config
|
14
|
+
|
15
|
+
yield active_cfg if block_given?
|
16
|
+
|
17
|
+
Loader.new(active_cfg).load
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.default_config
|
23
|
+
cfg = AppConfigLoader::Config.new
|
24
|
+
cfg.env = Rails.env
|
25
|
+
cfg.config_paths << Rails.root.join('config', 'app_configs')
|
26
|
+
cfg.local_overrides = Rails.root.join('config', 'app_configs', 'local_overrides.yml')
|
27
|
+
cfg
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: app_config_loader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Derrick Yeung
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faker
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A easy way to define application configurations using YAML in your Rails
|
70
|
+
environment.
|
71
|
+
email:
|
72
|
+
- lscspirit@hotmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- .travis.yml
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- app_config_loader.gemspec
|
84
|
+
- lib/app_config_loader.rb
|
85
|
+
- lib/app_config_loader/config.rb
|
86
|
+
- lib/app_config_loader/config_entry.rb
|
87
|
+
- lib/app_config_loader/config_map.rb
|
88
|
+
- lib/app_config_loader/config_with_indifferent_access.rb
|
89
|
+
- lib/app_config_loader/errors.rb
|
90
|
+
- lib/app_config_loader/loader.rb
|
91
|
+
- lib/app_config_loader/parser.rb
|
92
|
+
- lib/app_config_loader/version.rb
|
93
|
+
homepage: https://github.com/lscspirit/app_config_loader
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.6.7
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: Application configuration using YAML
|
117
|
+
test_files: []
|