tcfg 0.0.1 → 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 +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/README.md +4 -4
- data/Rakefile +7 -0
- data/VERSION +1 -1
- data/lib/tcfg/tcfg_base.rb +22 -1
- data/lib/tcfg/tcfg_helper.rb +192 -32
- data/lib/tcfg/tcfg_module.rb +24 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: baf00e0fc9572c1a3004fd09a7deb75c41385e12
|
4
|
+
data.tar.gz: e58ce1b90b952e089f8b3284ce016772c9dbf668
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c791b4b7f2b322fbcd754e576d3790ee1b744721947a9ffd40460bd04b8efdffcfa8804b73c04ee8b63287c907715938cf78eff7058b6ecb259d4718ecab4d9
|
7
|
+
data.tar.gz: a39546cb361ce1712c37851c58bce65e11a260ad1b13bd2d4fff61e412cf77e9cf3c21d54454b60fb5a74bed1fd0023d270dbb15172e06ff9fce5f5eb97f42c0
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -35,7 +35,7 @@ In the project root, make a yaml file called tcfg.yml that contains all configur
|
|
35
35
|
LOG_LEVEL: INFO
|
36
36
|
|
37
37
|
#This is a special section with overrides by 'environment'
|
38
|
-
|
38
|
+
t_environments:
|
39
39
|
QA:
|
40
40
|
BASE_URL: http://qa.mysite.com
|
41
41
|
|
@@ -69,7 +69,7 @@ Now you can access configuration in any before, after, or it block, like:
|
|
69
69
|
|
70
70
|
If you need to access configuration outside of a before, after, or it block you can use the TCFG module directly:
|
71
71
|
|
72
|
-
Log.level = TCFG
|
72
|
+
Log.level = TCFG['BASE_URL']
|
73
73
|
|
74
74
|
To control your test suite, you can use environment variables. To change the browser used:
|
75
75
|
|
@@ -77,10 +77,10 @@ To control your test suite, you can use environment variables. To change the br
|
|
77
77
|
$ rspec
|
78
78
|
|
79
79
|
#To change the browser used
|
80
|
-
$
|
80
|
+
$ T_BROWSER=chrome rspec
|
81
81
|
|
82
82
|
#To change which environment the tests execute against:
|
83
|
-
$
|
83
|
+
$ T_ENVIRONMENT=QA rspec
|
84
84
|
|
85
85
|
|
86
86
|
### Other uses
|
data/Rakefile
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'bundler'
|
5
|
+
|
5
6
|
begin
|
6
7
|
Bundler.setup(:default, :development)
|
7
8
|
rescue Bundler::BundlerError => e
|
@@ -43,3 +44,9 @@ end
|
|
43
44
|
|
44
45
|
task :default => :spec
|
45
46
|
|
47
|
+
#documentation
|
48
|
+
require 'yard'
|
49
|
+
YARD::Rake::YardocTask.new do |t|
|
50
|
+
t.files = ['lib/**/*.rb'] # optional
|
51
|
+
#t.options = ['--any', '--extra', '--opts'] # optional
|
52
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/lib/tcfg/tcfg_base.rb
CHANGED
@@ -1,10 +1,31 @@
|
|
1
1
|
require_relative 'tcfg_helper'
|
2
2
|
|
3
|
+
|
3
4
|
module TCFG
|
5
|
+
# The TCFG::Base object can used an instance of configuration
|
6
|
+
#
|
7
|
+
# All of the methods from TCFG::Helper are available as instance methods
|
8
|
+
#
|
9
|
+
# cfg = TCFG::Base.new
|
10
|
+
# cfg.tcfg_set 'my_key', 'my_value'
|
11
|
+
#
|
12
|
+
# cfg['my_key']
|
13
|
+
# => 'my_value'
|
14
|
+
#
|
15
|
+
# cfg.tcfg
|
16
|
+
# => { 'my_key' => 'my_value', ... }
|
17
|
+
#
|
4
18
|
class Base
|
5
19
|
include TCFG::Helper
|
6
|
-
def initialize
|
7
20
|
|
21
|
+
#the simplest way to acces configuration
|
22
|
+
#
|
23
|
+
# cfg['my_key']
|
24
|
+
# => 'some_value'
|
25
|
+
#
|
26
|
+
# @see TCFG::Helper#tcfg_get
|
27
|
+
def [] key
|
28
|
+
tcfg_get key
|
8
29
|
end
|
9
30
|
end
|
10
31
|
end
|
data/lib/tcfg/tcfg_helper.rb
CHANGED
@@ -1,68 +1,160 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'active_support/core_ext/hash/indifferent_access'
|
3
3
|
require 'active_support/core_ext/hash/deep_merge'
|
4
|
-
|
5
|
-
|
4
|
+
require 'active_support/core_ext/hash/deep_dup'
|
5
|
+
require 'active_support/core_ext/hash/slice'
|
6
6
|
|
7
7
|
module TCFG
|
8
|
-
module Helper
|
9
|
-
DEFAULT_CONFIG_FILE = 'tcfg.yml'
|
10
|
-
|
11
|
-
def tcfg
|
12
|
-
unless @resolved_config
|
13
|
-
resolved_config = ActiveSupport::HashWithIndifferentAccess.new
|
14
8
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
9
|
+
# TCFG::Helper does all the "heavy lifting". Essentially all the logic within TCFG is defined by this module.
|
10
|
+
# The intended ways to use this module are:
|
11
|
+
#
|
12
|
+
# - use the TCFG module methods whenever a single instance of configuration will do
|
13
|
+
#
|
14
|
+
# TCFG['some_key']
|
15
|
+
# => 'some_value'
|
16
|
+
#
|
17
|
+
# TCFG.tcfg
|
18
|
+
# => { 'some_key' => 'some_value', ... }
|
19
|
+
#
|
20
|
+
# - mix it into any class that needs configuration
|
21
|
+
#
|
22
|
+
# Class MyClass
|
23
|
+
# include TCFG::Helper
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# Myclass.new.tcfg
|
27
|
+
# => { 'some_key' => 'some_value', ... }
|
28
|
+
#
|
29
|
+
# - create a configuration instance object with this module pre-mixed in
|
30
|
+
#
|
31
|
+
# cfg = TCFG::Base.new
|
32
|
+
# cfg['some_key']
|
33
|
+
# => 'some_value'
|
34
|
+
#
|
35
|
+
# cfg.tcfg
|
36
|
+
# => { 'some_key' => 'some_value', ... }
|
37
|
+
#
|
38
|
+
#
|
39
|
+
module Helper
|
20
40
|
|
21
|
-
|
22
|
-
|
41
|
+
# the public config file that is looked for unless tcfg_config_file is called
|
42
|
+
DEFAULT_CONFIG_FILE = 'tcfg.yml'
|
23
43
|
|
24
|
-
|
25
|
-
|
44
|
+
#the default prefix that goes before environment variables and the environments section in config files
|
45
|
+
DEFAULT_ENV_VAR_PREFIX = 'T_'
|
26
46
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
return
|
47
|
+
# return a copy of the resolved configuration
|
48
|
+
#
|
49
|
+
# This is the preferred way to access a complete copy of the fully resolved configuration.
|
50
|
+
#
|
51
|
+
# @return [ActiveSupport::HashWithIndifferentAccess] a copy of the resolved configuration
|
52
|
+
def tcfg
|
53
|
+
@tcfg_resolved_config ||= resolve_config
|
54
|
+
#return a deep copy of the configuration object to prevent mutations
|
55
|
+
@tcfg_resolved_config.deep_dup
|
35
56
|
end
|
36
57
|
|
58
|
+
# change the name of the public configuration file
|
59
|
+
#
|
60
|
+
# Changing the name of the public configuration file also
|
61
|
+
# changes the default secret configuration file.
|
62
|
+
# For example calling #tcfg_config_file('my_cfg.ym') will
|
63
|
+
# cause TCFG to look for 'my_cfg.secret.yml' for the secret
|
64
|
+
# file unless #tcfg_secret_config_file is also called.
|
65
|
+
#
|
66
|
+
# @see #tcfg_secret_config_file
|
67
|
+
#
|
68
|
+
# @param filename [String] the path to a yaml file
|
69
|
+
# @return [nil]
|
70
|
+
#
|
37
71
|
def tcfg_config_file filename
|
38
72
|
confirm_config_file_existence filename
|
39
73
|
tcfg_reset
|
40
74
|
@tcfg_config_filename = filename
|
75
|
+
nil
|
41
76
|
end
|
42
77
|
|
78
|
+
# change the name of the secret configuration file
|
79
|
+
#
|
80
|
+
# Calling this method if neccesary only if:
|
81
|
+
# - you dont have a public configuration file, or
|
82
|
+
# - your secret file is not named like <public name>.secret.yml
|
83
|
+
#
|
84
|
+
# @param filename [String] the path to a yaml file
|
85
|
+
# @return [nil]
|
86
|
+
#
|
43
87
|
def tcfg_secret_config_file filename
|
44
88
|
confirm_config_file_existence filename
|
45
89
|
tcfg_reset
|
46
90
|
@tcfg_secret_config_filename = filename
|
91
|
+
nil
|
47
92
|
end
|
48
93
|
|
94
|
+
# to correct way to default configuration is to use tcfg_set
|
95
|
+
#
|
96
|
+
# @param key [String] the configuration key name
|
97
|
+
# @param value [String, Integer, FixNum, Array, Hash] the value of the configuration
|
98
|
+
# @return value The same value that was passed in
|
99
|
+
#
|
49
100
|
def tcfg_set key, value
|
50
101
|
tier_code_defaults[key] = value
|
51
102
|
tcfg_reset
|
52
103
|
return value
|
53
104
|
end
|
54
105
|
|
106
|
+
# return a single piece of configuration by key
|
107
|
+
#
|
108
|
+
# @param key [String] the configuration to return
|
109
|
+
# @return [String, Integer, FixNum, Array, Hash] the value of the configuration from the resolved configuration
|
110
|
+
#
|
55
111
|
def tcfg_get key
|
56
|
-
|
112
|
+
t_tcfg = tcfg
|
113
|
+
unless t_tcfg.has_key? key
|
114
|
+
raise NoSuchConfigurationKeyError.new "No configuration defined for '#{key}'"
|
115
|
+
end
|
116
|
+
t_tcfg[key]
|
57
117
|
end
|
58
118
|
|
119
|
+
# force tcfg to re-resolve the configuration
|
120
|
+
#
|
121
|
+
# This method can be called to force tcfg to re-resolve the configuration.
|
122
|
+
# This generally should not be needed directly, but situations where it
|
123
|
+
# could be used include:
|
124
|
+
# - The underlying config file(s) have changed and you want to re-read them
|
125
|
+
# - The underlying ENV environment variables have changed and you want to re-read them
|
126
|
+
#
|
127
|
+
# @return [nil]
|
128
|
+
#
|
59
129
|
def tcfg_reset
|
60
|
-
@
|
130
|
+
@tcfg_resolved_config = nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# change the prefix used for configuration finding
|
134
|
+
#
|
135
|
+
# By default TCFG looks for
|
136
|
+
# - environment variables prefixed with T_
|
137
|
+
# - sections in config files called t_environments
|
138
|
+
#
|
139
|
+
# This method lets you change that to any prefic you want.
|
140
|
+
# For example calling it like this:
|
141
|
+
#
|
142
|
+
# TCFG.tcfg_set_env_var_prefix 'MY_'
|
143
|
+
#
|
144
|
+
# Will cause tcfg to look for:
|
145
|
+
# - environment variables prefixed with MY_
|
146
|
+
# - sections in config files called my_environments
|
147
|
+
#
|
148
|
+
# @param prefix [String] the new prefix. It can be an empty string to specify no prefix should be used.
|
149
|
+
# @return [nil]
|
150
|
+
#
|
151
|
+
def tcfg_set_env_var_prefix prefix
|
152
|
+
@tcfg_env_var_prefix = prefix
|
153
|
+
tcfg_reset
|
61
154
|
end
|
62
155
|
|
63
156
|
private
|
64
157
|
|
65
|
-
#define how we handle state for each of the tiers
|
66
158
|
def tier_code_defaults
|
67
159
|
@tcfg_code_defaults ||= ActiveSupport::HashWithIndifferentAccess.new
|
68
160
|
end
|
@@ -87,12 +179,38 @@ module TCFG
|
|
87
179
|
end
|
88
180
|
|
89
181
|
def tier_environment_overrides
|
90
|
-
tenv =
|
182
|
+
tenv = tcfg_fetch_env_var 'ENVIRONMENT', nil
|
91
183
|
return {} unless @tcfg_environments_config and tenv
|
92
184
|
unless @tcfg_environments_config.has_key? tenv
|
93
185
|
raise TCFG::NoSuchEnvironmentError.new "No such environment in configuration '#{tenv}'"
|
94
186
|
end
|
95
|
-
@tcfg_environments_config[tenv].merge({'
|
187
|
+
@tcfg_environments_config[tenv].merge({tcfg_env_var_name('ENVIRONMENT') => tenv})
|
188
|
+
end
|
189
|
+
|
190
|
+
#environment variable overrides is the most complex tier
|
191
|
+
#The rules:
|
192
|
+
# - You can only override configuration that exists in a lower tier. An exception is raised otherwise
|
193
|
+
# - You can use a - character to override deeply
|
194
|
+
# - All env variables must start with the prefix (T_)
|
195
|
+
# - ignore the special T_ENVIRONMENT variable
|
196
|
+
def tier_environment_variable_overrides lower_config={}
|
197
|
+
tcfg_env_vars = ENV.keys.select{|ev| ev =~ /^#{tcfg_env_var_prefix}/}
|
198
|
+
tcfg_env = ENV.to_hash.slice(*tcfg_env_vars)
|
199
|
+
tcfg_env.each_pair do |full_var_name, value|
|
200
|
+
var_chain = full_var_name.sub(/^#{tcfg_env_var_prefix}/, '').split('-')
|
201
|
+
next if var_chain.first.upcase == 'ENVIRONMENT'
|
202
|
+
parent_of_config_to_modify = lower_config
|
203
|
+
var_chain[0...-1].each do |parent_key|
|
204
|
+
unless parent_of_config_to_modify.respond_to? :has_key? and parent_of_config_to_modify.has_key? parent_key
|
205
|
+
raise BadParentInDeepOverrideError.new("No such parent '#{parent_key}' for deep override '#{full_var_name}'")
|
206
|
+
end
|
207
|
+
parent_of_config_to_modify = parent_of_config_to_modify[parent_key]
|
208
|
+
end
|
209
|
+
unless parent_of_config_to_modify.respond_to? :has_key? and parent_of_config_to_modify.has_key? var_chain.last
|
210
|
+
raise NoSuchConfigurationKeyError.new("No such configuration for '#{var_chain.last}' for override var '#{full_var_name}'")
|
211
|
+
end
|
212
|
+
parent_of_config_to_modify[var_chain.last] = value
|
213
|
+
end
|
96
214
|
end
|
97
215
|
|
98
216
|
def tcfg_load_optional_config_file filename
|
@@ -100,7 +218,7 @@ module TCFG
|
|
100
218
|
if File.exist? filename
|
101
219
|
file_contents = YAML.load_file filename
|
102
220
|
hashed = ActiveSupport::HashWithIndifferentAccess.new file_contents
|
103
|
-
environments = hashed.delete
|
221
|
+
environments = hashed.delete tcfg_env_var_name('environments').downcase
|
104
222
|
@tcfg_environments_config.deep_merge!(environments) if environments
|
105
223
|
hashed
|
106
224
|
else
|
@@ -114,9 +232,51 @@ module TCFG
|
|
114
232
|
end
|
115
233
|
end
|
116
234
|
|
235
|
+
def tcfg_env_var_prefix
|
236
|
+
@tcfg_env_var_prefix ||= DEFAULT_ENV_VAR_PREFIX
|
237
|
+
@tcfg_env_var_prefix
|
238
|
+
end
|
239
|
+
|
240
|
+
def tcfg_env_var_name key
|
241
|
+
tcfg_env_var_prefix + key
|
242
|
+
end
|
243
|
+
|
244
|
+
def tcfg_fetch_env_var key, not_defined_value
|
245
|
+
ENV.fetch tcfg_env_var_name(key), not_defined_value
|
246
|
+
end
|
247
|
+
|
248
|
+
def resolve_config
|
249
|
+
resolved_config = ActiveSupport::HashWithIndifferentAccess.new
|
250
|
+
|
251
|
+
#tier 1 code defaults
|
252
|
+
resolved_config.merge! tier_code_defaults
|
253
|
+
|
254
|
+
#tier 2, the main config file
|
255
|
+
resolved_config.merge! tier_config_file
|
256
|
+
|
257
|
+
#tier 3, the main config file
|
258
|
+
resolved_config.merge! tier_secret_config_file
|
259
|
+
|
260
|
+
#tier 4, environment overrides
|
261
|
+
resolved_config.merge! tier_environment_overrides
|
262
|
+
|
263
|
+
#tier 5, environment variable overrides
|
264
|
+
tier_environment_variable_overrides resolved_config
|
265
|
+
|
266
|
+
resolved_config
|
267
|
+
end
|
268
|
+
|
117
269
|
end
|
118
270
|
|
119
|
-
#
|
271
|
+
#Raised when the requested environment is not available
|
120
272
|
class NoSuchEnvironmentError < StandardError; end
|
273
|
+
|
274
|
+
#raised when a non-existent config file is specified
|
121
275
|
class NoSuchConfigFileError < StandardError; end
|
276
|
+
|
277
|
+
#raise when a non-existent piece of configuration is requested
|
278
|
+
class NoSuchConfigurationKeyError < StandardError; end
|
279
|
+
|
280
|
+
#raise when a deep env var override specifies a non-existent parent hash in config
|
281
|
+
class BadParentInDeepOverrideError < StandardError; end
|
122
282
|
end
|
data/lib/tcfg/tcfg_module.rb
CHANGED
@@ -1,7 +1,31 @@
|
|
1
1
|
require_relative 'tcfg_helper'
|
2
2
|
|
3
|
+
# The TCFG module can be used as singleton for accessing and specifying configuration
|
4
|
+
#
|
5
|
+
# All of the public methods from TCFG::Helper are available as class methods
|
6
|
+
# right off the TCFG module
|
7
|
+
#
|
8
|
+
# TCFG.tcfg_set 'some_key', 'some_value'
|
9
|
+
#
|
10
|
+
# TCFG['some_key']
|
11
|
+
# => 'some_value'
|
12
|
+
#
|
13
|
+
# TCFG.tcfg
|
14
|
+
# => { 'some_key' => 'some_value', ... }
|
15
|
+
#
|
16
|
+
#
|
3
17
|
module TCFG
|
4
18
|
class << self
|
5
19
|
include TCFG::Helper
|
20
|
+
|
21
|
+
#the simplest way to acces configuration
|
22
|
+
#
|
23
|
+
# TCFG['my_key']
|
24
|
+
# => 'some_value'
|
25
|
+
#
|
26
|
+
# @see TCFG::Helper#tcfg_get
|
27
|
+
def [] key
|
28
|
+
tcfg_get key
|
29
|
+
end
|
6
30
|
end
|
7
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tcfg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- robert schultheis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
55
69
|
description: A tiered approach to configuration which allows for full control of your
|
56
70
|
test suite through environment variables
|
57
71
|
email: robert.schultheis@gmail.com
|