nenv 0.0.2

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: 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