a9n 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Gemfile +2 -2
- data/LICENSE +2 -2
- data/README.md +21 -9
- data/a9n.gemspec +2 -2
- data/lib/a9n/loader.rb +24 -18
- data/lib/a9n/version.rb +1 -1
- data/spec/spec_helper.rb +5 -10
- data/spec/unit/loader_spec.rb +51 -43
- data/test_app/config/no_defaults.yml +3 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c19e68e2155ba1e5eb4b94f13f4345cec6a6550
|
4
|
+
data.tar.gz: d49bfe9ff3a925e4117a094b190b57493acc0478
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db39e6ebef8b3cbff249bfcd43a57b42fe276886903df28e772ba5f59da862fa8e72d3656b38c98a0f85dc7117c4538dc7d88e00272047cd1473994d877637b6
|
7
|
+
data.tar.gz: 18c31604129a4785bac83c6d4d28be8225eff924cb0f1b9ae96c32ccd61fe578d3125f2504087681a05212ea0d9e6d12f45f291009353e8e5f0ac73dd245adf1
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2014 Krzysztof Knapik @knapo
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
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.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -3,14 +3,16 @@
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/a9n.png)][gem_version]
|
4
4
|
[![Build status](https://secure.travis-ci.org/knapo/a9n.png)][travis]
|
5
5
|
[![Code Climate](https://codeclimate.com/github/knapo/a9n.png)][codeclimate]
|
6
|
-
[![Coverage Status](https://
|
6
|
+
[![Coverage Status](https://codeclimate.com/github/knapo/a9n/coverage.png)][coverage]
|
7
7
|
|
8
8
|
[gem_version]: https://rubygems.org/gems/a9n
|
9
9
|
[travis]: http://travis-ci.org/knapo/a9n
|
10
10
|
[codeclimate]: https://codeclimate.com/github/knapo/a9n
|
11
|
-
[
|
11
|
+
[coverage]: https://codeclimate.com/github/knapo/a9n
|
12
12
|
|
13
|
-
A9n is a simple tool
|
13
|
+
A9n is a simple tool to keep ruby/rails apps configuration maintanable and verifiable. It supports Rails 2.x, 3.x, 4.x and Ruby 1.9, 2.0. 2.1. Ruby 1.8 is not supported since version 0.1.2.
|
14
|
+
|
15
|
+
Why it's named a9n? It's a numeronym for application (where 9 stands for the number of letters between the first **a** and last **n**, similar to i18n or l10n).
|
14
16
|
|
15
17
|
## Installation
|
16
18
|
|
@@ -26,9 +28,8 @@ Add `configuration.yml.example` and/or `configuration.yml` file into the config
|
|
26
28
|
directory. When none fo these files exists, `A9n::MissingConfigurationFile`
|
27
29
|
exception is thrown.
|
28
30
|
If both file exist, content of `configuration.yml` is validated. It means that
|
29
|
-
all keys existing in example file must exist in
|
30
|
-
keys`A9n::MissingConfigurationVariables` is thrown with
|
31
|
-
missing keys.
|
31
|
+
all keys existing in example file must exist in local file - in case of missing
|
32
|
+
keys `A9n::MissingConfigurationVariables` is thrown with the explanation what is missing.
|
32
33
|
|
33
34
|
Set application root and load configuration by adding to your `application.rb` or `environment.rb` right
|
34
35
|
after budler requires:
|
@@ -36,6 +37,7 @@ after budler requires:
|
|
36
37
|
A9n.root = File.expand_path('../..', __FILE__)
|
37
38
|
A9n.load
|
38
39
|
|
40
|
+
This step is not required ,if you don't use `a9n` in the environment settings or initializers.
|
39
41
|
It works with `Rails` by default. If you want to use `A9n` with non-rails app
|
40
42
|
you may need to tell that to A9n by:
|
41
43
|
|
@@ -60,19 +62,29 @@ is accessible by:
|
|
60
62
|
|
61
63
|
## Custom and multiple configuration files
|
62
64
|
|
63
|
-
If you want to
|
65
|
+
If you want to split configuration, you can use multiple files. All files from `config/a9n` are loaded by default, but you may pass custom paths as an argument to `A9n.load` e.g. `A9n.load('config/facebook.yml', 'config/mongoid.yml')`. In such cases config items are accessible through the scope consistent with the file name.
|
64
66
|
|
65
67
|
E.g. if you have `config/a9n/mandrill.yml`:
|
66
|
-
|
68
|
+
|
67
69
|
defaults:
|
68
70
|
username: "joe"
|
69
71
|
api_key: "1234asdf"
|
70
72
|
|
71
|
-
|
73
|
+
You can access it by:
|
72
74
|
|
73
75
|
A9n.mandrill.username # => `joe`
|
74
76
|
A9n.mandrill.api_key # => `1234asdf`
|
75
77
|
|
78
|
+
|
79
|
+
## Capistrano
|
80
|
+
|
81
|
+
If you use capistrano and you feel safe enough to keep all your instance ( staging, production) configuration in the repository, you may find it useful to use capistrano extensions.
|
82
|
+
Just add an instance configuration file e.g. `configuration.yml.staging`, `configuration.yml.production` (NOTE: file extension must be consistent with the capistrano stage) and add
|
83
|
+
|
84
|
+
require 'a9n/capistrano'
|
85
|
+
|
86
|
+
to your deploy.rb file. This way `configuration.yml.<stage>` overrides `configuration.yml` on each deploy.
|
87
|
+
|
76
88
|
## Contributing
|
77
89
|
|
78
90
|
1. Fork it
|
data/a9n.gemspec
CHANGED
@@ -4,8 +4,8 @@ require File.expand_path('../lib/a9n/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Krzysztof Knapik"]
|
6
6
|
gem.email = ["knapo@knapo.net"]
|
7
|
-
gem.description = %q{
|
8
|
-
gem.summary = %q{a9n is a
|
7
|
+
gem.description = %q{a9n is a tool to keep ruby/rails apps extra configuration easily maintainable and verifiable}
|
8
|
+
gem.summary = %q{a9n is a tool to keep ruby/rails apps extra configuration easily maintainable and verifiable}
|
9
9
|
gem.homepage = "https://github.com/knapo/a9n"
|
10
10
|
gem.license = 'MIT'
|
11
11
|
gem.files = `git ls-files`.split($\)
|
data/lib/a9n/loader.rb
CHANGED
@@ -4,6 +4,8 @@ module A9n
|
|
4
4
|
class Loader
|
5
5
|
attr_reader :env, :local_file, :example_file
|
6
6
|
|
7
|
+
COMMON_SCOPE = "defaults"
|
8
|
+
|
7
9
|
def initialize(file_path, env)
|
8
10
|
@env = env.to_s
|
9
11
|
@local_file = file_path
|
@@ -15,33 +17,37 @@ module A9n
|
|
15
17
|
end
|
16
18
|
|
17
19
|
def load
|
18
|
-
|
19
|
-
|
20
|
-
default_example = self.class.load_yml(example_file, "defaults")
|
21
|
-
default_local = self.class.load_yml(local_file, "defaults")
|
20
|
+
local_config = self.class.load_yml(local_file, env)
|
21
|
+
example_config = self.class.load_yml(example_file, env)
|
22
22
|
|
23
|
-
if
|
23
|
+
if local_config.nil? && example_config.nil?
|
24
24
|
raise A9n::MissingConfigurationData.new("Configuration data for *#{env}* env was not found in neither *#{example_file}* nor *#{local_file}*")
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
if !example.nil? && !local.nil?
|
31
|
-
verify!(example, local)
|
27
|
+
if !local_config.nil? && !example_config.nil?
|
28
|
+
verify!(local_config, example_config)
|
32
29
|
end
|
33
30
|
|
34
|
-
@struct = A9n::Struct.new(
|
31
|
+
@struct = A9n::Struct.new(local_config || example_config)
|
35
32
|
end
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
class << self
|
35
|
+
def load_yml(file_path, env)
|
36
|
+
return nil unless File.exists?(file_path)
|
37
|
+
yml = YAML.load(ERB.new(File.read(file_path)).result)
|
38
|
+
|
39
|
+
common_scope = prepare_yml_scope(yml, COMMON_SCOPE)
|
40
|
+
env_scope = prepare_yml_scope(yml, env)
|
41
|
+
|
42
|
+
A9n::HashExt.merge(common_scope, env_scope)
|
43
|
+
end
|
40
44
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
def prepare_yml_scope(yml, env_scope)
|
46
|
+
if yml[env_scope].is_a?(::Hash)
|
47
|
+
A9n::HashExt.deep_symbolize_keys(yml[env_scope])
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
data/lib/a9n/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,7 @@
|
|
1
1
|
require 'simplecov'
|
2
|
-
require '
|
2
|
+
require 'codeclimate-test-reporter'
|
3
3
|
|
4
|
-
|
5
|
-
SimpleCov::Formatter::HTMLFormatter,
|
6
|
-
Coveralls::SimpleCov::Formatter
|
7
|
-
]
|
8
|
-
SimpleCov.start do
|
9
|
-
add_filter '/spec/'
|
10
|
-
end
|
4
|
+
CodeClimate::TestReporter.start
|
11
5
|
|
12
6
|
require 'rubygems'
|
13
7
|
require 'bundler/setup'
|
@@ -15,8 +9,9 @@ require 'bundler/setup'
|
|
15
9
|
require 'a9n'
|
16
10
|
|
17
11
|
RSpec.configure do |config|
|
18
|
-
config.
|
12
|
+
config.expect_with :rspec do |expect_with|
|
13
|
+
expect_with.syntax = :expect
|
14
|
+
end
|
19
15
|
config.order = "random"
|
20
|
-
config.color_enabled = true
|
21
16
|
config.tty = true
|
22
17
|
end
|
data/spec/unit/loader_spec.rb
CHANGED
@@ -13,18 +13,12 @@ describe A9n::Loader do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
describe "#load" do
|
16
|
-
let(:
|
17
|
-
{ app_url: "http://127.0.0.1:3000", api_key: "
|
16
|
+
let(:example_config) {
|
17
|
+
{ app_url: "http://127.0.0.1:3000", api_key: "example1234" }
|
18
18
|
}
|
19
|
-
let(:
|
19
|
+
let(:local_config) {
|
20
20
|
{ app_host: "127.0.0.1:3000", api_key: "local1234" }
|
21
21
|
}
|
22
|
-
let(:example_default_config) {
|
23
|
-
{ page_title: "Base Kielbasa", api_key: "example1234default" }
|
24
|
-
}
|
25
|
-
let(:local_default_config) {
|
26
|
-
{ page_title: "Local Kielbasa", api_key: "local1234default" }
|
27
|
-
}
|
28
22
|
let(:env){
|
29
23
|
"tropical"
|
30
24
|
}
|
@@ -34,8 +28,6 @@ describe A9n::Loader do
|
|
34
28
|
before do
|
35
29
|
expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(nil)
|
36
30
|
expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(nil)
|
37
|
-
expect(described_class).to receive(:load_yml).with(subject.example_file, "defaults").and_return(nil)
|
38
|
-
expect(described_class).to receive(:load_yml).with(subject.local_file, "defaults").and_return(nil)
|
39
31
|
expect(subject).to receive(:verify!).never
|
40
32
|
end
|
41
33
|
it "raises expection" do
|
@@ -45,37 +37,30 @@ describe A9n::Loader do
|
|
45
37
|
end
|
46
38
|
end
|
47
39
|
|
48
|
-
context "when example configuration file exists
|
40
|
+
context "when only example configuration file exists" do
|
49
41
|
before do
|
50
|
-
expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(
|
42
|
+
expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(example_config)
|
51
43
|
expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(nil)
|
52
|
-
expect(described_class).to receive(:load_yml).with(subject.example_file, "defaults").and_return(example_default_config)
|
53
|
-
expect(described_class).to receive(:load_yml).with(subject.local_file, "defaults").and_return(nil)
|
54
|
-
|
55
44
|
expect(described_class).to receive(:verify!).never
|
56
45
|
subject.load
|
57
46
|
end
|
58
47
|
|
59
48
|
it { expect(config.app_url).to eq("http://127.0.0.1:3000") }
|
60
|
-
it { expect(config.
|
61
|
-
it { expect(config.api_key).to eq("base1234") }
|
49
|
+
it { expect(config.api_key).to eq("example1234") }
|
62
50
|
|
63
51
|
it {
|
64
52
|
expect { config.app_host }.to raise_error(A9n::NoSuchConfigurationVariable)
|
65
53
|
}
|
66
54
|
end
|
67
55
|
|
68
|
-
context "when local configuration file exists
|
56
|
+
context "when only local configuration file exists" do
|
69
57
|
before do
|
70
58
|
expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(nil)
|
71
|
-
expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(
|
72
|
-
expect(described_class).to receive(:load_yml).with(subject.example_file, "defaults").and_return(nil)
|
73
|
-
expect(described_class).to receive(:load_yml).with(subject.local_file, "defaults").and_return(local_default_config)
|
59
|
+
expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(local_config)
|
74
60
|
expect(described_class).to receive(:verify!).never
|
75
61
|
subject.load
|
76
62
|
end
|
77
63
|
it { expect(config.app_host).to eq("127.0.0.1:3000") }
|
78
|
-
it { expect(config.page_title).to eq("Local Kielbasa") }
|
79
64
|
it { expect(config.api_key).to eq("local1234") }
|
80
65
|
|
81
66
|
it {
|
@@ -86,19 +71,14 @@ describe A9n::Loader do
|
|
86
71
|
context "when both local and base configuration file exists without defaults" do
|
87
72
|
context "with same data" do
|
88
73
|
before do
|
89
|
-
expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(
|
90
|
-
expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(
|
91
|
-
expect(described_class).to receive(:load_yml).with(subject.example_file, "defaults").and_return(nil)
|
92
|
-
expect(described_class).to receive(:load_yml).with(subject.local_file, "defaults").and_return(nil)
|
74
|
+
expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(example_config)
|
75
|
+
expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(example_config)
|
93
76
|
subject.load
|
94
77
|
end
|
95
78
|
|
96
79
|
it { expect(config.app_url).to eq("http://127.0.0.1:3000") }
|
97
|
-
it { expect(config.api_key).to eq("
|
80
|
+
it { expect(config.api_key).to eq("example1234") }
|
98
81
|
|
99
|
-
it {
|
100
|
-
expect { config.page_title }.to raise_error(A9n::NoSuchConfigurationVariable)
|
101
|
-
}
|
102
82
|
it {
|
103
83
|
expect { config.app_host }.to raise_error(A9n::NoSuchConfigurationVariable)
|
104
84
|
}
|
@@ -106,10 +86,8 @@ describe A9n::Loader do
|
|
106
86
|
|
107
87
|
context "with different data" do
|
108
88
|
before do
|
109
|
-
expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(
|
110
|
-
expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(
|
111
|
-
expect(described_class).to receive(:load_yml).with(subject.example_file, "defaults").and_return(nil)
|
112
|
-
expect(described_class).to receive(:load_yml).with(subject.local_file, "defaults").and_return(nil)
|
89
|
+
expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(example_config)
|
90
|
+
expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(local_config)
|
113
91
|
end
|
114
92
|
it "raises expection" do
|
115
93
|
expect {
|
@@ -125,22 +103,31 @@ describe A9n::Loader do
|
|
125
103
|
subject { described_class.load_yml(file_path, env) }
|
126
104
|
|
127
105
|
context "when file not exists" do
|
128
|
-
let(:file_path) { "
|
106
|
+
let(:file_path) { "file_not_existing_in_the_universe.yml" }
|
129
107
|
|
130
108
|
it{ expect(subject).to be_nil }
|
131
109
|
end
|
132
110
|
|
133
111
|
context "when file exists" do
|
134
|
-
|
112
|
+
shared_examples "non-empty config file" do
|
113
|
+
it "returns non-empty hash" do
|
114
|
+
expect(subject).to be_kind_of(Hash)
|
115
|
+
expect(subject.keys).to_not be_empty
|
116
|
+
end
|
117
|
+
end
|
135
118
|
|
136
119
|
before {
|
137
120
|
ENV["DWARF"] = "erbized dwarf"
|
138
121
|
}
|
139
122
|
|
140
|
-
context "and
|
141
|
-
|
142
|
-
|
143
|
-
|
123
|
+
context "having env and defaults data" do
|
124
|
+
let(:file_path) { File.join(root, "config/configuration.yml") }
|
125
|
+
|
126
|
+
it_behaves_like "non-empty config file"
|
127
|
+
|
128
|
+
it "contains keys from defaults scope" do
|
129
|
+
expect(subject[:default_dwarf]).to eq("default dwarf")
|
130
|
+
expect(subject[:overriden_dwarf]).to eq("already overriden dwarf")
|
144
131
|
end
|
145
132
|
|
146
133
|
it "has symbolized keys" do
|
@@ -154,9 +141,30 @@ describe A9n::Loader do
|
|
154
141
|
end
|
155
142
|
end
|
156
143
|
|
157
|
-
context "and
|
144
|
+
context "having no env and only defaults data" do
|
145
|
+
let(:file_path) { File.join(root, "config/configuration.yml") }
|
158
146
|
let(:env) { "production" }
|
159
|
-
|
147
|
+
|
148
|
+
it_behaves_like "non-empty config file"
|
149
|
+
|
150
|
+
it "contains keys from defaults scope" do
|
151
|
+
expect(subject[:default_dwarf]).to eq("default dwarf")
|
152
|
+
expect(subject[:overriden_dwarf]).to eq("not yet overriden dwarf")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "having only env and no default data" do
|
157
|
+
let(:file_path) { File.join(root, "config/no_defaults.yml") }
|
158
|
+
|
159
|
+
context "valid env" do
|
160
|
+
let(:env) { "production" }
|
161
|
+
it_behaves_like "non-empty config file"
|
162
|
+
end
|
163
|
+
|
164
|
+
context "invalid env" do
|
165
|
+
let(:env) { "tropical" }
|
166
|
+
it { expect(subject).to be_nil }
|
167
|
+
end
|
160
168
|
end
|
161
169
|
end
|
162
170
|
end
|
metadata
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: a9n
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Krzysztof Knapik
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: a9n is a tool to keep ruby/rails apps extra configuration easily maintainable
|
14
|
+
and verifiable
|
14
15
|
email:
|
15
16
|
- knapo@knapo.net
|
16
17
|
executables: []
|
@@ -39,6 +40,7 @@ files:
|
|
39
40
|
- test_app/config/a9n/mandrill.yml
|
40
41
|
- test_app/config/configuration.yml
|
41
42
|
- test_app/config/configuration.yml.example
|
43
|
+
- test_app/config/no_defaults.yml
|
42
44
|
homepage: https://github.com/knapo/a9n
|
43
45
|
licenses:
|
44
46
|
- MIT
|
@@ -62,7 +64,8 @@ rubyforge_project:
|
|
62
64
|
rubygems_version: 2.2.2
|
63
65
|
signing_key:
|
64
66
|
specification_version: 4
|
65
|
-
summary: a9n is a
|
67
|
+
summary: a9n is a tool to keep ruby/rails apps extra configuration easily maintainable
|
68
|
+
and verifiable
|
66
69
|
test_files:
|
67
70
|
- spec/integration/a9n_spec.rb
|
68
71
|
- spec/spec_helper.rb
|