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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +8 -0
- data/Guardfile +34 -0
- data/LICENSE.txt +22 -0
- data/README.md +232 -0
- data/Rakefile +8 -0
- data/lib/nenv.rb +27 -0
- data/lib/nenv/autoenvironment.rb +9 -0
- data/lib/nenv/environment.rb +47 -0
- data/lib/nenv/environment/dumper.rb +10 -0
- data/lib/nenv/environment/loader.rb +29 -0
- data/lib/nenv/version.rb +3 -0
- data/nenv.gemspec +24 -0
- data/spec/lib/nenv/environment/dumper_spec.rb +34 -0
- data/spec/lib/nenv/environment/loader_spec.rb +59 -0
- data/spec/lib/nenv/environment_spec.rb +223 -0
- data/spec/lib/nenv_spec.rb +53 -0
- data/spec/spec_helper.rb +43 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -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
|
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
data/lib/nenv.rb
ADDED
@@ -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,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,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
|
data/lib/nenv/version.rb
ADDED
data/nenv.gemspec
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|