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.
@@ -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
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,19 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ configvar (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.5.1)
10
+ rake (10.4.2)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ bundler
17
+ configvar!
18
+ minitest
19
+ rake
@@ -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.
@@ -0,0 +1,131 @@
1
+ [![Build Status](https://magnum.travis-ci.com/heroku/configvar.svg?token=uNxDxTYyzRaxPpJGQ5yq)](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
+ ```
@@ -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
@@ -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
@@ -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,4 @@
1
+ module ConfigVar
2
+ class ConfigError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module ConfigVar
2
+ VERSION = '0.0.1'
3
+ 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
@@ -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: []