env_setting 1.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 96310d9ae84d2c930ac48bf3a6fd90b6a954d413
4
+ data.tar.gz: 6935b91753a1c70aef433eb9d7823439009c14a2
5
+ SHA512:
6
+ metadata.gz: 6b4a876883750972e645f49d54c1f02427e6114e4e9ae3312e579a74877c053495b5ff6573badc4b132e1c17e7427de1c3fc5c944f044c51f9a9db52f20c203d
7
+ data.tar.gz: bfce18429b78b30f255baa2791228591e6b220cb49f8f5b27559e67b4c4c29092a08690cc1642b7dc5a04b0452714585fdca0ac7a399e56027aa82424ac84778
@@ -0,0 +1,2 @@
1
+ L�)��\����vJ���)C�i}�6�������{p����"����ST%����E��GX�M�"���S�#� �!���0Ϸ�Q�k����=m{�'�9J�R_��|+%��s�g��f�aSOϽk��T,G����l-�ն��Z"$XA�Ҡ*eI�Cv������K��f�o媝�4��;��MڭpM�7�j=o�<���m�b#(O����+�W��V�
2
+ Qr[�RK��F ������5 �M ��6��ؼ�
@@ -0,0 +1 @@
1
+ y��^$?�#�~ml��v��Ni�U�YN�YbD>Q{�����火'��}J�v���#{��a9�E�c��,�����tQ!���f���+x7�JN�%���o^l��8�׌�����V�G#,����"sb��\����贸��,�?)�&4�y�]ɓ^��O�aTuv,^�z֎s�M?������N��+H��t�.r]-�{up�������5��ՋZ����1��Oų�:7���[+g��z��z�%
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .ruby-version
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ vendor/bundle
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - 2.2.0
6
+ - 2.3.0
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec name: 'env_setting'
4
+
5
+ # group :metrics do
6
+ # gem 'coveralls', require: false
7
+ # end
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2016 ORM Technologies
2
+ Copyright (c) 2013 Jonathan Camenisch
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,333 @@
1
+ # env_setting
2
+
3
+ Inspired by [ENV!](https://rubygems.org/gems/env_bang) and [David Copeland's
4
+ article on UNIX
5
+ Environment](http://naildrivin5.com/blog/2016/06/10/dont-use-ENV-directly.html),
6
+ `env_setting` is a slight rewrite of `env_bang` to provide OOP style access to
7
+ your ENV.
8
+
9
+ [![Build Status](https://travis-ci.org/ormtech/env_setting.svg?branch=master)](https://travis-ci.org/ormtech/env_setting)
10
+ [![Coverage Status](https://coveralls.io/repos/github/ormtech/env_setting/badge.svg?branch=master)](https://coveralls.io/github/ormtech/env_setting?branch=master)
11
+
12
+ `env_setting` is very similar to `ENV!`, it sets out to accomplish the same
13
+ purpose:
14
+
15
+ > - Provide a central place to specify all your app’s environment variables.
16
+ > - Fail loudly and helpfully if any environment variables are missing.
17
+ > - Prevent an application from starting up with missing environment variables.
18
+ > (This is especially helpful in environments like Heroku, as your app will
19
+ > continue running the old code until the server is configured for a new
20
+ > revision.)
21
+
22
+ But with one extra requirement:
23
+
24
+ - Provide access to environment variables in keeping with OOP doctrine.
25
+
26
+ To accomplish that goal, Environment variables are just methods on the
27
+ `EnvSetting` class after they are configured:
28
+
29
+ ```ruby
30
+ ENV["SOME_SETTING"] = "something"
31
+
32
+ EnvSetting.use "SOME_SETTING"
33
+
34
+ ...
35
+
36
+ EnvSetting.some_setting
37
+ # => "something"
38
+ EnvSetting.some_setting?
39
+ # => true
40
+ ```
41
+
42
+ ## Installation
43
+
44
+ Add this line to your application’s Gemfile:
45
+
46
+ ```ruby
47
+ gem 'env_setting'
48
+ ```
49
+
50
+ Or for Rails apps, use `env_setting-rails` instead for more convenience:
51
+
52
+ ```ruby
53
+ gem 'env_setting-rails'
54
+ ```
55
+
56
+ And then execute:
57
+
58
+ ```sh
59
+ $ bundle
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ ### Basic Configuration
65
+
66
+ Configuration style is _exactly_ the same for `env_bang` and `env_setting`, only
67
+ that there's no "ENV!" method... just the normal class: `EnvSetting` that is
68
+ called and configured.
69
+
70
+ First, configure your environment variables somewhere in your app’s startup
71
+ process. If you use the `env_setting-rails` gem, place this in `config/env.rb`
72
+ to load before application configuration.
73
+
74
+ Example configuration:
75
+
76
+ ```ruby
77
+ EnvSetting.config do
78
+ use :APP_HOST
79
+ use :RAILS_SECRET_TOKEN
80
+ use :STRIPE_SECRET_KEY
81
+ use :STRIPE_PUBLISHABLE_KEY
82
+ # ... etc.
83
+ end
84
+ ```
85
+
86
+ Once a variable is specified with the `use` method, access it with
87
+
88
+ ```ruby
89
+ EnvSetting.my_var
90
+ ```
91
+
92
+ Or you can still use the Hash syntax if you prefer it:
93
+
94
+ ```ruby
95
+ EnvSetting["MY_VAR"]
96
+ ```
97
+
98
+ This will function just like accessing `ENV` directly, except that it will
99
+ require the variable to have been specified, and, if no default value is
100
+ specified, it will raise a `KeyError` with an explanation of what needs to be
101
+ configured. In the event you reference a variable that you haven't specified,
102
+ it will produce a `NoMethodError` (if using the method syntax) or a `KeyError`
103
+ if using the Hash syntax.
104
+
105
+ ### Adding a default value
106
+
107
+ For some variables, you’ll want to include a default value in your code, and
108
+ allow each environment to omit the variable for default behaviors. You can
109
+ accomplish this with the `:default` option:
110
+
111
+ ```ruby
112
+ EnvSetting.config do
113
+ # ...
114
+ use :MAIL_DELIVERY_METHOD, default: 'smtp'
115
+ # ...
116
+ end
117
+ ```
118
+
119
+ ### Adding a description
120
+
121
+ When a new team member installs or deploys your project, they may run into a
122
+ missing environment variable error. Save them time by including documentation
123
+ along with the error that is raised. To accomplish this, provide a description
124
+ (of any length) to the `use` method:
125
+
126
+ ```ruby
127
+ EnvSetting.config do
128
+ use 'RAILS_SECRET_KEY_BASE',
129
+ 'Generate a fresh one with `SecureRandom.urlsafe_base64(64)`; see http://guides.rubyonrails.org/security.html#session-storage'
130
+ end
131
+ ```
132
+
133
+ Now if someone installs or deploys the app without setting the
134
+ `RAILS_SECRET_KEY_BASE` variable, they will see these instructions immediately
135
+ upon running the app.
136
+
137
+ ### Automatic type conversion
138
+
139
+ `env_setting` can convert your environment variables for you, keeping that
140
+ tedium out of your application code. To specify a type, use the `:class` option:
141
+
142
+ ```ruby
143
+ EnvSetting.config do
144
+ use :COPYRIGHT_YEAR, class: Integer
145
+ use :MEMCACHED_SERVERS, class: Array
146
+ use :MAIL_DELIVERY_METHOD, class: Symbol, default: :smtp
147
+ use :DEFAULT_FRACTION, class: Float
148
+ use :ENABLE_SOUNDTRACK, class: :boolean
149
+ use :PUPPETMASTERS, class: Hash
150
+ end
151
+ ```
152
+
153
+ **Note** that arrays will be derived by splitting the value on commas (','). To
154
+ get arrays of a specific type of value, use the `:of` option:
155
+
156
+ ```ruby
157
+ EnvSetting.config do
158
+ use :YEARS_OF_INTEREST, class: Array, of: Integer
159
+ end
160
+ ```
161
+
162
+ Hashes are split on commas (',') and key:value pairs are delimited by colon
163
+ (':'). To get hashes of a specific type of value, use the `:of` option, and to
164
+ use a different type for keys (default is `Symbol`), use the `:keys` option:
165
+
166
+ ```ruby
167
+ EnvSetting.config do
168
+ use :BIRTHDAYS, class: Hash, of: Integer, keys: String
169
+ end
170
+ ```
171
+
172
+ #### Default type conversion behavior
173
+
174
+ If you don’t specify a `:class` option for a variable, `env_setting` defaults to
175
+ a special type conversion called `:StringUnlessFalsey`. This conversion returns
176
+ a string, unless the value is a "falsey" string `['false', 'no', 'off', '0',
177
+ 'disable', 'disabled']`. To turn off this magic for one variable, pass in
178
+ `class: String`. To disable it globally, set
179
+
180
+ ```ruby
181
+ EnvSetting.config do
182
+ default_class String
183
+ end
184
+ ```
185
+
186
+ Or if you just dislike what is considered "falsey", configure your own regex
187
+ pattern of what strings are "falsey":
188
+
189
+ ```ruby
190
+ EnvSetting.config do
191
+ default_falsey_regex(/0|fubar|false|n/i)
192
+ end
193
+ ```
194
+
195
+ #### Custom type conversion
196
+
197
+ Suppose your app needs a special type conversion that doesn’t come with
198
+ `env_setting`. You can implement the conversion yourself with the `add_class`
199
+ method in the `EnvSetting.config` block. For example, to convert one of your
200
+ environment variables to type `Set`, you could write the following
201
+ configuration:
202
+
203
+ ```sh
204
+ # In your environment:
205
+ export NUMBER_SET=1,3,5,7,9
206
+ ```
207
+
208
+ ```ruby
209
+ # In your env.rb configuration file:
210
+ require 'set'
211
+
212
+ EnvSetting.config do
213
+ add_class Set do |value, options|
214
+ Set.new self.Array(value, options || {})
215
+ end
216
+
217
+ use :NUMBER_SET, class: Set, of: Integer
218
+ end
219
+ ```
220
+
221
+ ```ruby
222
+ # Somewhere in your application:
223
+ EnvSetting.number_set
224
+ #=> #<Set: {1, 3, 5, 7, 9}>
225
+ ```
226
+
227
+ ## What if I don't like `EnvSetting` for my settings class name?
228
+
229
+ We don't blame you, the easiest way to "rename" the settings class from
230
+ `EnvSetting` is to define a new class that inherits from `EnvSetting` like so:
231
+
232
+ ```ruby
233
+ class Settings < EnvSetting
234
+ end
235
+
236
+ Settings.config do
237
+ ...
238
+ end
239
+
240
+ # elsewhere in your app
241
+ Settings.my_special_env_var
242
+ ```
243
+
244
+ ## Implementation Notes
245
+
246
+ 1. Any method that can be run within an `EnvSetting.config` block can also be
247
+ run as a method directly on `EnvSetting`. For instance, instead of
248
+
249
+ ```ruby
250
+ EnvSetting.config do
251
+ add_class Set do
252
+ ...
253
+ end
254
+
255
+ use :NUMBER_SET, class: Set
256
+ end
257
+ ```
258
+
259
+ It would also work to run
260
+
261
+ ```ruby
262
+ EnvSetting.add_class Set do
263
+ ...
264
+ end
265
+
266
+ EnvSetting.use :NUMBER_SET, class: Set
267
+ ```
268
+
269
+ While the `config` block is designed to provide a cleaner configuration
270
+ file, calling the methods directly can occasionally be handy, such as when
271
+ trying things out in an IRB/Pry session.
272
+
273
+ 2. `EnvSetting` is a wrapper for global state, and while it appears that
274
+ everything is stored/modified on the class level, it is actually defining and
275
+ delegating everything to a Singleton. Effectively that means that all the
276
+ ENV variable access methods are actually defined on an instance Singleton and
277
+ **not** on the `EnvSetting` class itself. For example:
278
+
279
+ ```ruby
280
+ EnvSetting.use "BUNDLE_BIN_PATH"
281
+
282
+ # The class appears to respond to respond to our envrionment variable method
283
+ EnvSetting.bundle_bin_path
284
+ # => "/srv/app/shared/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/bundler-1.11.2/exe/bundle"
285
+ EnvSetting.:bundle_bin_path?
286
+ # => true
287
+
288
+ # However the Singelton is the true responder
289
+ EnvSetting.instance.bundle_bin_path
290
+ # => "/srv/app/shared/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/bundler-1.11.2/exe/bundle"
291
+ EnvSetting.instance.bundle_bin_path?
292
+ # => true
293
+
294
+ # Swapping in a different Singelton instance shows the truth.
295
+ EnvSetting.set_instance(EnvSetting.new)
296
+ EnvSetting.respond_to?(:bundle_bin_path)
297
+ # => false
298
+ EnvSetting.respond_to?(:bundle_bin_path?)
299
+ # => false
300
+
301
+ EnvSetting.instance.respond_to?(:bundle_bin_path)
302
+ # => false
303
+ EnvSetting.instance.respond_to?(:bundle_bin_path?)
304
+ # => false
305
+ ```
306
+
307
+ 3. `EnvSetting` stores the converted ENV variable values in a cache (just to
308
+ avoid having to repeat a laborious conversion). In the event that you want
309
+ all the cache to be cleared out and all the conversions applied again, use
310
+ the `clear_cache!` method on the **instance Singelton**:
311
+
312
+ ```ruby
313
+ EnvSetting.instance.clear_cache!
314
+ ```
315
+
316
+ ## Acknowledgements
317
+
318
+ Jonathan Camenisch, the author of `ENV!`, has done substantial work of which
319
+ this gem takes advantage. This gem would not be possible without that work.
320
+ This gem simply changes the style in which the work that `ENV!` does is exposed
321
+ (i.e. via methods).
322
+
323
+ ## License
324
+
325
+ This gem is licensed under the MIT License
326
+
327
+ ## Contributing
328
+
329
+ 1. Fork it
330
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
331
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
332
+ 4. Push to the branch (`git push origin my-new-feature`)
333
+ 5. Create new Pull Request
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ namespace 'dotenv' do
5
+ Bundler::GemHelper.install_tasks :name => 'env_setting'
6
+ end
7
+
8
+ desc 'Run all tests'
9
+ RSpec::Core::RakeTask.new(:spec) do |s|
10
+ s.rspec_opts = '-f d -c'
11
+ s.pattern = 'spec/**/*_spec.rb'
12
+ end
13
+ task :default => :spec
@@ -0,0 +1,22 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDjjCCAnagAwIBAgIBATANBgkqhkiG9w0BAQUFADBGMRUwEwYDVQQDDAx3aWxs
3
+ LnNwdXJnaW4xGDAWBgoJkiaJk/IsZAEZFghvcm0tdGVjaDETMBEGCgmSJomT8ixk
4
+ ARkWA2NvbTAeFw0xNjA2MjAxNTU4NDJaFw0xNzA2MjAxNTU4NDJaMEYxFTATBgNV
5
+ BAMMDHdpbGwuc3B1cmdpbjEYMBYGCgmSJomT8ixkARkWCG9ybS10ZWNoMRMwEQYK
6
+ CZImiZPyLGQBGRYDY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
7
+ vRMpVroSt8OrOY/zyh/00HzNryjthX5JabsyIlZBBkqJSBakNa4p9y3EleODdvP8
8
+ 3DPnDgAax5/nYd+UumPbcqPB7lhXHn+vw+082DVVOaL2IMg1fbqLRSpCXGvgz4za
9
+ P4QKMusXXRAo1+nLjl68pumyLfAD6dEF7bNk2diHpKppknb1ENsvs/v8/uWQBv27
10
+ AnIrHntPpKCLwSjxufgCa9IKdSy9EdwCBCwX9IOGTjUhFoRy+Fsx7pUi0NM7eaER
11
+ h0VYrXIPnembxN51iVA6LcM7wnzl6uVnSb/TJshc3zSIqZibvHSPKL1g17S2s8qa
12
+ 2AspSOGAQ7iDAkt1lRnccQIDAQABo4GGMIGDMAkGA1UdEwQCMAAwCwYDVR0PBAQD
13
+ AgSwMB0GA1UdDgQWBBTdymM1YAMQyvVpddd//6sgBWrCOTAkBgNVHREEHTAbgRl3
14
+ aWxsLnNwdXJnaW5Ab3JtLXRlY2guY29tMCQGA1UdEgQdMBuBGXdpbGwuc3B1cmdp
15
+ bkBvcm0tdGVjaC5jb20wDQYJKoZIhvcNAQEFBQADggEBAK4zjjfK53r01ZtIB+xF
16
+ GT8OR3ri+iSrcTAaC7dk4XmjNU42hGBlFZ34RjnzxBGBjBZH9w+3jwCjN8FkPfmO
17
+ f1kiI4+tCt+weUzWFqhKsIaC23TjEDrlhyZ2203HldlW4p26onVwDpIn3YOYG9Qr
18
+ c+9wUpquUpi5e4bBVsIaHoYnECMOrGIgRSleI8YWLAakTWAXRL63dtekC945+3ep
19
+ vbrWi4+bt0feapcxjBsEk2q1TW6XmEWU8HokYJOxNbqKt5XuWZq/fcGgBV+CftFN
20
+ 8o95YBJ2TniSxvMvbz2P9Q/Mh1AhMN4J0OqtcAo1One8UgJBXU8xZHj/qWMLwT9L
21
+ gtM=
22
+ -----END CERTIFICATE-----
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'env_setting/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "env_setting"
8
+ spec.version = EnvSetting::VERSION
9
+ spec.authors = ["Will Spurgin"]
10
+ spec.email = ["will.spurgin@orm-tech.com"]
11
+ spec.summary = %q{Mange your environment variables in OOP style}
12
+ spec.description = %q{Allows OOP access to ENV variables by a slight re-write of the env_bang gem.}
13
+ spec.homepage = "https://github.com/ormtech/env_setting"
14
+ spec.license = "MIT"
15
+
16
+ spec.cert_chain = ['certs/wspurgin.pem']
17
+ spec.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
18
+
19
+ spec.files = `git ls-files`.split($/)
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test)/})
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec", "~> 3.4"
26
+ spec.add_development_dependency "simplecov"
27
+ spec.add_development_dependency "coveralls"
28
+ end
@@ -0,0 +1,113 @@
1
+ require "env_setting/version"
2
+ require "env_setting/classes"
3
+ require "env_setting/formatter"
4
+
5
+ class EnvSetting
6
+ def self.config(&block)
7
+ class_eval(&block)
8
+ end
9
+
10
+ def self.use(var, *args)
11
+ var = var.to_s
12
+ description = args.first.is_a?(String) && args.shift
13
+ description ||= ""
14
+ options = args.last.is_a?(Hash) ? args.pop : {}
15
+
16
+ unless ENV.has_key?(var)
17
+ ENV[var] = options.fetch(:default) { raise_formatted_error(var, description) }.to_s
18
+ end
19
+
20
+ vars[var] = options
21
+
22
+ method_name = var.downcase
23
+
24
+ instance.define_singleton_method(method_name) do
25
+ cache[method_name] ||= self.class.get_value(var)
26
+ end
27
+
28
+ method_name_bool = "#{method_name}?"
29
+ instance.define_singleton_method(method_name_bool) do
30
+ cache[method_name_bool] ||= !!(self.send(method_name))
31
+ end
32
+ end
33
+
34
+ def self.raise_formatted_error(var, description)
35
+ raise KeyError.new Formatter.formatted_error(var, description)
36
+ end
37
+
38
+ def self.add_class(klass, &block)
39
+ Classes.send :define_singleton_method, klass.to_s, &block
40
+ end
41
+
42
+ def self.default_class(*args)
43
+ if args.any?
44
+ Classes.default_class = args.first
45
+ else
46
+ Classes.default_class
47
+ end
48
+ end
49
+
50
+ def self.default_falsey_regex(regex = nil)
51
+ if regex
52
+ Classes.default_falsey_regex = regex
53
+ else
54
+ Classes.default_falsey_regex
55
+ end
56
+ end
57
+
58
+ def self.respond_to?(method_sym)
59
+ instance.respond_to?(method_sym) || super
60
+ end
61
+
62
+ def self.method_missing(method, *args, &block)
63
+ instance.send(method, *args, &block)
64
+ end
65
+
66
+ def self.instance
67
+ @@instance ||= new
68
+ end
69
+
70
+ def self.set_instance(obj)
71
+ raise ArgumentError.new "Object must be a derivative of EnvSetting" unless obj.is_a?(EnvSetting)
72
+ @@instance = obj
73
+ end
74
+
75
+ def self.vars
76
+ @@vars ||= {}
77
+ end
78
+
79
+ def self.keys
80
+ vars.keys
81
+ end
82
+
83
+ def self.values
84
+ keys.map { |k| self[k] }
85
+ end
86
+
87
+ def self.get_value(var)
88
+ var = var.to_s
89
+ raise KeyError.new("#{var} is not configured in the ENV") unless vars.has_key?(var)
90
+
91
+ Classes.cast ENV[var], vars[var]
92
+ end
93
+
94
+ def self.[](var)
95
+ self.get_value(var)
96
+ end
97
+
98
+ def cache
99
+ @cache ||= {}
100
+ end
101
+
102
+ def clear_cache!
103
+ @cache = nil
104
+ end
105
+
106
+ def respond_to?(method_sym)
107
+ ENV.respond_to?(method_sym) || super
108
+ end
109
+
110
+ def method_missing(method, *args, &block)
111
+ ENV.send(method, *args, &block)
112
+ end
113
+ end
@@ -0,0 +1,60 @@
1
+ class EnvSetting
2
+ module Classes
3
+ class << self
4
+ attr_writer :default_class
5
+ end
6
+
7
+ def self.default_class
8
+ @@default_class ||= :StringUnlessFalsey
9
+ end
10
+
11
+ def self.default_falsey_regex
12
+ @@default_falsey_regex ||= /^(|0|disabled?|false|no|off)$/i
13
+ end
14
+
15
+ def self.default_falsey_regex=(regex)
16
+ @@default_falsey_regex = regex
17
+ end
18
+
19
+ def self.cast(value, options = {})
20
+ public_send(:"#{options.fetch(:class, default_class)}", value, options)
21
+ end
22
+
23
+ def self.boolean(value, options)
24
+ !(value =~ default_falsey_regex)
25
+ end
26
+
27
+ def self.Array(value, options)
28
+ item_options = options.merge(class: options.fetch(:of, default_class))
29
+ value.split(',').map { |v| cast(v.strip, item_options) }
30
+ end
31
+
32
+ def self.Hash(value, options)
33
+ key_options = options.merge(class: options.fetch(:keys, Symbol))
34
+ value_options = options.merge(class: options.fetch(:of, default_class))
35
+ {}.tap do |h|
36
+ value.split(',').each do |pair|
37
+ key, value = pair.split(':')
38
+ h[cast(key.strip, key_options)] = cast(value.strip, value_options)
39
+ end
40
+ end
41
+ end
42
+
43
+ def self.Symbol(value, options)
44
+ value.to_sym
45
+ end
46
+
47
+ def self.StringUnlessFalsey(value, options)
48
+ boolean(value, options) && value
49
+ end
50
+
51
+ def self.respond_to?(method_sym)
52
+ Kernel.respond_to?(method_sym) || super
53
+ end
54
+
55
+ # Delegate methods like Integer(), Float(), String(), etc. to the Kernel module
56
+ def self.method_missing(klass, value, options = {}, &block)
57
+ Kernel.send(klass, value)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,20 @@
1
+ class EnvSetting
2
+ module Formatter
3
+ def self.formatted_error(var, description)
4
+ indent 4, <<-EOS
5
+
6
+ Missing required environment variable: #{var}#{ description and "\n" <<
7
+ unindent(description) }
8
+ EOS
9
+ end
10
+
11
+ def self.unindent(string)
12
+ width = string.scan(/^ */).map(&:length).min
13
+ string.gsub(/^ {#{width}}/, '')
14
+ end
15
+
16
+ def self.indent(width, string)
17
+ string.gsub "\n", "\n#{' ' * width}"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ class EnvSetting
2
+ VERSION = "1.0.0".freeze
3
+ end
@@ -0,0 +1,300 @@
1
+ require_relative 'spec_helper'
2
+
3
+ RSpec.describe EnvSetting do
4
+
5
+ it "Raises exception if unconfigured ENV var requested" do
6
+ ENV['UNCONFIGURED'] = 'unconfigured'
7
+ expect { described_class.unconfigured }.to raise_error NoMethodError
8
+ expect { described_class['UNCONFIGURED'] }.to raise_error KeyError
9
+ end
10
+
11
+ it "Raises exception if configured ENV var is not present" do
12
+ ENV.delete('NOT_PRESENT')
13
+
14
+ expect {
15
+ described_class.config do
16
+ use 'NOT_PRESENT'
17
+ end
18
+ }.to raise_error KeyError
19
+ end
20
+
21
+ it "Should define two methods for each configured ENV var" do
22
+ ENV['CUSTOM_VAR'] = 'foo'
23
+
24
+ described_class.config do
25
+ use 'CUSTOM_VAR'
26
+ end
27
+
28
+ expect(described_class).to respond_to(:custom_var)
29
+ expect(described_class.custom_var).to eq 'foo'
30
+
31
+ expect(described_class).to respond_to(:custom_var?)
32
+ expect(described_class.custom_var?).to eq true
33
+ end
34
+
35
+ it "Uses provided default value if ENV var not already present" do
36
+ ENV.delete('WASNT_PRESENT')
37
+
38
+ described_class.config do
39
+ use 'WASNT_PRESENT', default: 'a default value'
40
+ end
41
+ expect(described_class.wasnt_present).to eq 'a default value'
42
+ end
43
+
44
+ it "Returns actual value from ENV if present" do
45
+ ENV['PRESENT'] = 'present in environment'
46
+
47
+ described_class.config do
48
+ use 'PRESENT', default: "You won't need this."
49
+ end
50
+ expect(described_class.present).to eq 'present in environment'
51
+ end
52
+
53
+ describe "Type casting" do
54
+ let(:truthy_values) { %w[true on yes yo yup anything] }
55
+ let(:falsey_values) { %w[false no off disable disabled 0] << '' }
56
+ let(:integers) { %w[0 1 10 -42 -55] }
57
+ let(:floats) { %w[0.1 1.3 10 -42.3 -55] }
58
+
59
+ it "Casts Integers" do
60
+ integer = integers.sample
61
+ ENV['INTEGER'] = integer
62
+ described_class.use 'INTEGER', class: Integer
63
+
64
+ expect(described_class.integer).to eq integer.to_i
65
+ end
66
+
67
+ it "Casts Symbols" do
68
+ ENV['SYMBOL'] = 'symbol'
69
+ described_class.use 'SYMBOL', class: Symbol
70
+
71
+ expect(described_class.symbol).to eq :symbol
72
+ end
73
+
74
+ it "Casts Floats" do
75
+ float = floats.sample
76
+ ENV['FLOAT'] = float
77
+ described_class.use 'FLOAT', class: Float
78
+
79
+ expect(described_class.float).to eq float.to_f
80
+ expect(described_class.float).to be_a Float
81
+ end
82
+
83
+ it "Casts Arrays" do
84
+ ENV['ARRAY'] = 'one,two , three, four'
85
+ described_class.use 'ARRAY', class: Array
86
+
87
+ expect(described_class.array).to match_array(%w[one two three four])
88
+ end
89
+
90
+ it "Casts Arrays of Integers" do
91
+ ENV['INTEGERS'] = integers.join(',')
92
+ described_class.use 'INTEGERS', class: Array, of: Integer
93
+
94
+ expect(described_class.integers).to match_array(integers.map(&:to_i))
95
+ end
96
+
97
+ it "Casts Arrays of Floats" do
98
+ ENV['FLOATS'] = floats.join(',')
99
+ described_class.use 'FLOATS', class: Array, of: Float
100
+
101
+ expect(described_class.floats).to match_array(floats.map(&:to_f))
102
+ end
103
+
104
+ it "regression: Casting Array always returns Array" do
105
+ ENV['ARRAY'] = 'one,two , three, four'
106
+ described_class.use 'ARRAY', class: Array
107
+
108
+ 2.times do
109
+ expect(described_class.array).to match_array(%w[one two three four])
110
+ end
111
+ end
112
+
113
+ it "Casts Hashes" do
114
+ ENV['HASH_VAR'] = 'one: two, three: four'
115
+ described_class.use 'HASH_VAR', class: Hash
116
+
117
+ expect(described_class.hash_var).to eq({one: 'two', three: 'four'})
118
+ end
119
+
120
+ it 'Casts Hashes of Integers' do
121
+ ENV['INT_HASH'] = 'one: 111, two: 222'
122
+ described_class.use 'INT_HASH', class: Hash, of: Integer
123
+
124
+ expect(described_class.int_hash).to eq({one: 111, two: 222})
125
+ end
126
+
127
+ it 'Casts Hashes with String keys' do
128
+ ENV['STRKEY_HASH'] = 'one: two, three: four'
129
+ described_class.use 'STRKEY_HASH', class: Hash, keys: String
130
+
131
+ expect(described_class.strkey_hash).to eq({'one' => 'two', 'three' => 'four'})
132
+ end
133
+
134
+ it "Casts true" do
135
+ ENV['TRUE'] = truthy_values.sample
136
+ described_class.use 'TRUE', class: :boolean
137
+
138
+ expect(described_class.true).to eq true
139
+ expect(described_class.true?).to eq true
140
+ end
141
+
142
+ it "Casts false" do
143
+ ENV['FALSE'] = falsey_values.sample
144
+ described_class.use 'FALSE', class: :boolean
145
+
146
+ expect(described_class.false).to eq false
147
+ expect(described_class.false?).to eq false
148
+ end
149
+
150
+ it "converts falsey or empty string to false by default" do
151
+ ENV['FALSE'] = falsey_values.sample
152
+ described_class.use 'FALSE'
153
+
154
+ expect(described_class.false).to eq false
155
+ end
156
+
157
+ it "leaves falsey string as string if specified" do
158
+ ENV['FALSE'] = falsey_values.sample
159
+ described_class.use 'FALSE', class: String
160
+
161
+ expect(described_class.false).to be_a String
162
+ end
163
+
164
+ it "allows default class to be overridden" do
165
+ expect(described_class.default_class).to eq :StringUnlessFalsey
166
+ orig = described_class.default_class
167
+
168
+ described_class.config { default_class String }
169
+ ENV['FALSE'] = falsey_values.sample
170
+ described_class.use 'FALSE'
171
+
172
+ expect(described_class.false).to be_a String
173
+
174
+ described_class.default_class orig
175
+ end
176
+
177
+ it "allows default falsey regex to be overridden" do
178
+ expect(described_class.default_falsey_regex).to eq(/^(|0|disabled?|false|no|off)$/i)
179
+ orig = described_class.default_falsey_regex
180
+
181
+ described_class.config { default_falsey_regex(/fubar/i) }
182
+
183
+ ENV['FALSEY'] = 'fubar'
184
+ described_class.use 'FALSEY'
185
+
186
+ expect(described_class.falsey).to be_a FalseClass
187
+
188
+ # Reset the default for rest of tests.
189
+ described_class.default_falsey_regex orig
190
+ end
191
+
192
+ it "allows addition of custom types" do
193
+ require 'set'
194
+
195
+ ENV['NUMBER_SET'] = '1,3,5,7,9'
196
+ described_class.config do
197
+ add_class Set do |value, options|
198
+ Set.new self.Array(value, options || {})
199
+ end
200
+
201
+ use :NUMBER_SET, class: Set, of: Integer
202
+ end
203
+ expect(described_class::Classes).to respond_to(:Set)
204
+
205
+ expect(described_class.number_set).to eq Set.new [1, 3, 5, 7, 9]
206
+ end
207
+ end
208
+
209
+ describe "Hash-like behavior" do
210
+ it "provides configured keys" do
211
+ ENV['VAR1'] = 'something'
212
+ ENV['VAR2'] = 'something else'
213
+ described_class.use 'VAR1'
214
+ described_class.use 'VAR2'
215
+
216
+ expect(described_class.keys).to include(*%w[VAR1 VAR2])
217
+ end
218
+
219
+ it "provides configured values" do
220
+ ENV['VAR1'] = 'something'
221
+ ENV['VAR2'] = 'something else'
222
+ described_class.use 'VAR1'
223
+ described_class.use 'VAR2'
224
+
225
+ expect(described_class.values).to include(*%w[something something\ else])
226
+ end
227
+ end
228
+
229
+ describe "Formatting" do
230
+ it "Includes provided description in error message" do
231
+ ENV.delete('NOT_PRESENT')
232
+
233
+ expect {
234
+ described_class.config do
235
+ use 'NOT_PRESENT', 'You need a NOT_PRESENT var in your ENV'
236
+ end
237
+ }.to raise_error(KeyError, /You need a NOT_PRESENT var in your ENV/)
238
+ end
239
+ end
240
+
241
+ describe ".instance" do
242
+ it "returns the instance singleton of #{described_class}" do
243
+ expect(described_class.instance).to be_a(described_class)
244
+ end
245
+
246
+ it "should have a local instance cache of the ENV variables' method names and their return values" do
247
+ ENV['VAR1'] = 'something'
248
+ described_class.use 'VAR1'
249
+
250
+ described_class.var1
251
+ described_class.var1?
252
+
253
+ expect(described_class.instance.cache).to have_key("var1")
254
+ expect(described_class.instance.cache).to have_key("var1?")
255
+ end
256
+
257
+ it "allows the cache to be cleared" do
258
+ ENV['VAR1'] = 'something'
259
+ described_class.use 'VAR1'
260
+
261
+ described_class.var1
262
+ described_class.var1?
263
+
264
+ expect(described_class.instance.cache.keys). to include "var1", "var1?"
265
+
266
+ described_class.instance.clear_cache!
267
+ expect(described_class.instance.cache).to be_empty
268
+ end
269
+ end
270
+
271
+ describe "#set_instance" do
272
+ let(:setting_class) { Class.new(described_class) }
273
+ it "should set the instance Singleton to the given object" do
274
+ obj = setting_class.new
275
+ described_class.set_instance(obj)
276
+ expect(described_class.instance).to be obj
277
+
278
+ obj = described_class.new
279
+ described_class.set_instance(obj)
280
+ expect(described_class.instance).to be obj
281
+ end
282
+
283
+ it "should raise an argument error if the given class is not a derivative of #{described_class}" do
284
+ expect { described_class.set_instance(Object.new) }.to raise_error(ArgumentError)
285
+ end
286
+ end
287
+
288
+ describe "#instance" do
289
+ it "should have the ENV variable methods defined on the Singelton, not the class" do
290
+ # Reset Singleton for fresh test
291
+ described_class.set_instance(described_class.new)
292
+ ENV["MY_SPECIAL_VAR"] = "foo"
293
+
294
+ described_class.use :MY_SPECIAL_VAR
295
+ expect(described_class.method_defined? :my_special_var).to eq false
296
+ expect(described_class.instance).to respond_to(:my_special_var)
297
+ expect(described_class.instance.public_methods).to include :my_special_var, :my_special_var?
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,17 @@
1
+ require 'rspec'
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ])
9
+ SimpleCov.start
10
+
11
+ Coveralls.wear!
12
+
13
+ require 'bundler/setup'
14
+ Bundler.setup
15
+
16
+ require 'env_setting'
17
+
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: env_setting
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Will Spurgin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDjjCCAnagAwIBAgIBATANBgkqhkiG9w0BAQUFADBGMRUwEwYDVQQDDAx3aWxs
14
+ LnNwdXJnaW4xGDAWBgoJkiaJk/IsZAEZFghvcm0tdGVjaDETMBEGCgmSJomT8ixk
15
+ ARkWA2NvbTAeFw0xNjA2MjAxNTU4NDJaFw0xNzA2MjAxNTU4NDJaMEYxFTATBgNV
16
+ BAMMDHdpbGwuc3B1cmdpbjEYMBYGCgmSJomT8ixkARkWCG9ybS10ZWNoMRMwEQYK
17
+ CZImiZPyLGQBGRYDY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
18
+ vRMpVroSt8OrOY/zyh/00HzNryjthX5JabsyIlZBBkqJSBakNa4p9y3EleODdvP8
19
+ 3DPnDgAax5/nYd+UumPbcqPB7lhXHn+vw+082DVVOaL2IMg1fbqLRSpCXGvgz4za
20
+ P4QKMusXXRAo1+nLjl68pumyLfAD6dEF7bNk2diHpKppknb1ENsvs/v8/uWQBv27
21
+ AnIrHntPpKCLwSjxufgCa9IKdSy9EdwCBCwX9IOGTjUhFoRy+Fsx7pUi0NM7eaER
22
+ h0VYrXIPnembxN51iVA6LcM7wnzl6uVnSb/TJshc3zSIqZibvHSPKL1g17S2s8qa
23
+ 2AspSOGAQ7iDAkt1lRnccQIDAQABo4GGMIGDMAkGA1UdEwQCMAAwCwYDVR0PBAQD
24
+ AgSwMB0GA1UdDgQWBBTdymM1YAMQyvVpddd//6sgBWrCOTAkBgNVHREEHTAbgRl3
25
+ aWxsLnNwdXJnaW5Ab3JtLXRlY2guY29tMCQGA1UdEgQdMBuBGXdpbGwuc3B1cmdp
26
+ bkBvcm0tdGVjaC5jb20wDQYJKoZIhvcNAQEFBQADggEBAK4zjjfK53r01ZtIB+xF
27
+ GT8OR3ri+iSrcTAaC7dk4XmjNU42hGBlFZ34RjnzxBGBjBZH9w+3jwCjN8FkPfmO
28
+ f1kiI4+tCt+weUzWFqhKsIaC23TjEDrlhyZ2203HldlW4p26onVwDpIn3YOYG9Qr
29
+ c+9wUpquUpi5e4bBVsIaHoYnECMOrGIgRSleI8YWLAakTWAXRL63dtekC945+3ep
30
+ vbrWi4+bt0feapcxjBsEk2q1TW6XmEWU8HokYJOxNbqKt5XuWZq/fcGgBV+CftFN
31
+ 8o95YBJ2TniSxvMvbz2P9Q/Mh1AhMN4J0OqtcAo1One8UgJBXU8xZHj/qWMLwT9L
32
+ gtM=
33
+ -----END CERTIFICATE-----
34
+ date: 2016-06-20 00:00:00.000000000 Z
35
+ dependencies:
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ requirement: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '3.4'
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '3.4'
64
+ - !ruby/object:Gem::Dependency
65
+ name: simplecov
66
+ requirement: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: coveralls
80
+ requirement: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ type: :development
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ description: Allows OOP access to ENV variables by a slight re-write of the env_bang
93
+ gem.
94
+ email:
95
+ - will.spurgin@orm-tech.com
96
+ executables: []
97
+ extensions: []
98
+ extra_rdoc_files: []
99
+ files:
100
+ - ".gitignore"
101
+ - ".travis.yml"
102
+ - Gemfile
103
+ - LICENSE.txt
104
+ - README.md
105
+ - Rakefile
106
+ - certs/wspurgin.pem
107
+ - env_setting.gemspec
108
+ - lib/env_setting.rb
109
+ - lib/env_setting/classes.rb
110
+ - lib/env_setting/formatter.rb
111
+ - lib/env_setting/version.rb
112
+ - spec/env_setting_spec.rb
113
+ - spec/spec_helper.rb
114
+ homepage: https://github.com/ormtech/env_setting
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.2.2
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Mange your environment variables in OOP style
138
+ test_files: []
@@ -0,0 +1,3 @@
1
+ �V��R?����
2
+ ��O�c�b��#Ot�M��js�_';��&8Q���'�'3�w��@(yu�7�2i�r"�Yx@����x�[� $�O{�S��1��=�����\��&6��gH��:E�(rf�gU�ȪN6F��/8:�W6aTG�����͌��|��"D:*D�||_��w�s��[D� ����/� ��>���{~����k`�' [�� �������L�?�1�O�k�C<_n
3
+ �!����������a��