configvar 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/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +19 -0
- data/LICENSE.md +19 -0
- data/README.md +131 -0
- data/Rakefile +12 -0
- data/configvar.gemspec +26 -0
- data/lib/configvar.rb +30 -0
- data/lib/configvar/context.rb +141 -0
- data/lib/configvar/error.rb +4 -0
- data/lib/configvar/version.rb +3 -0
- data/test/configvar_test.rb +29 -0
- data/test/context_test.rb +218 -0
- data/test/helper.rb +22 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 37d5bee26a68074ec2e63a8e194291aeac3c68cc
|
4
|
+
data.tar.gz: 3110ae9bf7e441707e9ec653c9dddd69466afdee
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e12a77bb4ce46bd1acb00786c607fd5864853ecca90d305c0aa5af115c494a5c75d11000db603a7f5df9b545ffd98880fbc5f6f011de7099a65f161d7ed9a7ba
|
7
|
+
data.tar.gz: d2cf44ff80a9cde9d07bfefa626c4dab06a2384a01ee4cdf66888bee72fec63d2d66ca8eedc8b76eb2a1974f910394b3da479e7309509bcd5ab4fe7c82667c90
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015 Heroku
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
[](https://magnum.travis-ci.com/heroku/configvar)
|
2
|
+
|
3
|
+
# ConfigVar
|
4
|
+
|
5
|
+
Manage configuration values loaded from the environment.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
You need to define the configuration variables that `ConfigVar` loads from the
|
10
|
+
environment.
|
11
|
+
|
12
|
+
### Required variables
|
13
|
+
|
14
|
+
Required variables can be defined for string, integer and boolean values.
|
15
|
+
When the configuration is initialized from the environment a
|
16
|
+
`ConfigVar::ConfigError` exception is raised if a required variable isn't
|
17
|
+
defined.
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
config = ConfigVar.define do
|
21
|
+
required_string :database_url
|
22
|
+
required_int :port
|
23
|
+
required_bool :enabled
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
With these definitions the environment must contain valid `DATABASE_URL`,
|
28
|
+
`PORT` and `ENABLED` variables. If any value is invalid an `ArgumentError`
|
29
|
+
exception is raised. Boolean values are case insensitive and may be `0` or
|
30
|
+
`false` for false and `1` or `true` for true.
|
31
|
+
|
32
|
+
### Optional variables
|
33
|
+
|
34
|
+
Optional variables can also be defined for string, integer and boolean values.
|
35
|
+
A default for each value must be provided. The values must be of the same
|
36
|
+
type as the optional value or `nil` if a default value isn't needed.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
config = ConfigVar.define do
|
40
|
+
optional_string :name, 'Bob'
|
41
|
+
optional_int :age, 42
|
42
|
+
optional_bool :friendly, true
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Default values will be used if `NAME`, `AGE` or `FRIENDLY` environment
|
47
|
+
variables are not present when the configuration is loaded.
|
48
|
+
|
49
|
+
### Custom variables
|
50
|
+
|
51
|
+
When simple string, integer and boolean values aren't sufficient you can
|
52
|
+
provide a block to process values from the environment according to your
|
53
|
+
needs. For example, if you want to load a required integer that must always
|
54
|
+
be 0 or greater you can provide a custom block to do the required validation.
|
55
|
+
It must take the environment to load values from and return a `Hash` of values
|
56
|
+
to include in the loaded configuration. The block should raise a
|
57
|
+
`ConfigVar::ConfigError` or `ArgumentError` exception if a required value is
|
58
|
+
missing or if any loaded value is invalid, respectively.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
config = ConfigVar.define do
|
62
|
+
required_custom :age do |env|
|
63
|
+
if value = env['AGE']
|
64
|
+
value = Integer(value)
|
65
|
+
if value < 0
|
66
|
+
raise ArgumentError.new("#{value} for AGE must be a 0 or greater")
|
67
|
+
end
|
68
|
+
{age: value}
|
69
|
+
else
|
70
|
+
raise ConfigVar::ConfigError.new(name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
Custom functions can return multiple key/value pairs and all of them will be
|
77
|
+
included in the loaded configuration. For example, if you want to extract
|
78
|
+
parts of a URL and make them available as individual configuration variables
|
79
|
+
you can provide a custom block to do so:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
config = ConfigVar.define do
|
83
|
+
optional_custom :url do |env|
|
84
|
+
if value = env['URL']
|
85
|
+
url = URI.parse(value)
|
86
|
+
{host: url.host, port: url.port, path: url.path}
|
87
|
+
else
|
88
|
+
{host: 'localhost', port: nil, path: '/example'}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
## Accessing configuration variables
|
95
|
+
|
96
|
+
The configuration object returned by `ConfigVar.define` exposes a basic
|
97
|
+
collection interface for accessing variables loaded from the environment,
|
98
|
+
based on the definitions provided.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
config = ConfigVar.define do
|
102
|
+
required_string :database_url
|
103
|
+
required_int :port
|
104
|
+
required_bool :enabled
|
105
|
+
end
|
106
|
+
|
107
|
+
database_url = config.database_url
|
108
|
+
port = config.port
|
109
|
+
enabled = config.enabled
|
110
|
+
```
|
111
|
+
|
112
|
+
A `NoMethodError` exception is raised if an unknown configuration variable is
|
113
|
+
accessed.
|
114
|
+
|
115
|
+
## Reloading configuration variables
|
116
|
+
|
117
|
+
The configuration object can be reloaded at any time from a `Hash` containing
|
118
|
+
environment variables. For example, it's easy to load values from a file to
|
119
|
+
refresh an already initialized configuration object. `ConfigVar.define` will
|
120
|
+
always initialize the `config` object from the environment.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
config = ConfigVar.define do
|
124
|
+
required_string :database_url
|
125
|
+
required_int :port
|
126
|
+
required_bool :enabled
|
127
|
+
end
|
128
|
+
|
129
|
+
env = load_values_from_file('my.config')
|
130
|
+
config.reload(env)
|
131
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
Rake::TestTask.new do |task|
|
5
|
+
task.verbose = true
|
6
|
+
task.ruby_opts << '-r minitest/autorun'
|
7
|
+
task.ruby_opts << '-r configvar'
|
8
|
+
task.ruby_opts << '-I test'
|
9
|
+
task.test_files = FileList['test/**/*_test.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :test
|
data/configvar.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'configvar/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'configvar'
|
10
|
+
spec.version = ConfigVar::VERSION
|
11
|
+
spec.authors = ['jkakar', 'heroku']
|
12
|
+
spec.email = ['jkakar@kakar.ca', 'opensource@heroku.com']
|
13
|
+
spec.description = 'Manage configuration loaded from the environment'
|
14
|
+
spec.summary = 'Manage configuration loaded from the environment'
|
15
|
+
spec.homepage = 'https://github.com/heroku/configvar'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep('^(test|spec|features)/')
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'minitest'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
end
|
data/lib/configvar.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module ConfigVar
|
2
|
+
# Define required and optional configuration variables and load them from
|
3
|
+
# the environment. Returns a configuration object that can be treated like
|
4
|
+
# a Hash with values available using lowercase symbols. For example, a PORT
|
5
|
+
# value from the environment can be accesses as config.port in the returned
|
6
|
+
# object. Booleans are only considered valid if they are one of '0', '1',
|
7
|
+
# 'true' or 'false'. The values are case insensitive, so 'TRUE', 'True' and
|
8
|
+
# 'TrUe' are all valid booleans.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# config = ConfigVar.define do
|
13
|
+
# required_string :database_url
|
14
|
+
# required_int :port
|
15
|
+
# required_bool :enabled
|
16
|
+
# optional_string :name, 'Bob'
|
17
|
+
# optional_int :age, 42
|
18
|
+
# optional_bool :friendly, true
|
19
|
+
# end
|
20
|
+
def self.define(&blk)
|
21
|
+
context = ConfigVar::Context.new
|
22
|
+
context.instance_eval(&blk)
|
23
|
+
context.reload(ENV)
|
24
|
+
context
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'configvar/context'
|
29
|
+
require 'configvar/error'
|
30
|
+
require 'configvar/version'
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module ConfigVar
|
2
|
+
class Context
|
3
|
+
def initialize
|
4
|
+
@definitions = {}
|
5
|
+
@values = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
# Reload the environment from a Hash of available environment values.
|
9
|
+
def reload(env)
|
10
|
+
@values = {}
|
11
|
+
@definitions.each_value do |function|
|
12
|
+
@values.merge!(function.call(env))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Fetch a configuration value. The name must be a lowercase version of an
|
17
|
+
# uppercase name defined in the environment. A NoMethodError is raised if
|
18
|
+
# no value matching the specified name is available.
|
19
|
+
def method_missing(name, *args)
|
20
|
+
value = @values[name]
|
21
|
+
if value.nil? && !@values.has_key?(name)
|
22
|
+
address = "<#{self.class.name}:0x00#{(self.object_id << 1).to_s(16)}>"
|
23
|
+
raise NoMethodError.new("undefined method `#{name}' for ##{address}")
|
24
|
+
end
|
25
|
+
value
|
26
|
+
end
|
27
|
+
|
28
|
+
# Define a required string config var.
|
29
|
+
def required_string(name)
|
30
|
+
required_custom(name) do |env|
|
31
|
+
if value = env[name.to_s.upcase]
|
32
|
+
{name => value}
|
33
|
+
else
|
34
|
+
raise ConfigError.new("A value must be provided for #{name.to_s.upcase}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Define a required integer config var.
|
40
|
+
def required_int(name)
|
41
|
+
required_custom(name) do |env|
|
42
|
+
if value = env[name.to_s.upcase]
|
43
|
+
{name => parse_int(name, value)}
|
44
|
+
else
|
45
|
+
raise ConfigError.new("A value must be provided for #{name.to_s.upcase}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Define a required boolean config var.
|
51
|
+
def required_bool(name)
|
52
|
+
required_custom(name) do |env|
|
53
|
+
if value = env[name.to_s.upcase]
|
54
|
+
{name => parse_bool(name, value)}
|
55
|
+
else
|
56
|
+
raise ConfigError.new("A value must be provided for #{name.to_s.upcase}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Define a required custom config var. The block must take the
|
62
|
+
# environment as a parameter, load and process values from the it, and
|
63
|
+
# return a hash that will be merged into the collection of all config
|
64
|
+
# vars. If a required value is not found in the environment the block
|
65
|
+
# must raise a ConfigVar::ConfigError exception.
|
66
|
+
def required_custom(name, &blk)
|
67
|
+
define_config(name, &blk)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Define a required string config var with a default value.
|
71
|
+
def optional_string(name, default)
|
72
|
+
optional_custom(name) do |env|
|
73
|
+
if value = env[name.to_s.upcase]
|
74
|
+
{name => value}
|
75
|
+
else
|
76
|
+
{name => default}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Define a required integer config var with a default value.
|
82
|
+
def optional_int(name, default)
|
83
|
+
optional_custom(name) do |env|
|
84
|
+
if value = env[name.to_s.upcase]
|
85
|
+
{name => parse_int(name, value)}
|
86
|
+
else
|
87
|
+
{name => default}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Define a required boolean config var with a default value.
|
93
|
+
def optional_bool(name, default)
|
94
|
+
optional_custom(name) do |env|
|
95
|
+
if value = env[name.to_s.upcase]
|
96
|
+
{name => parse_bool(name, value)}
|
97
|
+
else
|
98
|
+
{name => default}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Define a required custom config var. The block must take the
|
104
|
+
# environment as a parameter, load and process values from the it, and
|
105
|
+
# return a hash that will be merged into the collection of all config
|
106
|
+
# vars.
|
107
|
+
def optional_custom(name, &blk)
|
108
|
+
define_config(name, &blk)
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# Convert a string to an integer. An ArgumentError is raised if the
|
114
|
+
# string is not a valid integer.
|
115
|
+
def parse_int(name, value)
|
116
|
+
Integer(value)
|
117
|
+
rescue ArgumentError
|
118
|
+
raise ArgumentError.new("#{value} is not a valid boolean for #{name.to_s.upcase}")
|
119
|
+
end
|
120
|
+
|
121
|
+
# Convert a string to boolean. An ArgumentError is raised if the string
|
122
|
+
# is not a valid boolean.
|
123
|
+
def parse_bool(name, value)
|
124
|
+
if ['1', 'true'].include?(value.downcase)
|
125
|
+
true
|
126
|
+
elsif ['0', 'false'].include?(value.downcase)
|
127
|
+
false
|
128
|
+
else
|
129
|
+
raise ArgumentError.new("#{value} is not a valid boolean for #{name.to_s.upcase}")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Define a handler for a configuration value.
|
134
|
+
def define_config(name, &blk)
|
135
|
+
if @definitions.has_key?(name)
|
136
|
+
raise ConfigError.new("#{name.to_s.upcase} is already registered")
|
137
|
+
end
|
138
|
+
@definitions[name] = blk
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ConfigVarTest < Minitest::Test
|
4
|
+
include EnvironmentHelper
|
5
|
+
|
6
|
+
# ConfigVar.define takes a block that defines required and optional
|
7
|
+
# configuration variables, loads values for those variables from the
|
8
|
+
# environment, and returns a collection that can be used to retrieve loaded
|
9
|
+
# values.
|
10
|
+
def test_define
|
11
|
+
set_env('DATABASE_URL', 'postgres:///example')
|
12
|
+
set_env('PORT', '8080')
|
13
|
+
set_env('ENABLED', '1')
|
14
|
+
config = ConfigVar.define do
|
15
|
+
required_string :database_url
|
16
|
+
required_int :port
|
17
|
+
required_bool :enabled
|
18
|
+
optional_string :name, 'Bob'
|
19
|
+
optional_int :age, 42
|
20
|
+
optional_bool :friendly, true
|
21
|
+
end
|
22
|
+
assert_equal('postgres:///example', config.database_url)
|
23
|
+
assert_equal(8080, config.port)
|
24
|
+
assert_equal(true, config.enabled)
|
25
|
+
assert_equal('Bob', config.name)
|
26
|
+
assert_equal(42, config.age)
|
27
|
+
assert_equal(true, config.friendly)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
class ContextTest < Minitest::Test
|
2
|
+
# Context.<name> raises a NoMethodError if an unknown configuration value is
|
3
|
+
# requested.
|
4
|
+
def test_index_unknown_value
|
5
|
+
context = ConfigVar::Context.new
|
6
|
+
error = assert_raises NoMethodError do
|
7
|
+
context.unknown
|
8
|
+
end
|
9
|
+
assert_match(
|
10
|
+
/undefined method `unknown' for #<ConfigVar::Context:0x[0-9a-f]*>/,
|
11
|
+
error.message)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Context.reload loads required string values.
|
15
|
+
def test_reload_required_string
|
16
|
+
context = ConfigVar::Context.new
|
17
|
+
context.required_string :database_url
|
18
|
+
context.reload('DATABASE_URL' => 'postgres:///test')
|
19
|
+
assert_equal('postgres:///test', context.database_url)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Context.reload raises a ConfigError if no value is provided for a required
|
23
|
+
# string value.
|
24
|
+
def test_reload_required_string_without_value
|
25
|
+
context = ConfigVar::Context.new
|
26
|
+
context.required_string :database_url
|
27
|
+
error = assert_raises ConfigVar::ConfigError do
|
28
|
+
context.reload({})
|
29
|
+
end
|
30
|
+
assert_equal('A value must be provided for DATABASE_URL', error.message)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Context.reload loads required integer values.
|
34
|
+
def test_reload_required_int
|
35
|
+
context = ConfigVar::Context.new
|
36
|
+
context.required_int :port
|
37
|
+
context.reload('PORT' => '8080')
|
38
|
+
assert_equal(8080, context.port)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Context.reload raises an ArgumentError if a non-int value is provided for
|
42
|
+
# a required integer value.
|
43
|
+
def test_reload_required_int_with_malformed_value
|
44
|
+
context = ConfigVar::Context.new
|
45
|
+
context.required_int :port
|
46
|
+
error = assert_raises ArgumentError do
|
47
|
+
context.reload('PORT' => 'eight zero 8 zero')
|
48
|
+
end
|
49
|
+
assert_equal('eight zero 8 zero is not a valid boolean for PORT',
|
50
|
+
error.message)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Context.reload raises a ConfigError if no value is provided for a required
|
54
|
+
# integer value.
|
55
|
+
def test_reload_required_int_without_value
|
56
|
+
context = ConfigVar::Context.new
|
57
|
+
context.required_int :port
|
58
|
+
error = assert_raises ConfigVar::ConfigError do
|
59
|
+
context.reload({})
|
60
|
+
end
|
61
|
+
assert_equal('A value must be provided for PORT', error.message)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Context.reload loads required boolean values.
|
65
|
+
def test_reload_required_bool
|
66
|
+
context = ConfigVar::Context.new
|
67
|
+
context.required_bool :value_1
|
68
|
+
context.required_bool :value_true
|
69
|
+
context.required_bool :value_0
|
70
|
+
context.required_bool :value_false
|
71
|
+
context.reload('VALUE_1' => '1', 'VALUE_TRUE' => 'True',
|
72
|
+
'VALUE_0' => '0', 'VALUE_FALSE' => 'False')
|
73
|
+
assert_equal(true, context.value_1)
|
74
|
+
assert_equal(true, context.value_true)
|
75
|
+
assert_equal(false, context.value_0)
|
76
|
+
assert_equal(false, context.value_false)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Context.reload raises an ArgumentError if a non-bool value is provided for
|
80
|
+
# a required boolean value. Only case insensitive versions of '1', 'true',
|
81
|
+
# '0' and 'false' are permitted.
|
82
|
+
def test_reload_required_bool_with_malformed_value
|
83
|
+
context = ConfigVar::Context.new
|
84
|
+
context.required_bool :value
|
85
|
+
error = assert_raises ArgumentError do
|
86
|
+
context.reload('VALUE' => 'malformed')
|
87
|
+
end
|
88
|
+
assert_equal('malformed is not a valid boolean for VALUE', error.message)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Context.reload raises a ConfigError if no value is provided for a required
|
92
|
+
# boolean value.
|
93
|
+
def test_reload_required_bool_without_value
|
94
|
+
context = ConfigVar::Context.new
|
95
|
+
context.required_bool :value
|
96
|
+
error = assert_raises ConfigVar::ConfigError do
|
97
|
+
context.reload({})
|
98
|
+
end
|
99
|
+
assert_equal('A value must be provided for VALUE', error.message)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Context.reload makes all values returned from the custom block used to
|
103
|
+
# process a required value available via the collection interface.
|
104
|
+
def test_reload_required_custom
|
105
|
+
context = ConfigVar::Context.new
|
106
|
+
context.required_custom :name do |env|
|
107
|
+
{greeting: 'Hello', name: 'Bob', age: 42}
|
108
|
+
end
|
109
|
+
context.reload({})
|
110
|
+
assert_equal('Hello', context.greeting)
|
111
|
+
assert_equal('Bob', context.name)
|
112
|
+
assert_equal(42, context.age)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Context.reload loads optional string values.
|
116
|
+
def test_reload_optional_string
|
117
|
+
context = ConfigVar::Context.new
|
118
|
+
context.optional_string :database_url, 'postgres:///default'
|
119
|
+
context.reload('DATABASE_URL' => 'postgres:///test')
|
120
|
+
assert_equal('postgres:///test', context.database_url)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Context.reload loads optional string values.
|
124
|
+
def test_reload_optional_string_without_value
|
125
|
+
context = ConfigVar::Context.new
|
126
|
+
context.optional_string :database_url, 'postgres:///default'
|
127
|
+
context.reload({})
|
128
|
+
assert_equal('postgres:///default', context.database_url)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Context.reload loads optional integer values.
|
132
|
+
def test_reload_optional_int
|
133
|
+
context = ConfigVar::Context.new
|
134
|
+
context.optional_int :port, 8080
|
135
|
+
context.reload('PORT' => '8081')
|
136
|
+
assert_equal(8081, context.port)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Context.reload loads optional integer values.
|
140
|
+
def test_reload_optional_int_without_value
|
141
|
+
context = ConfigVar::Context.new
|
142
|
+
context.optional_int :port, 8080
|
143
|
+
context.reload({})
|
144
|
+
assert_equal(8080, context.port)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Context.reload loads optional boolean values.
|
148
|
+
def test_reload_optional_bool
|
149
|
+
context = ConfigVar::Context.new
|
150
|
+
context.optional_bool :value_1, false
|
151
|
+
context.optional_bool :value_true, false
|
152
|
+
context.optional_bool :value_0, true
|
153
|
+
context.optional_bool :value_false, true
|
154
|
+
context.reload('VALUE_1' => '1', 'VALUE_TRUE' => 'True',
|
155
|
+
'VALUE_0' => '0', 'VALUE_FALSE' => 'False')
|
156
|
+
assert_equal(true, context.value_1)
|
157
|
+
assert_equal(true, context.value_true)
|
158
|
+
assert_equal(false, context.value_0)
|
159
|
+
assert_equal(false, context.value_false)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Context.reload loads optional boolean values.
|
163
|
+
def test_reload_optional_bool_without_value
|
164
|
+
context = ConfigVar::Context.new
|
165
|
+
context.optional_bool :value_1, false
|
166
|
+
context.optional_bool :value_true, false
|
167
|
+
context.optional_bool :value_0, true
|
168
|
+
context.optional_bool :value_false, true
|
169
|
+
context.reload({})
|
170
|
+
assert_equal(false, context.value_1)
|
171
|
+
assert_equal(false, context.value_true)
|
172
|
+
assert_equal(true, context.value_0)
|
173
|
+
assert_equal(true, context.value_false)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Context.reload correctly handles a nil default value for an optional
|
177
|
+
# variable.
|
178
|
+
def test_reload_optional_value_with_nil_default
|
179
|
+
context = ConfigVar::Context.new
|
180
|
+
context.optional_int :port, nil
|
181
|
+
context.reload({})
|
182
|
+
assert_nil(context.port)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Context.reload makes all values returned from the custom block used to
|
186
|
+
# process an optional value available via the collection interface.
|
187
|
+
def test_reload_optional_custom
|
188
|
+
context = ConfigVar::Context.new
|
189
|
+
context.optional_custom :name do |env|
|
190
|
+
{greeting: 'Hello', name: 'Bob', age: 42}
|
191
|
+
end
|
192
|
+
context.reload({})
|
193
|
+
assert_equal('Hello', context.greeting)
|
194
|
+
assert_equal('Bob', context.name)
|
195
|
+
assert_equal(42, context.age)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Context.reload reinitialized loaded values from the provided environment.
|
199
|
+
def test_reload
|
200
|
+
context = ConfigVar::Context.new
|
201
|
+
context.optional_int :port, 8080
|
202
|
+
context.reload('PORT' => '8081')
|
203
|
+
assert_equal(8081, context.port)
|
204
|
+
context.reload('PORT' => '9000')
|
205
|
+
assert_equal(9000, context.port)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Context.required_* and Context.optional_* methods raise a ConfigError if
|
209
|
+
# the same name is defined twice.
|
210
|
+
def test_register_duplicate_config
|
211
|
+
context = ConfigVar::Context.new
|
212
|
+
context.required_int :port
|
213
|
+
error = assert_raises ConfigVar::ConfigError do
|
214
|
+
context.optional_int :port, 8080
|
215
|
+
end
|
216
|
+
assert_equal('PORT is already registered', error.message)
|
217
|
+
end
|
218
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module EnvironmentHelper
|
2
|
+
# Override an environment variable in the current test.
|
3
|
+
def set_env(key, value)
|
4
|
+
overrides[key] = ENV[key] unless overrides.has_key?(key)
|
5
|
+
ENV[key] = value
|
6
|
+
end
|
7
|
+
|
8
|
+
# Restore the environment back to the state before tests ran.
|
9
|
+
def teardown
|
10
|
+
overrides.each do |key, value|
|
11
|
+
ENV[key] = value
|
12
|
+
end
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# The overridden environment variables to restore when the test finishes.
|
19
|
+
def overrides
|
20
|
+
@overrides ||= {}
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: configvar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jkakar
|
8
|
+
- heroku
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-02-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: minitest
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: Manage configuration loaded from the environment
|
57
|
+
email:
|
58
|
+
- jkakar@kakar.ca
|
59
|
+
- opensource@heroku.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".travis.yml"
|
65
|
+
- Gemfile
|
66
|
+
- Gemfile.lock
|
67
|
+
- LICENSE.md
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- configvar.gemspec
|
71
|
+
- lib/configvar.rb
|
72
|
+
- lib/configvar/context.rb
|
73
|
+
- lib/configvar/error.rb
|
74
|
+
- lib/configvar/version.rb
|
75
|
+
- test/configvar_test.rb
|
76
|
+
- test/context_test.rb
|
77
|
+
- test/helper.rb
|
78
|
+
homepage: https://github.com/heroku/configvar
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.2.2
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Manage configuration loaded from the environment
|
102
|
+
test_files: []
|