env_setting 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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��