nenv 0.0.2

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: 4aa66c0bda93185b1177fda8e93484e004f4e802
4
+ data.tar.gz: 8b360b9cb96f49e49ade892326f1ab979ab4ab64
5
+ SHA512:
6
+ metadata.gz: 872463708e4ba5ea45e565f5cf2de586900e702abd3996037388947f3e9e69da949e62d1a60dfa41550400427ee27e6cc8c736516f17278dc2a606903d7f81bb
7
+ data.tar.gz: b793da1678ae9e09d164f0121286f6f35874c8721da2351aab457918188d086c318dcd84a75deaa67c9f7c1f270057275d26d5f02fabdf1929e1fe336c202882
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nenv.gemspec
4
+ gemspec development_group: :test
5
+
6
+ group :development do
7
+ gem 'guard-rspec', '~> 4.5.0'
8
+ end
@@ -0,0 +1,34 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ directories(%w(lib spec))
6
+
7
+ ## Uncomment to clear the screen before every task
8
+ # clearing :on
9
+
10
+ # Note: The cmd option is now required due to the increasing number of ways
11
+ # rspec may be run, below are examples of the most common uses.
12
+ # * bundler: 'bundle exec rspec'
13
+ # * bundler binstubs: 'bin/rspec'
14
+ # * spring: 'bin/rspec' (This will use spring if running and you have
15
+ # installed the spring binstubs per the docs)
16
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
17
+ # * 'just' rspec: 'rspec'
18
+
19
+ guard :rspec, cmd: "bundle exec rspec" do
20
+ require "guard/rspec/dsl"
21
+ dsl = Guard::RSpec::Dsl.new(self)
22
+
23
+ # Feel free to open issues for suggestions and improvements
24
+
25
+ # RSpec files
26
+ rspec = dsl.rspec
27
+ watch(rspec.spec_helper) { rspec.spec_dir }
28
+ watch(rspec.spec_support) { rspec.spec_dir }
29
+ watch(rspec.spec_files)
30
+
31
+ # Ruby files
32
+ ruby = dsl.ruby
33
+ dsl.watch_spec_files_for(ruby.lib_files)
34
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Cezary Baginski
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,232 @@
1
+ # Nenv
2
+
3
+ Using ENV in Ruby is like using raw SQL statements - it feels wrong, because it is.
4
+
5
+ If you agree, this gem is for you.
6
+
7
+ ## The benefits over using ENV directly:
8
+
9
+ - much friendlier stubbing in tests
10
+ - you no longer have to care whether false is "0" or "false" or whatever
11
+ - NO MORE ALL CAPS EVERYWHERE!
12
+ - keys become methods
13
+ - namespaces which can be passed around as objects
14
+ - you can subclass!
15
+ - you can marshal/unmarshal your own types automatically!
16
+ - strict mode saves you from doing validation yourself
17
+ - and there's more to come...
18
+
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'nenv'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install nenv
35
+
36
+
37
+ ## Examples !!!
38
+
39
+
40
+ ### Automatic booleans
41
+
42
+ You no longer have to care whether the value is "0" or "false" or "no" or "FALSE" or ... whatever
43
+
44
+ ```ruby
45
+ t.verbose = (ENV['CI'] == 'true')
46
+ ok = ENV['RUBYGEMS_GEMDEPS'] == "1" || ENV.key?('BUNDLE_GEMFILE']
47
+ ENV['DEBUG'] = "true"
48
+
49
+ ```
50
+
51
+ now becomes:
52
+
53
+ ```ruby
54
+ t.verbose = Nenv.ci?
55
+ gemdeps = Nenv.rubygems_gemdeps? || Nenv.bundle_gemfile?
56
+ Nenv.debug = true
57
+ ```
58
+
59
+ ### "Namespaces"
60
+
61
+ ```ruby
62
+ puts ENV['GIT_BROWSER`]
63
+ puts ENV['GIT_PAGER`]
64
+ puts ENV['GIT_EDITOR`]
65
+ ```
66
+
67
+ now becomes:
68
+
69
+ ```ruby
70
+ git = Nenv :git
71
+ puts git.browser
72
+ puts git.pager
73
+ puts git.editor
74
+ ```
75
+
76
+ ### Custom type handling
77
+
78
+ ```ruby
79
+ paths = [ENV['GEM_HOME`]] + ENV['GEM_PATH'].split(':')
80
+ enable_logging if Integer(ENV['WEB_CONCURRENCY']) > 1
81
+ mydata = YAML.load(ENV['MY_DATA'])
82
+ ENV['VERBOSE'] = debug ? "1" : nil
83
+ ```
84
+
85
+ can become:
86
+
87
+ ```ruby
88
+ # setup
89
+ gem = Nenv :gem
90
+ gem.instance.create_method(:path) { |p| p.split(':') }
91
+
92
+ web = Nenv :web
93
+ web.instance.create_method(:concurrency) { |c| Integer(c) }
94
+
95
+ my = Nenv :my
96
+ my.instance.create_method(:data) { |d| YAML.load(d) }
97
+
98
+ Nenv.instance.create_method(:verbose=) { |v| v ? 1 : nil }
99
+
100
+ # and then you can simply do:
101
+
102
+ paths = [gem.home] + gem.path
103
+ enable_logging if web.concurrency > 1
104
+ mydata = my.data
105
+ Nenv.verbose = debug
106
+ ```
107
+
108
+ ### Automatic conversion to string
109
+
110
+ ```ruby
111
+ ENV['RUBYGEMS_GEMDEPS'] = 1 # TypeError: no implicit conversion of Fixnum into String
112
+ ```
113
+
114
+ No automatically uses `to_s`:
115
+
116
+ ```ruby
117
+ Nenv.rubygems_gemdeps = 1 # no problem here
118
+ ```
119
+
120
+
121
+ ### Custom assignment
122
+
123
+ ```ruby
124
+ data = YAML.load(ENV['MY_DATA'])
125
+ data[:foo] = :bar
126
+ ENV['MY_DATA'] = YAML.dump(data)
127
+ ```
128
+
129
+ can now become:
130
+
131
+ ```ruby
132
+ my = Nenv(:my)
133
+ my.instance.create_method(:data) { |d| YAML.load(d) }
134
+ my.instance.create_method(:data=) { |d| YAML.dump(d) }
135
+
136
+ data = my.data
137
+ data[:foo] = :bar
138
+ my.data = data
139
+ ```
140
+
141
+ ### Strict mode
142
+
143
+ ```ruby
144
+ fail 'home not allowed' if ENV['HOME'] = Dir.pwd # BUG! Assignment instead of comparing!
145
+ puts ENV['HOME'] # Now contains clobbered value
146
+ ```
147
+
148
+ Now, clobbering can be prevented:
149
+
150
+ ```ruby
151
+ env = Nenv::Environment.new
152
+ env.create_method(:home)
153
+
154
+ fail 'home not allowed' if env.home = Dir.pwd # Fails with NoMethodError
155
+ puts env.home # works
156
+ ```
157
+
158
+ ### Mashup mode
159
+
160
+ You can first define all the load/dump logic globally in one place
161
+
162
+ ```ruby
163
+ Nenv.instance.create_method(:web_concurrency) { |d| Integer(d) }
164
+ Nenv.instance.create_method(:web_concurrency=)
165
+ Nenv.instance.create_method(:path) { |p| Pathname(p.split(File::PATH_SEPARATOR)) }
166
+ Nenv.instance.create_method(:path=) { |array| array.map(&:to_s).join(File::PATH_SEPARATOR) }
167
+
168
+ # And now, anywhere in your app:
169
+
170
+ Nenv.web_concurrency += 3
171
+ Nenv.path += Pathname.pwd + "foo"
172
+
173
+ ```
174
+
175
+ ## NOTES
176
+
177
+ Still, avoid using environment variables if you can.
178
+
179
+ At least, avoid actually setting them - especially in multithreaded apps.
180
+
181
+ As for Nenv, while you can access the same variable with or without namespaces,
182
+ filters are tied to instances, e.g.:
183
+
184
+ ```ruby
185
+ Nenv.instance.create_method(:foo_bar) { |d| Integer(d) }
186
+ Nenv('foo').instance.create_method(:bar) { |d| Float(d) }
187
+ env = Nenv::Environment.new(:foo).tap { |e| e.create_method(:bar) }
188
+ ```
189
+
190
+ all work on the same variable, but each uses a different filter for reading the value.
191
+
192
+
193
+ ## What's wrong with ENV?
194
+
195
+ Well sure, having ENV act like a Hash is much better than calling "getenv".
196
+
197
+ Unfortunately, the advantages of using ENV make no sense:
198
+
199
+ 1) it's faster but ... environment variables are rarely used thousands of times in tight loops
200
+ 2) it's already an object ... but there's not much you can do with it
201
+ 3) it's globally available ... but you can't isolate it in tests (you need to reset it every time)
202
+ 4) you can use it to set variables ... but it's named like a const
203
+ 5) it allows you to use keys regardless of case ... but by convention lowercase shouldn't be used except for local variables (which are only really used by shell scripts)
204
+ 6) it's supposed to look ugly to discourage use ... but often your app/gem is forced to use them anyway
205
+ 7) it's a simple class ... but either you encapsulate it in your own classes - or all the value mapping/validation happens everywhere you want the data
206
+
207
+
208
+ But the BIGGEST disadvantage is in specs, e.g.:
209
+
210
+ ```ruby
211
+ allow(ENV).to receive(:[]).with('MY_VARIABLE').and_return("old data")
212
+ allow(ENV).to receive(:[]=).with('MY_VARIABLE', "new data")
213
+ ```
214
+
215
+ which could instead be completely isolated as:
216
+
217
+ ```ruby
218
+ let(:env) { instance_double(Nenv::Environment) }
219
+ before { allow(Nenv::Environment).to receive(:new).with(:my).and_return(env) }
220
+
221
+ allow(env).to receive(:variable).and_return("old data")
222
+ allow(env).to receive(:variable=).with("new data")
223
+ ```
224
+
225
+
226
+ ## Contributing
227
+
228
+ 1. Fork it ( https://github.com/[my-github-username]/nenv/fork )
229
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
230
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
231
+ 4. Push to the branch (`git push origin my-new-feature`)
232
+ 5. Create a new Pull Request
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ default_tasks = []
4
+
5
+ require "rspec/core/rake_task"
6
+ default_tasks << RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: default_tasks.map(&:name)
@@ -0,0 +1,27 @@
1
+ require "nenv/version"
2
+
3
+ require "nenv/autoenvironment"
4
+
5
+ def Nenv(namespace=nil)
6
+ Nenv::AutoEnvironment.new(namespace)
7
+ end
8
+
9
+ module Nenv
10
+ class << self
11
+ def respond_to?(meth)
12
+ instance.respond_to?(meth)
13
+ end
14
+
15
+ def method_missing(meth, *args)
16
+ instance.send(meth, *args)
17
+ end
18
+
19
+ def reset
20
+ @instance = nil
21
+ end
22
+
23
+ def instance
24
+ @instance ||= Nenv::AutoEnvironment.new
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ require "nenv/environment"
2
+ module Nenv
3
+ class AutoEnvironment < Nenv::Environment
4
+ def method_missing(meth, *args)
5
+ create_method(meth) unless respond_to?(meth)
6
+ self.send(meth, *args)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,47 @@
1
+ require "nenv/environment/dumper"
2
+ require "nenv/environment/loader"
3
+
4
+ module Nenv
5
+ class Environment
6
+ class Error < ArgumentError
7
+ end
8
+
9
+ class MethodError < Error
10
+ def initialize(meth)
11
+ @meth = meth
12
+ end
13
+ end
14
+
15
+ class AlreadyExistsError < MethodError
16
+ def message
17
+ format("Method %s already exists", @meth.inspect)
18
+ end
19
+ end
20
+
21
+ def initialize(namespace = nil)
22
+ @namespace = (namespace ? namespace.upcase : nil)
23
+ end
24
+
25
+ def create_method(meth, &block)
26
+ fail(AlreadyExistsError, meth) if respond_to?(meth)
27
+
28
+ (class << self; self; end).send(:define_method, meth) do |*args|
29
+ raw_value = args.first
30
+ env_name = [@namespace, _sanitize(meth)].compact.join('_')
31
+
32
+ callback = block
33
+ if args.size == 1
34
+ ENV[env_name] = Dumper.new.dump(raw_value, &callback)
35
+ else
36
+ Loader.new(meth).load(ENV[env_name], &callback)
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def _sanitize(meth)
44
+ meth[/^([^=?]*)[=?]?$/, 1].upcase
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,10 @@
1
+ module Nenv
2
+ class Environment
3
+ class Dumper
4
+ def dump(raw_value, &callback)
5
+ return callback.call(raw_value) if callback
6
+ raw_value.nil? ? nil : raw_value.to_s
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,29 @@
1
+ module Nenv
2
+ class Environment
3
+ class Loader
4
+ def initialize(meth)
5
+ @bool = meth.to_s.end_with?("?")
6
+ end
7
+
8
+ def load(raw_value, &callback)
9
+ return callback.call(raw_value) if callback
10
+ @bool ? _to_bool(raw_value) : raw_value
11
+ end
12
+
13
+ private
14
+
15
+ def _to_bool(raw_value)
16
+ case raw_value
17
+ when nil
18
+ nil
19
+ when ""
20
+ fail ArgumentError, "Can't convert empty string into Bool"
21
+ when "0", "false", "n", "no", "NO", "FALSE"
22
+ false
23
+ else
24
+ true
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Nenv
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nenv/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "nenv"
8
+ spec.version = Nenv::VERSION
9
+ spec.authors = ["Cezary Baginski"]
10
+ spec.email = ["cezary@chronomantic.net"]
11
+ spec.summary = %q{Convenience wrapper for Ruby's ENV}
12
+ spec.description = %q{Using ENV is like using raw SQL statements in your code. Well all know how that ends...}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rspec", "~> 3.1"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ end
@@ -0,0 +1,34 @@
1
+ require 'yaml'
2
+
3
+ require "nenv/environment/dumper"
4
+
5
+ RSpec.describe Nenv::Environment::Dumper do
6
+ subject { described_class.new.dump(value) }
7
+
8
+ context "with \"abc\"" do
9
+ let(:value) { "abc" }
10
+ it { is_expected.to eq("abc") }
11
+ end
12
+
13
+ context "with 123" do
14
+ let(:value) { 123 }
15
+ it { is_expected.to eq("123") }
16
+ end
17
+
18
+ context "with nil" do
19
+ let(:value) { nil }
20
+ it { is_expected.to eq(nil) }
21
+ end
22
+
23
+ context "with a block" do
24
+ subject do
25
+ described_class.new.dump(value) { |data| YAML::dump(data) }
26
+ end
27
+
28
+ context "with a yaml string" do
29
+ let(:value) { { foo: 3 } }
30
+ let(:yaml) { "---\n:foo: 3\n" }
31
+ it { is_expected.to eq(yaml) }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ require 'yaml'
2
+ require "nenv/environment/loader"
3
+
4
+ RSpec.describe Nenv::Environment::Loader do
5
+ context "with no block" do
6
+ subject { described_class.new(meth).load(value) }
7
+
8
+ context "with a normal method" do
9
+ let(:meth) { :foo }
10
+
11
+ context "with \"abc\"" do
12
+ let(:value) { "abc" }
13
+ it { is_expected.to eq("abc") }
14
+ end
15
+ end
16
+
17
+ context "with a bool method" do
18
+ let(:meth) { :foo? }
19
+
20
+ %w(1 true y yes TRUE YES foobar).each do |data|
21
+ context "with #{data.inspect}" do
22
+ let(:value) { data }
23
+ it { is_expected.to eq(true) }
24
+ end
25
+ end
26
+
27
+ %w(0 false n no FALSE NO).each do |data|
28
+ context "with #{data.inspect}" do
29
+ let(:value) { data }
30
+ it { is_expected.to eq(false) }
31
+ end
32
+ end
33
+
34
+ context "with nil" do
35
+ let(:value) { nil }
36
+ it { is_expected.to eq(nil) }
37
+ end
38
+
39
+ context "when empty string" do
40
+ let(:value) { "" }
41
+ it do
42
+ expect { subject }.to raise_error(
43
+ ArgumentError, /Can't convert empty string into Bool/
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ context "with a block" do
51
+ subject do
52
+ described_class.new(:foo).load(value) { |data| YAML::load(data) }
53
+ end
54
+ context "with a yaml string" do
55
+ let(:value) { "--- foo\n...\n" }
56
+ it { is_expected.to eq("foo") }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,223 @@
1
+ require "yaml"
2
+
3
+ require "nenv/environment"
4
+
5
+ RSpec.describe Nenv::Environment do
6
+ let(:env) { instance_double(Hash) } # a hash is close enough
7
+ before(:each) { stub_const("ENV", env) }
8
+
9
+ context "without integration" do
10
+
11
+ let(:dumper) { instance_double(described_class::Dumper) }
12
+ let(:loader) { instance_double(described_class::Loader) }
13
+
14
+ before do
15
+ allow(described_class::Dumper).to receive(:new).and_return(dumper)
16
+ allow(described_class::Loader).to receive(:new).and_return(loader)
17
+ end
18
+
19
+ context "with no namespace" do
20
+ let(:instance) { described_class.new }
21
+
22
+ context "with an existing method" do
23
+ before do
24
+ subject.create_method(:foo?)
25
+ end
26
+
27
+ it "uses the name as full key" do
28
+ expect(ENV).to receive(:[]).with("FOO").and_return("true")
29
+ expect(loader).to receive(:load).with("true").and_return(true)
30
+ expect(subject.foo?).to eq(true)
31
+ end
32
+ end
33
+ end
34
+
35
+ context "with any namespace" do
36
+ let(:namespace) { "bar" }
37
+ let(:instance) { described_class.new(namespace) }
38
+
39
+ describe "creating a method" do
40
+ subject { instance }
41
+
42
+ before do
43
+ subject.create_method(:foo)
44
+ end
45
+
46
+ it { is_expected.to respond_to(:foo) }
47
+
48
+ context "when the method already exists" do
49
+ let(:error) { described_class::AlreadyExistsError }
50
+ let(:message) { "Method :foo already exists" }
51
+ specify do
52
+ expect do
53
+ subject.create_method(:foo)
54
+ end.to raise_error(error, message)
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "calling" do
60
+ subject { instance }
61
+
62
+ context "when method does not exist" do
63
+ let(:error) { NoMethodError }
64
+ let(:message) { /undefined method `foo' for/ }
65
+ it { expect { subject.foo }.to raise_error(error, message) }
66
+ end
67
+
68
+ context "with a reader method" do
69
+
70
+ context "with no block" do
71
+ before { instance.create_method(meth) }
72
+
73
+ context "with a normal method" do
74
+ let(:meth) { :foo }
75
+ before do
76
+ allow(loader).to receive(:load).with("123").and_return(123)
77
+ end
78
+
79
+ it "returns unmarshalled stored value" do
80
+ expect(ENV).to receive(:[]).with("BAR_FOO").and_return("123")
81
+ expect(subject.foo).to eq 123
82
+ end
83
+ end
84
+
85
+ context "with a bool method" do
86
+ let(:meth) { :foo? }
87
+
88
+ it "references the proper ENV variable" do
89
+ allow(loader).to receive(:load).with("false").and_return(false)
90
+ expect(ENV).to receive(:[]).with("BAR_FOO").and_return("false")
91
+ expect(subject.foo?).to eq false
92
+ end
93
+ end
94
+ end
95
+
96
+ context "with a block" do
97
+ before do
98
+ instance.create_method(:foo) { |data| YAML::load(data) }
99
+ end
100
+
101
+ let(:value) { "---\n:foo: 5\n" }
102
+
103
+ it "unmarshals using the block" do
104
+ allow(ENV).to receive(:[]).with("BAR_FOO").
105
+ and_return(value)
106
+
107
+ allow(loader).to receive(:load).with(value) do |arg, &block|
108
+ expect(block).to be
109
+ block.call(arg)
110
+ end
111
+
112
+ expect(subject.foo).to eq(foo: 5)
113
+ end
114
+ end
115
+
116
+ end
117
+
118
+ context "with a writer method" do
119
+ before { instance.create_method(:foo=) }
120
+
121
+ it "set the environment variable" do
122
+ expect(ENV).to receive(:[]=).with("BAR_FOO", "123")
123
+ allow(dumper).to receive(:dump).with(123).and_return("123")
124
+ subject.foo = 123
125
+ end
126
+
127
+ it "marshals and stores the value" do
128
+ expect(ENV).to receive(:[]=).with("BAR_FOO", "123")
129
+ allow(dumper).to receive(:dump).with(123).and_return("123")
130
+ subject.foo = 123
131
+ end
132
+ end
133
+
134
+ context "with a method containing underscores" do
135
+ before { instance.create_method(:foo_baz) }
136
+ it "reads the correct variable" do
137
+ expect(ENV).to receive(:[]).with("BAR_FOO_BAZ").and_return("123")
138
+ allow(loader).to receive(:load).with("123").and_return(123)
139
+ subject.foo_baz
140
+ end
141
+ end
142
+
143
+ context "with a block" do
144
+ before do
145
+ instance.create_method(:foo=) { |data| YAML::dump(data) }
146
+ end
147
+
148
+ let(:result) { "---\n:foo: 5\n" }
149
+
150
+ it "marshals using the block" do
151
+ allow(ENV).to receive(:[]=).with("BAR_FOO", result)
152
+
153
+ allow(dumper).to receive(:dump).with(foo: 5) do |arg, &block|
154
+ expect(block).to be
155
+ block.call(arg)
156
+ end
157
+
158
+ subject.foo = { foo: 5 }
159
+ end
160
+ end
161
+
162
+ context "with an unsanitized name" do
163
+ pending
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ describe "with integration" do
170
+ context "with any namespace" do
171
+ let(:namespace) { "baz" }
172
+ let(:instance) { described_class.new(namespace) }
173
+ subject { instance }
174
+
175
+ context "with a reader method" do
176
+ context "with no block" do
177
+ before { instance.create_method(:foo) }
178
+
179
+ it "returns the stored value" do
180
+ allow(ENV).to receive(:[]).with("BAZ_FOO").and_return("123")
181
+ expect(subject.foo).to eq "123"
182
+ end
183
+ end
184
+
185
+ context "with a block" do
186
+ before do
187
+ instance.create_method(:foo) { |data| YAML::load(data) }
188
+ end
189
+
190
+ it "unmarshals the value" do
191
+ expect(ENV).to receive(:[]).with("BAZ_FOO").
192
+ and_return("---\n:foo: 5\n")
193
+
194
+ expect(subject.foo).to eq(foo: 5)
195
+ end
196
+ end
197
+ end
198
+
199
+ context "with a writer method" do
200
+ context "with no block" do
201
+ before { instance.create_method(:foo=) }
202
+
203
+ it "marshals and stores the value" do
204
+ expect(ENV).to receive(:[]=).with("BAZ_FOO", "123")
205
+ subject.foo = 123
206
+ end
207
+ end
208
+
209
+ context "with a block" do
210
+ before do
211
+ instance.create_method(:foo=) { |data| YAML::dump(data) }
212
+ end
213
+
214
+ it "nmarshals the value" do
215
+ expect(ENV).to receive(:[]=).with("BAZ_FOO", "---\n:foo: 5\n")
216
+
217
+ subject.foo = { foo: 5 }
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,53 @@
1
+ require "nenv"
2
+
3
+ RSpec.describe Nenv do
4
+ let(:env) { instance_double(Hash) } # Hash is close enough
5
+ before { stub_const("ENV", env) }
6
+
7
+ describe "Nenv() helper method" do
8
+ it "reads from env" do
9
+ expect(ENV).to receive(:[]).with('GIT_BROWSER').and_return('chrome')
10
+ Nenv('git').browser
11
+ end
12
+
13
+ it "return the value from env" do
14
+ allow(ENV).to receive(:[]).with('GIT_BROWSER').and_return('firefox')
15
+ expect(Nenv('git').browser).to eq('firefox')
16
+ end
17
+ end
18
+
19
+ describe "Nenv() module" do
20
+ it "reads from env" do
21
+ expect(ENV).to receive(:[]).with('CI').and_return('true')
22
+ Nenv.ci?
23
+ end
24
+
25
+ it "return the value from env" do
26
+ allow(ENV).to receive(:[]).with('CI').and_return('false')
27
+ expect(Nenv.ci?).to be(false)
28
+ end
29
+
30
+ context "with no method" do
31
+ it "automatically creates the method" do
32
+ expect(ENV).to receive(:[]).with('FOO').and_return('true')
33
+ Nenv.foo?
34
+ end
35
+ end
36
+
37
+ context "with existing method" do
38
+ before do
39
+ Nenv.instance.create_method(:foo?)
40
+ end
41
+
42
+ it "reads from env" do
43
+ expect(ENV).to receive(:[]).with('FOO').and_return("true")
44
+ Nenv.foo?
45
+ end
46
+
47
+ it "return the value from env" do
48
+ expect(ENV).to receive(:[]).with('FOO').and_return("true")
49
+ expect(Nenv.foo?).to be(true)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,43 @@
1
+ RSpec.configure do |config|
2
+ config.expect_with :rspec do |expectations|
3
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
4
+ end
5
+
6
+ config.mock_with :rspec do |mocks|
7
+ mocks.verify_partial_doubles = true
8
+ end
9
+
10
+ config.filter_run :focus
11
+ config.run_all_when_everything_filtered = true
12
+
13
+ config.disable_monkey_patching!
14
+
15
+ # config.warnings = true
16
+
17
+ if config.files_to_run.one?
18
+ config.default_formatter = 'doc'
19
+ end
20
+
21
+ # config.profile_examples = 10
22
+
23
+ config.order = :random
24
+
25
+ Kernel.srand config.seed
26
+
27
+ config.before do
28
+ allow(ENV).to receive(:[]) do |key|
29
+ fail "stub me: ENV[#{key.inspect}]"
30
+ end
31
+
32
+ allow(ENV).to receive(:[]=) do |key, value|
33
+ fail "stub me: ENV[#{key.inspect}] = #{value.inspect}"
34
+ end
35
+ end
36
+
37
+ config.after do
38
+ begin
39
+ Nenv.method(:reset).call
40
+ rescue NameError
41
+ end
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nenv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Cezary Baginski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ description: Using ENV is like using raw SQL statements in your code. Well all know
56
+ how that ends...
57
+ email:
58
+ - cezary@chronomantic.net
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".rspec"
65
+ - Gemfile
66
+ - Guardfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - lib/nenv.rb
71
+ - lib/nenv/autoenvironment.rb
72
+ - lib/nenv/environment.rb
73
+ - lib/nenv/environment/dumper.rb
74
+ - lib/nenv/environment/loader.rb
75
+ - lib/nenv/version.rb
76
+ - nenv.gemspec
77
+ - spec/lib/nenv/environment/dumper_spec.rb
78
+ - spec/lib/nenv/environment/loader_spec.rb
79
+ - spec/lib/nenv/environment_spec.rb
80
+ - spec/lib/nenv_spec.rb
81
+ - spec/spec_helper.rb
82
+ homepage: ''
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.2.2
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Convenience wrapper for Ruby's ENV
106
+ test_files:
107
+ - spec/lib/nenv/environment/dumper_spec.rb
108
+ - spec/lib/nenv/environment/loader_spec.rb
109
+ - spec/lib/nenv/environment_spec.rb
110
+ - spec/lib/nenv_spec.rb
111
+ - spec/spec_helper.rb