a9n 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -3
- data/README.md +1 -1
- data/lib/a9n.rb +34 -25
- data/lib/a9n/struct.rb +14 -0
- data/lib/a9n/version.rb +1 -1
- data/spec/integration/a9n_spec.rb +1 -0
- data/spec/unit/a9n_spec.rb +21 -35
- data/spec/unit/struct_spec.rb +87 -44
- data/test_app/benchmark.rb +29 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f1aa00ff577227a9d07088e0053ea48814a8816
|
4
|
+
data.tar.gz: 932f917737c5c1c6cdb6919a9b16a1127a0a1c8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e69852a57a9f29d640d4983dfec8406c7943b038d07a5b58a441461a2f2ae7df8cfc6489c3df784dcb1b698a1c66ad8aeb4519c21c9bf00218217cc6478a507
|
7
|
+
data.tar.gz: d578632bc24b83cbfcdc835f7a76f727ba850bd527134bdd08940823af9a49d3a6d9d9655401d3428e5fa115724bdbb8c45ec21caf11a47a3346ea014ff9acff
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,7 @@
|
|
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 to keep ruby/rails apps configuration maintanable and verifiable. It supports Rails 2.x, 3.x, 4.x and Ruby
|
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 2.0. 2.1, 2.2. Ruby 1.8 is not supported since version 0.1.2. Ruby 1.9 is not supported since version 0.4.0.
|
14
14
|
|
15
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).
|
16
16
|
|
data/lib/a9n.rb
CHANGED
@@ -6,6 +6,8 @@ require "yaml"
|
|
6
6
|
require "erb"
|
7
7
|
|
8
8
|
module A9n
|
9
|
+
extend SingleForwardable
|
10
|
+
|
9
11
|
class ConfigurationNotLoaded < StandardError; end
|
10
12
|
class MissingConfigurationData < StandardError; end
|
11
13
|
class MissingConfigurationVariables < StandardError; end
|
@@ -47,19 +49,6 @@ module A9n
|
|
47
49
|
ENV[name]
|
48
50
|
end
|
49
51
|
|
50
|
-
def fetch(*args)
|
51
|
-
scope(DEFAULT_SCOPE).fetch(*args)
|
52
|
-
end
|
53
|
-
|
54
|
-
def scope(name)
|
55
|
-
load unless instance_variable_defined?(var_name_for(name))
|
56
|
-
instance_variable_get(var_name_for(name))
|
57
|
-
end
|
58
|
-
|
59
|
-
def var_name_for(file)
|
60
|
-
:"@#{File.basename(file.to_s).split('.').first}"
|
61
|
-
end
|
62
|
-
|
63
52
|
def default_files
|
64
53
|
files = Dir[root.join("config/#{DEFAULT_SCOPE}.#{EXTENSION_LIST}").to_s]
|
65
54
|
files += Dir[root.join("config/a9n/*.#{EXTENSION_LIST}")]
|
@@ -67,28 +56,48 @@ module A9n
|
|
67
56
|
end
|
68
57
|
|
69
58
|
def load(*files)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
instance_variable_set(var_name_for(file), A9n::Loader.new(file, env).get)
|
77
|
-
end
|
59
|
+
files = files.empty? ? default_files : get_absolute_paths_for(files)
|
60
|
+
files.map { |file| load_file(file) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def storage
|
64
|
+
@storage ||= A9n::Struct.new
|
78
65
|
end
|
79
66
|
|
80
67
|
def method_missing(name, *args)
|
81
|
-
if
|
82
|
-
|
68
|
+
load if storage.empty?
|
69
|
+
storage.send(name)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def load_file(file)
|
75
|
+
scope_name = scope_name_from_file(file)
|
76
|
+
scope_data = A9n::Loader.new(file, env).get
|
77
|
+
setup_scope(scope_name, scope_data)
|
78
|
+
end
|
79
|
+
|
80
|
+
def setup_scope(name, data)
|
81
|
+
if default_scope?(name)
|
82
|
+
storage.merge(data)
|
83
|
+
def_delegators :storage, *data.keys
|
83
84
|
else
|
84
|
-
|
85
|
+
storage[name] = data
|
86
|
+
def_delegators :storage, name
|
85
87
|
end
|
88
|
+
return data
|
86
89
|
end
|
87
90
|
|
88
|
-
|
91
|
+
def scope_name_from_file(file)
|
92
|
+
File.basename(file.to_s).split('.').first.to_sym
|
93
|
+
end
|
89
94
|
|
90
95
|
def get_absolute_paths_for(files)
|
91
96
|
files.map { |file| Pathname.new(file).absolute? ? file : self.root.join('config', file).to_s }
|
92
97
|
end
|
98
|
+
|
99
|
+
def default_scope?(scope)
|
100
|
+
scope.to_s == DEFAULT_SCOPE.to_s
|
101
|
+
end
|
93
102
|
end
|
94
103
|
end
|
data/lib/a9n/struct.rb
CHANGED
@@ -2,6 +2,10 @@ require 'ostruct'
|
|
2
2
|
|
3
3
|
module A9n
|
4
4
|
class Struct < OpenStruct
|
5
|
+
def empty?
|
6
|
+
@table.empty?
|
7
|
+
end
|
8
|
+
|
5
9
|
def keys
|
6
10
|
@table.keys
|
7
11
|
end
|
@@ -10,6 +14,16 @@ module A9n
|
|
10
14
|
@table.fetch(name.to_sym, default)
|
11
15
|
end
|
12
16
|
|
17
|
+
def key?(key)
|
18
|
+
to_h.key?(key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def merge(key_value)
|
22
|
+
key_value.each_pair do |key, value|
|
23
|
+
self[key] = value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
13
27
|
def method_missing(name, *args)
|
14
28
|
raise NoSuchConfigurationVariable.new(name)
|
15
29
|
end
|
data/lib/a9n/version.rb
CHANGED
data/spec/unit/a9n_spec.rb
CHANGED
@@ -4,6 +4,7 @@ describe A9n do
|
|
4
4
|
subject { described_class }
|
5
5
|
|
6
6
|
after {
|
7
|
+
subject.instance_variable_set(:@storage, nil)
|
7
8
|
subject.instance_variable_set(:@env, nil)
|
8
9
|
subject.root = nil
|
9
10
|
subject.app = nil
|
@@ -105,41 +106,6 @@ describe A9n do
|
|
105
106
|
it { expect(subject.get_env_var("IS_DWARF")).to be_nil}
|
106
107
|
end
|
107
108
|
|
108
|
-
describe ".var_name_for" do
|
109
|
-
it { expect(subject.var_name_for(:configuration)).to eq(:@configuration) }
|
110
|
-
it { expect(subject.var_name_for("configuration.yml")).to eq(:@configuration) }
|
111
|
-
it { expect(subject.var_name_for("custom_dir/extra.yml")).to eq(:@extra) }
|
112
|
-
end
|
113
|
-
|
114
|
-
describe ".fetch" do
|
115
|
-
let(:example_config) { A9n::Struct.new({hello: "world"}) }
|
116
|
-
before {
|
117
|
-
expect(subject).to receive(:scope).with(subject::DEFAULT_SCOPE).twice.and_return(example_config)
|
118
|
-
}
|
119
|
-
it {
|
120
|
-
expect(subject.fetch(:hello)).to eq("world")
|
121
|
-
expect(subject.fetch(:wold)).to eq(nil)
|
122
|
-
}
|
123
|
-
end
|
124
|
-
|
125
|
-
describe ".scope" do
|
126
|
-
context "when config has been loaded" do
|
127
|
-
before {
|
128
|
-
subject.instance_variable_set(:@custom1, { api_key: '1234asdf'})
|
129
|
-
expect(subject).to receive(:load).never
|
130
|
-
}
|
131
|
-
it {
|
132
|
-
expect(subject.scope(:custom1)).to eq({ api_key: '1234asdf'})
|
133
|
-
}
|
134
|
-
end
|
135
|
-
context "when config has not been loaded yet" do
|
136
|
-
it {
|
137
|
-
expect(subject).to receive(:load)
|
138
|
-
subject.scope(:custom2)
|
139
|
-
}
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
109
|
describe ".default_files" do
|
144
110
|
before {
|
145
111
|
subject.root = File.expand_path("../../../test_app", __FILE__)
|
@@ -195,4 +161,24 @@ describe A9n do
|
|
195
161
|
}
|
196
162
|
end
|
197
163
|
end
|
164
|
+
|
165
|
+
describe ".method_missing" do
|
166
|
+
context "when storage is empty" do
|
167
|
+
before { expect(subject).to receive(:load).once }
|
168
|
+
it {
|
169
|
+
expect(subject.storage).to be_empty
|
170
|
+
expect { subject.whatever }.to raise_error(A9n::NoSuchConfigurationVariable)
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
context "when storage is not empty" do
|
175
|
+
before {
|
176
|
+
subject.storage[:whenever] = 'whenever'
|
177
|
+
expect(subject).not_to receive(:load)
|
178
|
+
}
|
179
|
+
it {
|
180
|
+
expect { subject.whatever }.to raise_error(A9n::NoSuchConfigurationVariable)
|
181
|
+
}
|
182
|
+
end
|
183
|
+
end
|
198
184
|
end
|
data/spec/unit/struct_spec.rb
CHANGED
@@ -1,66 +1,109 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe A9n::Struct do
|
4
|
-
|
5
|
-
described_class.new
|
6
|
-
non_empty_dwarf: 'dwarf',
|
7
|
-
nil_dwarf: nil,
|
8
|
-
false_dwarf: false,
|
9
|
-
true_dwarf: true,
|
10
|
-
hash_dwarf: { dwarf: 'hello' }
|
11
|
-
})
|
12
|
-
}
|
13
|
-
|
14
|
-
describe '#keys' do
|
15
|
-
subject { super().keys }
|
16
|
-
it { should == [:non_empty_dwarf, :nil_dwarf, :false_dwarf, :true_dwarf, :hash_dwarf] }
|
17
|
-
end
|
4
|
+
context "without any values" do
|
5
|
+
subject { described_class.new }
|
18
6
|
|
19
|
-
|
20
|
-
|
21
|
-
|
7
|
+
it 'is empty' do
|
8
|
+
expect(subject).to be_empty
|
9
|
+
end
|
22
10
|
|
23
|
-
|
24
|
-
|
11
|
+
it 'raises error' do
|
12
|
+
expect {
|
13
|
+
subject.dwarf
|
14
|
+
}.to raise_error(A9n::NoSuchConfigurationVariable, 'dwarf')
|
15
|
+
end
|
25
16
|
end
|
26
17
|
|
27
|
-
|
28
|
-
|
29
|
-
|
18
|
+
context "with values" do
|
19
|
+
subject {
|
20
|
+
described_class.new({
|
21
|
+
non_empty_dwarf: 'dwarf',
|
22
|
+
nil_dwarf: nil,
|
23
|
+
false_dwarf: false,
|
24
|
+
true_dwarf: true,
|
25
|
+
hash_dwarf: { dwarf: 'hello' }
|
26
|
+
})
|
27
|
+
}
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
end
|
29
|
+
describe '#keys' do
|
30
|
+
subject { super().keys }
|
34
31
|
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
it do
|
33
|
+
expect(subject).to eq [:non_empty_dwarf, :nil_dwarf, :false_dwarf, :true_dwarf, :hash_dwarf]
|
34
|
+
end
|
35
|
+
end
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
37
|
+
describe '#merge' do
|
38
|
+
before { subject.merge(argument) }
|
39
|
+
|
40
|
+
context 'hash' do
|
41
|
+
let(:argument) { { non_empty_dwarf: 'hello dwarf' } }
|
42
|
+
|
43
|
+
it do
|
44
|
+
expect(subject.non_empty_dwarf).to eq('hello dwarf')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'struct' do
|
49
|
+
let(:argument) { described_class.new(non_empty_dwarf: 'hello dwarf') }
|
44
50
|
|
45
|
-
|
46
|
-
|
47
|
-
|
51
|
+
it do
|
52
|
+
expect(subject.non_empty_dwarf).to eq('hello dwarf')
|
53
|
+
end
|
54
|
+
end
|
48
55
|
end
|
49
56
|
|
50
|
-
it '
|
51
|
-
expect(subject
|
57
|
+
it 'is not empty' do
|
58
|
+
expect(subject).not_to be_empty
|
52
59
|
end
|
53
60
|
|
54
|
-
it '
|
55
|
-
expect(subject.
|
61
|
+
it 'gets non-empty value' do
|
62
|
+
expect(subject.non_empty_dwarf).to eq('dwarf')
|
56
63
|
end
|
57
64
|
|
58
|
-
it '
|
59
|
-
expect(subject.
|
65
|
+
it 'gets nil value' do
|
66
|
+
expect(subject.nil_dwarf).to eq(nil)
|
60
67
|
end
|
61
68
|
|
62
|
-
it '
|
63
|
-
expect(subject.
|
69
|
+
it 'gets true value' do
|
70
|
+
expect(subject.true_dwarf).to eq(true)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'gets false value' do
|
74
|
+
expect(subject.false_dwarf).to eq(false)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'gets hash value' do
|
78
|
+
expect(subject.hash_dwarf).to be_kind_of(Hash)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'raises exception when value not exists' do
|
82
|
+
expect {
|
83
|
+
subject.non_existing_dwarf
|
84
|
+
}.to raise_error(A9n::NoSuchConfigurationVariable)
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '#fetch' do
|
88
|
+
it 'return non empty value' do
|
89
|
+
expect(subject.fetch(:non_empty_dwarf)).to eq('dwarf')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'return false value' do
|
93
|
+
expect(subject.fetch(:false_dwarf)).to eq(false)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'return nil value' do
|
97
|
+
expect(subject.fetch(:nil_dwarf)).to eq(nil)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'return nil for non existing value' do
|
101
|
+
expect(subject.fetch(:non_existing_dwarf)).to eq(nil)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'return default for non existing value' do
|
105
|
+
expect(subject.fetch(:non_existing_dwarf, 'default')).to eq('default')
|
106
|
+
end
|
64
107
|
end
|
65
108
|
end
|
66
109
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'benchmark'
|
4
|
+
require 'a9n'
|
5
|
+
|
6
|
+
class SampleBenchmarkApp
|
7
|
+
def run
|
8
|
+
0.upto(1_000).map do |index|
|
9
|
+
"#{index} #{::A9n.string_dwarf} #{::A9n.overriden_dwarf}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def root
|
14
|
+
Pathname.new('./test_app').expand_path
|
15
|
+
end
|
16
|
+
|
17
|
+
def env
|
18
|
+
:test
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
A9n.app = SampleBenchmarkApp.new
|
23
|
+
results = []
|
24
|
+
|
25
|
+
(1..10).each do
|
26
|
+
results << Benchmark.realtime { A9n.app.run }
|
27
|
+
end
|
28
|
+
|
29
|
+
puts (results.reduce(&:+) / 10).round(4)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: a9n
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Krzysztof Knapik
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: a9n is a tool to keep ruby/rails apps extra configuration easily maintainable
|
14
14
|
and verifiable
|
@@ -40,6 +40,7 @@ files:
|
|
40
40
|
- spec/unit/a9n_spec.rb
|
41
41
|
- spec/unit/loader_spec.rb
|
42
42
|
- spec/unit/struct_spec.rb
|
43
|
+
- test_app/benchmark.rb
|
43
44
|
- test_app/config/a9n/cloud.yml.erb
|
44
45
|
- test_app/config/a9n/mailer.yml.example
|
45
46
|
- test_app/config/a9n/mandrill.yml
|