remenv 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +118 -0
- data/Rakefile +15 -0
- data/bin/remenv +6 -0
- data/lib/remenv.rb +30 -0
- data/lib/remenv/adapter/memory.rb +33 -0
- data/lib/remenv/adapter/redis.rb +36 -0
- data/lib/remenv/cli.rb +106 -0
- data/lib/remenv/config.rb +16 -0
- data/lib/remenv/formatter.rb +15 -0
- data/lib/remenv/io.rb +46 -0
- data/lib/remenv/tasks/remenv.cap +16 -0
- data/lib/remenv/version.rb +3 -0
- data/remenv.gemspec +28 -0
- data/spec/adapter/memory_spec.rb +12 -0
- data/spec/adapter/redis_spec.rb +42 -0
- data/spec/remenv/config_spec.rb +32 -0
- data/spec/remenv/formatter_spec.rb +27 -0
- data/spec/remenv/io_spec.rb +70 -0
- data/spec/remenv_spec.rb +61 -0
- data/spec/spec_helper.rb +22 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5a6fba380b02fbbac878a4d9d213a6fc2e79aa16
|
4
|
+
data.tar.gz: d6b942a1a0e4b9307823cb06f9f4bc34ea48744a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d20f07b922da837876ba08bfdd6b5d218ba43d97af224878c184ee8a68ebe3d48f61413c0b5489950afdb0dbffd67ace5d74f660b71133597479d09026bec2c5
|
7
|
+
data.tar.gz: 8487cf51d4e4d596aaadee6dbc4fe284f47d380910a75beba59436a355bd522d925f5fe689946d9796f74e80531508c88d02f1bc7424dc3f276ffb52791d82cf
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Dscout, Inc
|
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,118 @@
|
|
1
|
+
# Remenv
|
2
|
+
|
3
|
+
Remote environment management. Heroku style environment configuration brought to
|
4
|
+
any production environment that has a key/value store.
|
5
|
+
|
6
|
+
## Why Remenv?
|
7
|
+
|
8
|
+
Managing your system's configuration with environment variables is effective,
|
9
|
+
convenient, safe, and flexibile. It lets an application use the same code across
|
10
|
+
all environments and be configured entirely by variables in the environment.
|
11
|
+
The problem comes in when you have multiple production systems that need to have
|
12
|
+
the same environment. Problems are compounded when there are multiple developers
|
13
|
+
deploying to the same environment but with differing local copies of production
|
14
|
+
environment variables. Of course, ideally nobody would be in that kind of
|
15
|
+
distributed race conundrum, but in reality people make mistakes and the
|
16
|
+
production flow isn't as coordinated as we may like.
|
17
|
+
|
18
|
+
The goal of remenv is to provide a very simple (very Heroku inspired) set of
|
19
|
+
tools for managing an applications environment across any number of systems. It
|
20
|
+
approaches the problem by applying a thin layer of sugar over maniupating a
|
21
|
+
centralized key value store—namely Redis.
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Add this line to your application's Gemfile.
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem 'remenv'
|
29
|
+
```
|
30
|
+
|
31
|
+
You'll probably want to binstub it so you can avoid typing `bundle exec` all of
|
32
|
+
the time:
|
33
|
+
|
34
|
+
```bash
|
35
|
+
bundle binstub remenv
|
36
|
+
```
|
37
|
+
|
38
|
+
## Command Line Usage
|
39
|
+
|
40
|
+
Running `remenv` from the command line will print out a (hopefully) helpful bit
|
41
|
+
of documentation about each command. That is certainly the best place to start:
|
42
|
+
|
43
|
+
```
|
44
|
+
$ remenv
|
45
|
+
|
46
|
+
Commands:
|
47
|
+
remenv clear # Clear all stored key/value pairs
|
48
|
+
remenv dump FILENAME # Dump all stored key/value pairs to FILENAME
|
49
|
+
remenv help [COMMAND] # Describe available commands or one specific command
|
50
|
+
remenv load FILENAME # Load all key/value pairs from FILENAME
|
51
|
+
remenv set KEY=VALUE # Store VALUE for KEY
|
52
|
+
remenv show [KEY] # Show value for KEY or all stored key/value pairs
|
53
|
+
remenv unset KEY # Remove values for KEY
|
54
|
+
|
55
|
+
Options:
|
56
|
+
[--adapter=ADAPTER] # Default: redis
|
57
|
+
[--stage=STAGE] # Default: development
|
58
|
+
[--namespace=NAMESPACE] # Default: remenv
|
59
|
+
[--url=URL]
|
60
|
+
```
|
61
|
+
|
62
|
+
## Configuration
|
63
|
+
|
64
|
+
Settings should be persisted by writing configuration as YAML to `.remenv.yml`
|
65
|
+
for the current directory. Multiple stages (development, production, etc) can be
|
66
|
+
specified within the same configuration if desired. The default stage is assumed
|
67
|
+
to be development.
|
68
|
+
|
69
|
+
```yaml
|
70
|
+
test:
|
71
|
+
adapter: memory
|
72
|
+
namespace: remenv-test
|
73
|
+
development:
|
74
|
+
adapter: redis
|
75
|
+
namespace: remenv
|
76
|
+
url: redis://localhost:6379/15
|
77
|
+
production:
|
78
|
+
adapter: redis
|
79
|
+
namespace: remenv-production
|
80
|
+
url: redis://server.com:6379/0
|
81
|
+
```
|
82
|
+
|
83
|
+
## Workflow
|
84
|
+
|
85
|
+
1. Import your existing `.env` configuration for development or production:
|
86
|
+
|
87
|
+
```bash
|
88
|
+
$ remenv load .env
|
89
|
+
```
|
90
|
+
|
91
|
+
2. Update any vars that may be missing or incorrect:
|
92
|
+
|
93
|
+
```bash
|
94
|
+
$ remenv show
|
95
|
+
|
96
|
+
LOG_LEVEL DEBUG
|
97
|
+
RACK_ENV production
|
98
|
+
RAILS_ENV production
|
99
|
+
|
100
|
+
$ remenv set LOG_LEVEL=INFO
|
101
|
+
|
102
|
+
OK!
|
103
|
+
LOG_LEVEL INFO
|
104
|
+
```
|
105
|
+
|
106
|
+
3. Sync the configuration to a remote server using Capistrano:
|
107
|
+
|
108
|
+
```bash
|
109
|
+
$ cap production remenv:sync
|
110
|
+
```
|
111
|
+
|
112
|
+
## Contributing
|
113
|
+
|
114
|
+
1. Fork it ( http://github.com/dscout/remenv/fork )
|
115
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
116
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
117
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
118
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new :spec
|
6
|
+
|
7
|
+
task :console do
|
8
|
+
require 'irb'
|
9
|
+
require 'irb/completion'
|
10
|
+
require 'remenv'
|
11
|
+
ARGV.clear
|
12
|
+
IRB.start
|
13
|
+
end
|
14
|
+
|
15
|
+
task default: :spec
|
data/bin/remenv
ADDED
data/lib/remenv.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'remenv/adapter/memory'
|
3
|
+
require 'remenv/config'
|
4
|
+
require 'remenv/io'
|
5
|
+
require 'remenv/version'
|
6
|
+
require 'remenv/formatter'
|
7
|
+
|
8
|
+
module Remenv
|
9
|
+
extend Forwardable
|
10
|
+
extend self
|
11
|
+
|
12
|
+
def_delegators :adapter, :show, :clear!, :set, :get, :unset
|
13
|
+
def_delegators :io, :dump, :load
|
14
|
+
|
15
|
+
def configure
|
16
|
+
yield self
|
17
|
+
end
|
18
|
+
|
19
|
+
def adapter=(adapter)
|
20
|
+
@adapter = adapter
|
21
|
+
end
|
22
|
+
|
23
|
+
def adapter
|
24
|
+
@adapter ||= Adapter::Memory.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def io
|
28
|
+
Remenv::IO.new(adapter)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Remenv
|
2
|
+
module Adapter
|
3
|
+
class Memory
|
4
|
+
attr_reader :store
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
clear!
|
8
|
+
end
|
9
|
+
|
10
|
+
def show
|
11
|
+
store
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear!
|
15
|
+
@store = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def set(pairs)
|
19
|
+
pairs.each do |key, value|
|
20
|
+
store[key.to_s] = value.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(key)
|
25
|
+
store[key.to_s]
|
26
|
+
end
|
27
|
+
|
28
|
+
def unset(*keys)
|
29
|
+
keys.each { |key| store.delete(key) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
module Remenv
|
4
|
+
module Adapter
|
5
|
+
class Redis
|
6
|
+
attr_reader :store, :namespace
|
7
|
+
|
8
|
+
def initialize(url: , namespace: 'remenv')
|
9
|
+
@store = ::Redis.new(url: url)
|
10
|
+
@namespace = namespace
|
11
|
+
end
|
12
|
+
|
13
|
+
def show
|
14
|
+
store.hgetall(namespace)
|
15
|
+
end
|
16
|
+
|
17
|
+
def clear!
|
18
|
+
store.del(namespace)
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(pairs)
|
22
|
+
pairs.each do |field, value|
|
23
|
+
store.hset(namespace, field, value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(key)
|
28
|
+
store.hget(namespace, key)
|
29
|
+
end
|
30
|
+
|
31
|
+
def unset(*keys)
|
32
|
+
store.hdel(namespace, keys)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/remenv/cli.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Remenv
|
4
|
+
class CLI < Thor
|
5
|
+
class_option :adapter, default: 'redis'
|
6
|
+
class_option :stage, default: 'development'
|
7
|
+
class_option :namespace, default: 'remenv'
|
8
|
+
class_option :url
|
9
|
+
|
10
|
+
no_commands do
|
11
|
+
def merged_settings
|
12
|
+
defaults = Remenv::Config.load(stage: options[:stage])
|
13
|
+
symbolized = defaults.each_with_object({}) do |(key, val), hash|
|
14
|
+
hash[key.to_sym] = val
|
15
|
+
end
|
16
|
+
|
17
|
+
symbolized.merge!(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure!
|
21
|
+
settings = merged_settings
|
22
|
+
|
23
|
+
if settings['adapter'] == 'redis'
|
24
|
+
require 'remenv/adapter/redis'
|
25
|
+
|
26
|
+
Remenv.adapter = Remenv::Adapter::Redis.new(
|
27
|
+
url: settings['url'],
|
28
|
+
namespace: settings['namespace']
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def with_configuration
|
34
|
+
configure!
|
35
|
+
yield
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'show [KEY]', 'Show value for KEY or all stored key/value pairs'
|
40
|
+
def show(key = nil)
|
41
|
+
with_configuration do
|
42
|
+
pairs = key.nil? ? Remenv.show : Remenv.get(key)
|
43
|
+
|
44
|
+
puts Remenv::Formatter.pretty(pairs)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
desc 'clear', 'Clear all stored key/value pairs'
|
49
|
+
def clear
|
50
|
+
with_configuration do
|
51
|
+
Remenv.clear!
|
52
|
+
|
53
|
+
if yes?('Are you sure? (y/n)')
|
54
|
+
say 'OK! Cleared', :green
|
55
|
+
else
|
56
|
+
say 'Aborted', :red
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
desc 'set KEY=VALUE', 'Store VALUE for KEY'
|
62
|
+
def set(*pairs)
|
63
|
+
with_configuration do
|
64
|
+
hash = pairs.each_with_object({}) do |pair, memo|
|
65
|
+
key, value = pair.split('=')
|
66
|
+
memo[key] = value
|
67
|
+
end
|
68
|
+
|
69
|
+
Remenv.set(hash)
|
70
|
+
|
71
|
+
say 'OK!', :green
|
72
|
+
puts Remenv::Formatter.pretty(hash)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
desc 'unset KEY', 'Remove values for KEY'
|
77
|
+
def unset(*keys)
|
78
|
+
with_configuration do
|
79
|
+
Remenv.unset(*keys)
|
80
|
+
|
81
|
+
say 'OK!', :green
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
desc 'dump FILENAME', 'Dump all stored key/value pairs to FILENAME'
|
86
|
+
def dump(filename)
|
87
|
+
with_configuration do
|
88
|
+
Remenv.dump(filename)
|
89
|
+
|
90
|
+
say "OK! Dumped to #{filename}", :green
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'load FILENAME', 'Load all key/value pairs from FILENAME'
|
95
|
+
def load(filename)
|
96
|
+
with_configuration do
|
97
|
+
if yes?('This will clear all existing variables: (y/n)')
|
98
|
+
Remenv.load(filename)
|
99
|
+
say 'OK! Loaded', :green
|
100
|
+
else
|
101
|
+
say 'Aborted', :red
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'psych'
|
2
|
+
|
3
|
+
module Remenv
|
4
|
+
module Config
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def load(filename: '.remenv.yml', stage: :development)
|
8
|
+
if File.exist?(filename)
|
9
|
+
loaded = Psych.load_file(filename)
|
10
|
+
loaded[stage] || loaded[stage.to_s]
|
11
|
+
else
|
12
|
+
{}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Remenv
|
2
|
+
module Formatter
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def pretty(hash)
|
6
|
+
hash ||= {}
|
7
|
+
max_len = hash.keys.map(&:size).max
|
8
|
+
sorted = hash.sort_by { |key, _| key }
|
9
|
+
|
10
|
+
sorted.inject('') do |buff, (key, val)|
|
11
|
+
buff << "#{key.to_s.ljust(max_len)} #{val}\n"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/remenv/io.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Remenv
|
2
|
+
class IO
|
3
|
+
attr_reader :adapter
|
4
|
+
|
5
|
+
def initialize(adapter)
|
6
|
+
@adapter = adapter
|
7
|
+
end
|
8
|
+
|
9
|
+
def dump(filename)
|
10
|
+
write(File.open(filename, 'w+'))
|
11
|
+
end
|
12
|
+
|
13
|
+
def load(filename)
|
14
|
+
adapter.clear!
|
15
|
+
|
16
|
+
File.open(filename, 'r') do |file|
|
17
|
+
read(file)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def read(handle)
|
22
|
+
pairs = handle.each_with_object({}) do |line, hash|
|
23
|
+
raw_key, raw_val = line.split(/\s*=\s*/)
|
24
|
+
|
25
|
+
next if raw_key.nil? || raw_val.nil?
|
26
|
+
|
27
|
+
key = raw_key.sub(/^export /i, '')
|
28
|
+
val = raw_val.strip.gsub(/'|"/, '')
|
29
|
+
|
30
|
+
hash[key] = val
|
31
|
+
end
|
32
|
+
|
33
|
+
adapter.set(pairs)
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(handle, leader: 'export')
|
37
|
+
handle.tap do |io|
|
38
|
+
adapter.show.each do |key, value|
|
39
|
+
io << %(#{leader} #{key}="#{value}"\n)
|
40
|
+
end
|
41
|
+
|
42
|
+
io.rewind
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
namespace :remenv do
|
2
|
+
set :remenv_file, -> { "#{shared_path}/.env" }
|
3
|
+
|
4
|
+
desc "Upload the current environment as a file"
|
5
|
+
task :sync do
|
6
|
+
on roles(:app), in: :parallel do
|
7
|
+
envfile = fetch(:remenv_file)
|
8
|
+
envio = Remenv::IO.write(stringio)
|
9
|
+
|
10
|
+
upload!(envio, envfile)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Sync and restart apps"
|
15
|
+
task sync_with_restart: ['remenv:sync', 'deploy:restart']
|
16
|
+
end
|
data/remenv.gemspec
ADDED
@@ -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 'remenv/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'remenv'
|
8
|
+
spec.version = Remenv::VERSION
|
9
|
+
spec.authors = ['Parker Selbert']
|
10
|
+
spec.email = ['parker@sorentwo.com']
|
11
|
+
spec.homepage = 'https://github.com/dscout/remenv'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
spec.summary = %q{Database backed environment management}
|
14
|
+
spec.description = %q{Database backed environment management}
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = 'remenv'
|
18
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'thor'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'capistrano'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
spec.add_development_dependency 'rspec', '3.0.0'
|
27
|
+
spec.add_development_dependency 'redis'
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'remenv/adapter/memory'
|
2
|
+
|
3
|
+
describe Remenv::Adapter::Memory do
|
4
|
+
it 'converts all keys and to strings' do
|
5
|
+
adapter = Remenv::Adapter::Memory.new
|
6
|
+
|
7
|
+
adapter.set(port: 3000)
|
8
|
+
|
9
|
+
expect(adapter.show).to have_key('port')
|
10
|
+
expect(adapter.get('port')).to eq('3000')
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'remenv/adapter/redis'
|
2
|
+
|
3
|
+
describe Remenv::Adapter::Redis do
|
4
|
+
let(:adapter) do
|
5
|
+
Remenv::Adapter::Redis.new(url: 'redis://localhost:6379/15')
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
adapter.clear!
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#show' do
|
13
|
+
it 'is an empty hash without anything stored' do
|
14
|
+
expect(adapter.show).to eq({})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#set' do
|
19
|
+
it 'stores the key and value' do
|
20
|
+
adapter.set(PORT: 3000)
|
21
|
+
|
22
|
+
expect(adapter.show).to eq('PORT' => '3000')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#get' do
|
27
|
+
it 'retrieves the value at the provided key' do
|
28
|
+
adapter.set(PORT: 3000)
|
29
|
+
|
30
|
+
expect(adapter.get('PORT')).to eq('3000')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#unset' do
|
35
|
+
it 'clears the provided keys' do
|
36
|
+
adapter.set(KEY1: 1, KEY2: 2, KEY3: 3)
|
37
|
+
adapter.unset(:KEY1, :KEY3)
|
38
|
+
|
39
|
+
expect(adapter.show).to eq('KEY2' => '2')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'remenv/config'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
describe Remenv::Config do
|
5
|
+
after do
|
6
|
+
FileUtils.rm('.remenv.yml', force: true)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'loads default settings from the current directory' do
|
10
|
+
hash = { url: 'cache.com:6329/0', namespace: 'cache-env' }
|
11
|
+
|
12
|
+
File.open('.remenv.yml', 'w') do |file|
|
13
|
+
Psych.dump({ 'development' => hash }, file)
|
14
|
+
end
|
15
|
+
|
16
|
+
expect(Remenv::Config.load).to eq(hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'loads environment specific settings when provided' do
|
20
|
+
staging = { url: 'cache.com:6329/0', namespace: 'cache-env' }
|
21
|
+
|
22
|
+
File.open('.remenv.yml', 'w') do |file|
|
23
|
+
Psych.dump({ staging: staging }, file)
|
24
|
+
end
|
25
|
+
|
26
|
+
expect(Remenv::Config.load(stage: :staging)).to eq(staging)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'falls back to an empty hash without a config' do
|
30
|
+
expect(Remenv::Config.load).to eq({})
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'remenv/formatter'
|
2
|
+
|
3
|
+
describe Remenv::Formatter do
|
4
|
+
describe '.pretty' do
|
5
|
+
it 'prettifies the supplied hash' do
|
6
|
+
formatted = Remenv::Formatter.pretty(
|
7
|
+
PORT: 3000,
|
8
|
+
URL: '/api',
|
9
|
+
LOG_LEVEL: 'info'
|
10
|
+
)
|
11
|
+
|
12
|
+
expected = <<-EOF.gsub(/^\s+/, '')
|
13
|
+
LOG_LEVEL info
|
14
|
+
PORT 3000
|
15
|
+
URL /api
|
16
|
+
EOF
|
17
|
+
|
18
|
+
expect(formatted).to eq(expected)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'does not format nil input' do
|
22
|
+
formatted = Remenv::Formatter.pretty(nil)
|
23
|
+
|
24
|
+
expect(formatted).to eq('')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'remenv/io'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
describe Remenv::IO do
|
5
|
+
let(:adapter) { Remenv::Adapter::Memory.new }
|
6
|
+
|
7
|
+
describe '#write' do
|
8
|
+
it 'writes the current configuration to an io object' do
|
9
|
+
handle = StringIO.new
|
10
|
+
io = Remenv::IO.new(adapter)
|
11
|
+
|
12
|
+
adapter.set(key1: 'a', key2: 'b')
|
13
|
+
io.write(handle, leader: 'export')
|
14
|
+
|
15
|
+
expected = <<-DATA.gsub(/^\s+/, '')
|
16
|
+
export key1="a"
|
17
|
+
export key2="b"
|
18
|
+
DATA
|
19
|
+
|
20
|
+
expect(handle.read).to eq(expected)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#read' do
|
25
|
+
it 'reads configuration from an io object' do
|
26
|
+
handle = StringIO.new("key1=a\nkey2=b\n")
|
27
|
+
|
28
|
+
io = Remenv::IO.new(adapter)
|
29
|
+
io.read(handle)
|
30
|
+
|
31
|
+
expect(adapter.show).to eq(
|
32
|
+
'key1' => 'a',
|
33
|
+
'key2' => 'b'
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'ignores export statements and whitespace' do
|
38
|
+
handle = StringIO.new(%(export key1 = a\n\n# comment\nEXPORT key2 = 'b'\nexport key3="c"))
|
39
|
+
|
40
|
+
io = Remenv::IO.new(adapter)
|
41
|
+
io.read(handle)
|
42
|
+
|
43
|
+
expect(adapter.show).to eq(
|
44
|
+
'key1' => 'a',
|
45
|
+
'key2' => 'b',
|
46
|
+
'key3' => 'c'
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#load' do
|
52
|
+
let(:filename) { 'vars.env' }
|
53
|
+
|
54
|
+
after do
|
55
|
+
FileUtils.rm(filename, force: true)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'clears any existing stored configuration' do
|
59
|
+
File.open(filename, 'w') do |file|
|
60
|
+
file << 'export key2="b"'
|
61
|
+
end
|
62
|
+
|
63
|
+
adapter.set(key1: 'a')
|
64
|
+
io = Remenv::IO.new(adapter)
|
65
|
+
io.load(filename)
|
66
|
+
|
67
|
+
expect(adapter.show).to eq('key2' => 'b')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/remenv_spec.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
describe Remenv do
|
4
|
+
after do
|
5
|
+
Remenv.adapter = nil
|
6
|
+
Remenv.clear!
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.adapter' do
|
10
|
+
it 'defaults to an in-memory adapter' do
|
11
|
+
expect(Remenv.adapter).to be_instance_of(Remenv::Adapter::Memory)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'can be overridden to use a custom adapter' do
|
15
|
+
adapter = double(:adapter)
|
16
|
+
|
17
|
+
Remenv.adapter = adapter
|
18
|
+
|
19
|
+
expect(Remenv.adapter).to be(adapter)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '.configure' do
|
24
|
+
it 'allows interaction with the module' do
|
25
|
+
adapter = double(:adapter)
|
26
|
+
|
27
|
+
Remenv.configure do |remenv|
|
28
|
+
remenv.adapter = adapter
|
29
|
+
end
|
30
|
+
|
31
|
+
expect(Remenv.adapter).to be(adapter)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '.set' do
|
36
|
+
it 'stores each key and value' do
|
37
|
+
pairs = { 'RACK_ENV' => 'production', 'PORT' => '3000' }
|
38
|
+
|
39
|
+
Remenv.set(pairs)
|
40
|
+
|
41
|
+
expect(Remenv.show).to eq(pairs)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '.unset' do
|
46
|
+
it 'clears the provided keys' do
|
47
|
+
Remenv.set(KEY1: 1, KEY2: 2, KEY3: 3)
|
48
|
+
Remenv.unset('KEY1', 'KEY3')
|
49
|
+
|
50
|
+
expect(Remenv.show).to eq('KEY2' => '2')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.get' do
|
55
|
+
it 'retrieves the value at the provided key' do
|
56
|
+
Remenv.set('PORT' => '3000')
|
57
|
+
|
58
|
+
expect(Remenv.get('PORT')).to eq('3000')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'remenv'
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.filter_run :focus
|
5
|
+
config.run_all_when_everything_filtered = true
|
6
|
+
|
7
|
+
if config.files_to_run.one?
|
8
|
+
config.formatter = 'doc' if config.formatters.none?
|
9
|
+
end
|
10
|
+
|
11
|
+
config.order = :random
|
12
|
+
Kernel.srand config.seed
|
13
|
+
|
14
|
+
config.expect_with :rspec do |expectations|
|
15
|
+
expectations.syntax = :expect
|
16
|
+
end
|
17
|
+
|
18
|
+
config.mock_with :rspec do |mocks|
|
19
|
+
mocks.syntax = :expect
|
20
|
+
mocks.verify_partial_doubles = true
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: remenv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Parker Selbert
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: capistrano
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.0.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.0.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: redis
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Database backed environment management
|
98
|
+
email:
|
99
|
+
- parker@sorentwo.com
|
100
|
+
executables:
|
101
|
+
- remenv
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- ".rspec"
|
107
|
+
- CHANGELOG.md
|
108
|
+
- Gemfile
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- bin/remenv
|
113
|
+
- lib/remenv.rb
|
114
|
+
- lib/remenv/adapter/memory.rb
|
115
|
+
- lib/remenv/adapter/redis.rb
|
116
|
+
- lib/remenv/cli.rb
|
117
|
+
- lib/remenv/config.rb
|
118
|
+
- lib/remenv/formatter.rb
|
119
|
+
- lib/remenv/io.rb
|
120
|
+
- lib/remenv/tasks/remenv.cap
|
121
|
+
- lib/remenv/version.rb
|
122
|
+
- remenv.gemspec
|
123
|
+
- spec/adapter/memory_spec.rb
|
124
|
+
- spec/adapter/redis_spec.rb
|
125
|
+
- spec/remenv/config_spec.rb
|
126
|
+
- spec/remenv/formatter_spec.rb
|
127
|
+
- spec/remenv/io_spec.rb
|
128
|
+
- spec/remenv_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
homepage: https://github.com/dscout/remenv
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.3.0
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: Database backed environment management
|
154
|
+
test_files:
|
155
|
+
- spec/adapter/memory_spec.rb
|
156
|
+
- spec/adapter/redis_spec.rb
|
157
|
+
- spec/remenv/config_spec.rb
|
158
|
+
- spec/remenv/formatter_spec.rb
|
159
|
+
- spec/remenv/io_spec.rb
|
160
|
+
- spec/remenv_spec.rb
|
161
|
+
- spec/spec_helper.rb
|