a9n 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1278accbf3b20ef8dcf2084995996dbaa1bdabda
4
- data.tar.gz: d444fa5ca535f636c4d0eefb03895d3bf12813da
3
+ metadata.gz: e1fa842e61082deb4d82eb3059efed6cb0ce0e50
4
+ data.tar.gz: a3250f33bf6df0ae3b06de1bb30774acf958d65f
5
5
  SHA512:
6
- metadata.gz: 71e3c6444b5418cf69547a9e81cd88dcefd6d346648268de14f2adf045f0920100865041cd0f7637c25f5d2fe376036b39c4b3be24df2ddb8729531bcee02d1b
7
- data.tar.gz: 5b924cb72e0a6d6813056ab6ef43e1b3a07ac4eb5c3615ae74c26834b3bc8ce0938fabbe0d07eeeaf06a23b94708255cf8ad192114405d40c00458a7bd345090
6
+ metadata.gz: ca996bb79d7331734d27e51eb5b35c1759a95bc680056ca6a25c78ec138d2c6c2272e613cb2bbd18aa4cbb01155ef981d8fa4719f909239abfb3c15c273e9b2a
7
+ data.tar.gz: d9e4c62825e16d882d3f9dadb56e4689e64b7036a47b77557208ff9c89a2f5798992fde85eb77fbe26c97d310c35dfd8967b627135497fd18e0f6a1ad0e37841
data/README.md CHANGED
@@ -10,8 +10,7 @@
10
10
  [codeclimate]: https://codeclimate.com/github/knapo/a9n
11
11
  [coveralls]: https://coveralls.io/r/knapo/a9n
12
12
 
13
- Simple tool for managing extra configuration in ruby/rails apps. Supports Rails 2.x, 3.x, 4.x and Ruby 1.9, 2.0.
14
- Ruby 1.8 is not supported in version 0.1.2 and higher.
13
+ A9n is a simple tool for managing extra configuration in ruby/rails apps. 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.
15
14
 
16
15
  ## Installation
17
16
 
@@ -37,15 +36,14 @@ after budler requires:
37
36
  A9n.root = File.expand_path('../..', __FILE__)
38
37
  A9n.load
39
38
 
40
-
41
39
  It works with `Rails` by default. If you want to use `A9n` with non-rails app
42
- you may need to tell it A9n:
40
+ you may need to tell that to A9n by:
43
41
 
44
42
  A9n.local_app = MyApp
45
43
 
46
44
  ## Usage
47
45
 
48
- You can access any variable defined in configuration files but delegating it to
46
+ You can access any variable defined in configuration files by delegating it to
49
47
  `A9n`. E.g:
50
48
 
51
49
  defaults:
@@ -58,21 +56,22 @@ You can access any variable defined in configuration files but delegating it to
58
56
  is accessible by:
59
57
 
60
58
  A9n.app_host # => `knapo.net` in production and `localhost:3000` in development
61
- A9n.email_from # => `no-reply@knapo.net` in all envs
59
+ A9n.email_from # => `no-reply@knapo.net` in both envs
62
60
 
63
61
  ## Custom and multiple configuration files
64
62
 
65
- If you need to load config from custom files (e.g `config/mongo.yml`, `config/other.yml` and `config/custom_dir/extra.yml`), add:
66
-
67
- A9n.load('mongo.yml', 'other.yml', 'custom_dir/extra')
63
+ If you want to scope configuration you may split to 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('lib/facebook/api.yml', 'config/mongoid.yml')`. In such cases config items are accessialbe via scope consistient with the file name.
68
64
 
69
- and the configuration is availble under `mongo`, `other` and `extra` scopes:
65
+ E.g. if you have `config/a9n/mandrill.yml`:
66
+
67
+ defaults:
68
+ username: "joe"
69
+ api_key: "1234asdf"
70
70
 
71
- A9n.mongo.varname
71
+ you can access it by:
72
72
 
73
- A9n.other.varname
74
-
75
- A9n.extra.varname
73
+ A9n.mandrill.username # => `joe`
74
+ A9n.mandrill.api_key # => `1234asdf`
76
75
 
77
76
  ## Contributing
78
77
 
@@ -82,7 +81,3 @@ and the configuration is availble under `mongo`, `other` and `extra` scopes:
82
81
  4. Push to the branch (`git push origin my-new-feature`)
83
82
  5. Create new Pull Request
84
83
 
85
- ### Contributors
86
-
87
- * [Grzegorz Świrski](https://github.com/sognat)
88
- * [Jakub Łopusiński](https://github.com/siemakuba)
data/lib/a9n.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "a9n/version"
2
2
  require "a9n/struct"
3
- require "a9n/core_ext/hash"
3
+ require "a9n/ext/hash"
4
+ require "a9n/loader"
4
5
  require "yaml"
5
6
  require "erb"
6
7
 
@@ -15,82 +16,63 @@ module A9n
15
16
 
16
17
  class << self
17
18
  def env
18
- @env ||= local_app_env || get_env_var("RAILS_ENV") || get_env_var("RACK_ENV") || get_env_var("APP_ENV")
19
+ @env ||= app_env || get_env_var("RAILS_ENV") || get_env_var("RACK_ENV") || get_env_var("APP_ENV")
19
20
  end
20
21
 
21
- def local_app_env
22
- local_app.env if local_app && local_app.respond_to?(:env)
22
+ def app_env
23
+ app.env if app && app.respond_to?(:env)
23
24
  end
24
25
 
25
- def local_app
26
- @local_app ||= get_rails
26
+ def app
27
+ @app ||= get_rails
27
28
  end
28
29
 
29
- def local_app=(local_app)
30
- @local_app = local_app
30
+ def app=(app_instance)
31
+ @app = app_instance
31
32
  end
32
33
 
33
34
  def root
34
- @root ||= local_app.root
35
+ @root ||= app.root
35
36
  end
36
37
 
37
38
  def root=(path)
38
- path = path.to_s
39
- @root = path.empty? ? nil : Pathname.new(path.to_s)
39
+ @root = path.to_s.empty? ? nil : Pathname.new(path.to_s)
40
40
  end
41
41
 
42
- def scope(name)
43
- instance_variable_get(var_name_for(name)) || (name == DEFAULT_SCOPE && load.first)
42
+ def get_rails
43
+ defined?(Rails) ? Rails : nil
44
44
  end
45
45
 
46
- def load(*files)
47
- files = [DEFAULT_FILE] if files.empty?
48
- files.map do |file|
49
- default_and_env_config = load_config(file)
50
-
51
- instance_variable_set(var_name_for(file), A9n::Struct.new(default_and_env_config))
52
- end
46
+ def get_env_var(name)
47
+ ENV[name]
53
48
  end
54
49
 
55
- def load_config(file)
56
- env_example = load_yml("config/#{file}.example", env)
57
- env_local = load_yml("config/#{file}", env)
58
- default_example = load_yml("config/#{file}.example", "defaults")
59
- default_local = load_yml("config/#{file}", "defaults")
60
-
61
- if env_example.nil? && env_local.nil? && default_example.nil? && default_local.nil?
62
- raise MissingConfigurationData.new("Configuration data was not found in neither config/#{file}.example nor config/#{file}")
63
- end
64
-
65
- example = Hash.merge(default_example, env_example)
66
- local = Hash.merge( default_local,env_local)
67
-
68
- if !example.nil? && !local.nil?
69
- verify!(example, local)
70
- end
71
-
72
- local || example
50
+ def fetch(*args)
51
+ scope(DEFAULT_SCOPE).fetch(*args)
73
52
  end
74
53
 
75
- def load_yml(file, env)
76
- path = File.join(self.root, file)
77
- return nil unless File.exists?(path)
78
- yml = YAML.load(ERB.new(File.read(path)).result)
54
+ def scope(name)
55
+ load unless instance_variable_defined?(var_name_for(name))
56
+ instance_variable_get(var_name_for(name))
57
+ end
79
58
 
80
- if yml[env].is_a?(Hash)
81
- return yml[env].deep_symbolize_keys
82
- else
83
- return nil
84
- end
59
+ def var_name_for(file)
60
+ :"@#{File.basename(file.to_s, '.*')}"
85
61
  end
86
62
 
87
- # Fixes rspec issue
88
- def to_ary
89
- nil
63
+ def default_files
64
+ [root.join("config/#{DEFAULT_SCOPE}.yml").to_s] + Dir[root.join("config/a9n/*.yml")]
90
65
  end
91
66
 
92
- def fetch(*args)
93
- scope(DEFAULT_SCOPE).fetch(*args)
67
+ def load(*files)
68
+ if files.empty?
69
+ files = default_files
70
+ else
71
+ files = get_absolute_paths_for(files)
72
+ end
73
+ files.map do |file|
74
+ instance_variable_set(var_name_for(file), A9n::Loader.new(file, env).get)
75
+ end
94
76
  end
95
77
 
96
78
  def method_missing(name, *args)
@@ -101,25 +83,10 @@ module A9n
101
83
  end
102
84
  end
103
85
 
104
- def get_rails
105
- defined?(Rails) ? Rails : nil
106
- end
107
-
108
- def get_env_var(name)
109
- ENV[name]
110
- end
111
-
112
- def var_name_for(file)
113
- :"@#{file.to_s.split('/').last.split('.').first}"
114
- end
115
-
116
86
  private
117
87
 
118
- def verify!(example, local)
119
- missing_keys = example.keys - local.keys
120
- if missing_keys.any?
121
- raise MissingConfigurationVariables.new("Following variables are missing in your configuration file: #{missing_keys.join(",")}")
122
- end
88
+ def get_absolute_paths_for(files)
89
+ files.map { |file| Pathname.new(file).absolute? ? file : self.root.join('config', file).to_s }
123
90
  end
124
91
  end
125
92
  end
@@ -0,0 +1,21 @@
1
+ module A9n
2
+ class HashExt
3
+ class << self
4
+ # Hash#deep_symbolize_keys
5
+ # based on
6
+ # https://github.com/svenfuchs/i18n/blob/master/lib/i18n/core_ext/hash.rb
7
+ def deep_symbolize_keys(hash)
8
+ hash.inject({}) { |result, (key, value)|
9
+ value = deep_symbolize_keys(value) if value.is_a?(::Hash)
10
+ result[(key.to_sym rescue key) || key] = value
11
+ result
12
+ }
13
+ end
14
+
15
+ def merge(*items)
16
+ return nil if items.compact.empty?
17
+ items.compact.inject({}){|sum, item| sum.merge!(item)}
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,57 @@
1
+ require 'ostruct'
2
+
3
+ module A9n
4
+ class Loader
5
+ attr_reader :env, :local_file, :example_file
6
+
7
+ def initialize(file_path, env)
8
+ @env = env.to_s
9
+ @local_file = file_path
10
+ @example_file = "#{file_path}.example"
11
+ end
12
+
13
+ def get
14
+ @struct ||= load
15
+ end
16
+
17
+ def load
18
+ env_example = self.class.load_yml(example_file, env)
19
+ env_local = self.class.load_yml(local_file, env)
20
+ default_example = self.class.load_yml(example_file, "defaults")
21
+ default_local = self.class.load_yml(local_file, "defaults")
22
+
23
+ if env_example.nil? && env_local.nil? && default_example.nil? && default_local.nil?
24
+ raise A9n::MissingConfigurationData.new("Configuration data for *#{env}* env was not found in neither *#{example_file}* nor *#{local_file}*")
25
+ end
26
+
27
+ example = A9n::HashExt.merge(default_example, env_example)
28
+ local = A9n::HashExt.merge(default_local, env_local)
29
+
30
+ if !example.nil? && !local.nil?
31
+ verify!(example, local)
32
+ end
33
+
34
+ @struct = A9n::Struct.new(local || example)
35
+ end
36
+
37
+ def self.load_yml(file_path, env)
38
+ return nil unless File.exists?(file_path)
39
+ yml = YAML.load(ERB.new(File.read(file_path)).result)
40
+
41
+ if yml[env].is_a?(::Hash)
42
+ A9n::HashExt.deep_symbolize_keys(yml[env])
43
+ else
44
+ return nil
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def verify!(example, local)
51
+ missing_keys = example.keys - local.keys
52
+ if missing_keys.any?
53
+ raise A9n::MissingConfigurationVariables.new("Following variables are missing in #{local_file} file: #{missing_keys.join(",")}")
54
+ end
55
+ end
56
+ end
57
+ end
@@ -7,7 +7,7 @@ module A9n
7
7
  end
8
8
 
9
9
  def fetch(name, default = nil)
10
- @table[name.to_sym] || default
10
+ @table.fetch(name.to_sym, default)
11
11
  end
12
12
 
13
13
  def method_missing(name, *args)
@@ -1,3 +1,3 @@
1
1
  module A9n
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+
3
+ describe A9n do
4
+ subject { described_class }
5
+
6
+ let(:env) { "test" }
7
+
8
+ before {
9
+ subject.app = double(env: env)
10
+ subject.root = File.expand_path("../../../test_app", __FILE__)
11
+ }
12
+
13
+ after {
14
+ subject.instance_variable_set(:@env, nil)
15
+ subject.root = nil
16
+ subject.app = nil
17
+ }
18
+
19
+ context "base config file" do
20
+ it {
21
+ expect(subject.default_dwarf).to eq("default dwarf")
22
+ expect(subject.overriden_dwarf).to eq("already overriden dwarf")
23
+ }
24
+ end
25
+
26
+ context "undefined env" do
27
+ let(:env) { "tropical" }
28
+ it {
29
+ expect(subject.default_dwarf).to eq("default dwarf")
30
+ expect(subject.overriden_dwarf).to eq("not yet overriden dwarf")
31
+ }
32
+ end
33
+
34
+ context "extra config file" do
35
+ it {
36
+ expect(subject.mandrill).to be_kind_of(A9n::Struct)
37
+ expect(subject.mandrill.username).to eq("joe")
38
+ expect(subject.mandrill.api_key).to eq("asdf1234")
39
+ }
40
+ end
41
+ end
@@ -15,7 +15,8 @@ require 'bundler/setup'
15
15
  require 'a9n'
16
16
 
17
17
  RSpec.configure do |config|
18
+ config.treat_symbols_as_metadata_keys_with_true_values = true
18
19
  config.order = "random"
19
20
  config.color_enabled = true
20
21
  config.tty = true
21
- end
22
+ end
@@ -0,0 +1,198 @@
1
+ require "spec_helper"
2
+
3
+ describe A9n do
4
+ subject { described_class }
5
+
6
+ after {
7
+ subject.instance_variable_set(:@env, nil)
8
+ subject.root = nil
9
+ subject.app = nil
10
+ }
11
+
12
+ describe ".env" do
13
+ before {
14
+ subject.instance_variable_set(:@env, nil)
15
+ }
16
+
17
+ context "app_env is set" do
18
+ before {
19
+ expect(subject).to receive(:app).and_return(double(env: "dwarf_env")).exactly(3).times
20
+ expect(subject).to receive(:get_env_var).never
21
+ }
22
+
23
+ it { expect(subject.env).to eq("dwarf_env") }
24
+ end
25
+
26
+ context "when APP_ENV is set" do
27
+ before {
28
+ expect(subject).to receive(:app_env).and_return(nil)
29
+ expect(subject).to receive(:get_env_var).with("RAILS_ENV").and_return(nil)
30
+ expect(subject).to receive(:get_env_var).with("RACK_ENV").and_return(nil)
31
+ expect(subject).to receive(:get_env_var).with("APP_ENV").and_return("dwarf_env")
32
+ }
33
+
34
+ it { expect(subject.env).to eq("dwarf_env") }
35
+ end
36
+ end
37
+
38
+ describe ".app" do
39
+ context "when rails not found" do
40
+ before {
41
+ expect(subject).to receive(:get_rails).and_return(nil)
42
+ }
43
+ specify {
44
+ expect(subject.app).to be_nil
45
+ }
46
+ end
47
+
48
+ context "when rails app is being used" do
49
+ let(:app) { double(env: "test", root: "/apps/a9n") }
50
+ before {
51
+ expect(subject).to receive(:get_rails).and_return(app)
52
+ }
53
+
54
+ specify { expect(subject.app).to eq(app) }
55
+ end
56
+
57
+ context "when custom non-rails app is being used" do
58
+ let(:app) { double(env: "test", root: "/apps/a9n") }
59
+ before { subject.app = app }
60
+
61
+ specify { expect(subject.app).to eq(app) }
62
+ end
63
+ end
64
+
65
+ describe ".root" do
66
+ let(:app) { double(env: "test", root: "/apps/a9n") }
67
+ before { subject.app = app }
68
+
69
+ context "with custom path" do
70
+ before {
71
+ subject.root = "/home/knapo/workspace/a9n"
72
+ }
73
+ specify {
74
+ expect(subject.root).to eq(Pathname.new("/home/knapo/workspace/a9n"))
75
+ }
76
+ end
77
+
78
+ context "with local app path" do
79
+ specify {
80
+ expect(subject.root).to eq("/apps/a9n")
81
+ }
82
+ end
83
+ end
84
+
85
+ describe ".get_rails" do
86
+ context "when defined" do
87
+ before {
88
+ Object.const_set(:Rails, Module.new)
89
+ }
90
+ after {
91
+ Object.send(:remove_const, :Rails)
92
+ }
93
+ it {
94
+ expect(subject.get_rails).to be_kind_of(Module)
95
+ }
96
+ end
97
+ context "when not defined" do
98
+ it { expect(subject.get_rails).to be_nil }
99
+ end
100
+ end
101
+
102
+ describe ".get_env_var" do
103
+ before { ENV["DWARF"] = "little dwarf" }
104
+ it { expect(subject.get_env_var("DWARF")).to eq("little dwarf")}
105
+ it { expect(subject.get_env_var("IS_DWARF")).to be_nil}
106
+ end
107
+
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
+ describe ".default_files" do
144
+ before {
145
+ subject.root = File.expand_path("../../../test_app", __FILE__)
146
+ }
147
+ it {
148
+ expect(subject.default_files[0]).to include("configuration.yml")
149
+ expect(Pathname.new(subject.default_files[0])).to be_absolute
150
+ expect(subject.default_files[1]).to include("a9n/mandrill.yml")
151
+ expect(Pathname.new(subject.default_files[1])).to be_absolute
152
+ }
153
+ end
154
+
155
+ describe ".load" do
156
+ before {
157
+ expect(described_class).to receive(:env).exactly(2).times.and_return("dev")
158
+ subject.root = "/apps/test_app"
159
+ files.each do |f, cfg|
160
+ expect(A9n::Loader).to receive(:new).with(f, "dev").and_return(double(get: cfg))
161
+ end
162
+ }
163
+ context "when no files given" do
164
+ let(:files) {
165
+ {
166
+ "/apps/test_app/config/file1.yml" => { host: "host1.com" },
167
+ "/apps/test_app/config/dir/file2.yml" => { host: "host2.com" }
168
+ }
169
+ }
170
+ before {
171
+ expect(subject).to receive(:default_files).and_return(files.keys)
172
+ expect(subject).to receive(:get_absolute_paths_for).never
173
+ }
174
+ it {
175
+ expect(subject.load).to eq(files.values)
176
+ }
177
+ end
178
+
179
+ context "when custom files given" do
180
+ let(:given_files) {
181
+ ["file3.yml", "/apps/test_app/config/dir/file4.yml"]
182
+ }
183
+ let(:files) {
184
+ {
185
+ "/apps/test_app/config/file3.yml" => { host: "host3.com" },
186
+ "/apps/test_app/config/dir/file4.yml" => { host: "host4.com" }
187
+ }
188
+ }
189
+ before {
190
+ expect(subject).to receive(:default_files).never
191
+ expect(subject).to receive(:get_absolute_paths_for).with(given_files).and_call_original
192
+ }
193
+ it {
194
+ expect(subject.load(*given_files)).to eq(files.values)
195
+ }
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,163 @@
1
+ require "spec_helper"
2
+
3
+ describe A9n::Loader do
4
+ let(:env) { "test" }
5
+ let(:root) { File.expand_path("../../../test_app", __FILE__) }
6
+ let(:file_path) { File.join(root, "config/configuration.yml") }
7
+ subject { described_class.new(file_path, env) }
8
+
9
+ describe "#intialize" do
10
+ it { expect(subject.env).to eq(env) }
11
+ it { expect(subject.local_file).to eq(file_path) }
12
+ it { expect(subject.example_file).to eq("#{file_path}.example") }
13
+ end
14
+
15
+ describe "#load" do
16
+ let(:example_env_config) {
17
+ { app_url: "http://127.0.0.1:3000", api_key: "base1234" }
18
+ }
19
+ let(:local_env_config) {
20
+ { app_host: "127.0.0.1:3000", api_key: "local1234" }
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
+ let(:env){
29
+ "tropical"
30
+ }
31
+ let(:config) { subject.get }
32
+
33
+ context "when no configuration file exists" do
34
+ before do
35
+ expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(nil)
36
+ 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
+ expect(subject).to receive(:verify!).never
40
+ end
41
+ it "raises expection" do
42
+ expect {
43
+ subject.load
44
+ }.to raise_error(A9n::MissingConfigurationData)
45
+ end
46
+ end
47
+
48
+ context "when example configuration file exists with defaults" do
49
+ before do
50
+ expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(example_env_config)
51
+ 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
+ expect(described_class).to receive(:verify!).never
56
+ subject.load
57
+ end
58
+
59
+ it { expect(config.app_url).to eq("http://127.0.0.1:3000") }
60
+ it { expect(config.page_title).to eq("Base Kielbasa") }
61
+ it { expect(config.api_key).to eq("base1234") }
62
+
63
+ it {
64
+ expect { config.app_host }.to raise_error(A9n::NoSuchConfigurationVariable)
65
+ }
66
+ end
67
+
68
+ context "when local configuration file exists with defaults" do
69
+ before do
70
+ 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(local_env_config)
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)
74
+ expect(described_class).to receive(:verify!).never
75
+ subject.load
76
+ end
77
+ it { expect(config.app_host).to eq("127.0.0.1:3000") }
78
+ it { expect(config.page_title).to eq("Local Kielbasa") }
79
+ it { expect(config.api_key).to eq("local1234") }
80
+
81
+ it {
82
+ expect { config.app_url }.to raise_error(A9n::NoSuchConfigurationVariable)
83
+ }
84
+ end
85
+
86
+ context "when both local and base configuration file exists without defaults" do
87
+ context "with same data" do
88
+ before do
89
+ expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(example_env_config)
90
+ expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(example_env_config)
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)
93
+ subject.load
94
+ end
95
+
96
+ it { expect(config.app_url).to eq("http://127.0.0.1:3000") }
97
+ it { expect(config.api_key).to eq("base1234") }
98
+
99
+ it {
100
+ expect { config.page_title }.to raise_error(A9n::NoSuchConfigurationVariable)
101
+ }
102
+ it {
103
+ expect { config.app_host }.to raise_error(A9n::NoSuchConfigurationVariable)
104
+ }
105
+ end
106
+
107
+ context "with different data" do
108
+ before do
109
+ expect(described_class).to receive(:load_yml).with(subject.example_file, env).and_return(example_env_config)
110
+ expect(described_class).to receive(:load_yml).with(subject.local_file, env).and_return(local_env_config)
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)
113
+ end
114
+ it "raises expection" do
115
+ expect {
116
+ subject.load
117
+ }.to raise_error(A9n::MissingConfigurationVariables)
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe ".load_yml" do
124
+ let(:env) { "test" }
125
+ subject { described_class.load_yml(file_path, env) }
126
+
127
+ context "when file not exists" do
128
+ let(:file_path) { "file_not_existing_in_universe.yml" }
129
+
130
+ it{ expect(subject).to be_nil }
131
+ end
132
+
133
+ context "when file exists" do
134
+ let(:file_path) { File.join(root, "config/configuration.yml") }
135
+
136
+ before {
137
+ ENV["DWARF"] = "erbized dwarf"
138
+ }
139
+
140
+ context "and has data" do
141
+ it "returns non-empty hash" do
142
+ expect(subject).to be_kind_of(Hash)
143
+ expect(subject.keys).to_not be_empty
144
+ end
145
+
146
+ it "has symbolized keys" do
147
+ expect(subject.keys.first).to be_kind_of(Symbol)
148
+ expect(subject[:hash_dwarf]).to be_kind_of(Hash)
149
+ expect(subject[:hash_dwarf].keys.first).to be_kind_of(Symbol)
150
+ end
151
+
152
+ it "parses erb" do
153
+ expect(subject[:erb_dwarf]).to eq("erbized dwarf")
154
+ end
155
+ end
156
+
157
+ context "and has no data" do
158
+ let(:env) { "production" }
159
+ it{ expect(subject).to be_nil }
160
+ end
161
+ end
162
+ end
163
+ end
@@ -47,8 +47,20 @@ describe A9n::Struct do
47
47
  expect(subject.fetch(:non_empty_dwarf)).to eq('dwarf')
48
48
  end
49
49
 
50
- it 'not returns nil for non existing value' do
50
+ it 'return false value' do
51
+ expect(subject.fetch(:false_dwarf)).to eq(false)
52
+ end
53
+
54
+ it 'return nil value' do
55
+ expect(subject.fetch(:nil_dwarf)).to eq(nil)
56
+ end
57
+
58
+ it 'return nil for non existing value' do
51
59
  expect(subject.fetch(:non_existing_dwarf)).to eq(nil)
52
60
  end
61
+
62
+ it 'return default for non existing value' do
63
+ expect(subject.fetch(:non_existing_dwarf, 'default')).to eq('default')
64
+ end
53
65
  end
54
66
  end
@@ -0,0 +1,3 @@
1
+ defaults:
2
+ username: "joe"
3
+ api_key: "asdf1234"
@@ -20,4 +20,4 @@ test:
20
20
  overriden_dwarf: "already overriden dwarf"
21
21
  hash_dwarf:
22
22
  dwarf_1: "hello 1"
23
- dwarf_2: "hello 2"
23
+ dwarf_2: "hello 2"
@@ -0,0 +1,23 @@
1
+ defaults:
2
+ default_dwarf: "example default dwarf"
3
+ overriden_dwarf: "example not yet overriden dwarf"
4
+ development:
5
+ nil_dwarf: ~
6
+ false_dwarf: false
7
+ true_dwarf: true
8
+ string_dwarf: "dwarf"
9
+ erb_dwarf: "<%= ENV['DWARF'] %>"
10
+ overriden_dwarf: "example already overriden dwarf"
11
+ hash_dwarf:
12
+ dwarf_1: "hello 1"
13
+ dwarf_2: "hello 2"
14
+ test:
15
+ nil_dwarf: ~
16
+ false_dwarf: false
17
+ true_dwarf: true
18
+ string_dwarf: "dwarf"
19
+ erb_dwarf: "<%= ENV['DWARF'] %>"
20
+ overriden_dwarf: "example already overriden dwarf"
21
+ hash_dwarf:
22
+ dwarf_1: "hello 1"
23
+ dwarf_2: "hello 2"
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.2.3
4
+ version: 0.3.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: 2014-05-16 00:00:00.000000000 Z
11
+ date: 2014-05-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Simple tool for managing extra configuration in ruby/rails apps
14
14
  email:
@@ -27,13 +27,18 @@ files:
27
27
  - a9n.gemspec
28
28
  - lib/a9n.rb
29
29
  - lib/a9n/capistrano.rb
30
- - lib/a9n/core_ext/hash.rb
30
+ - lib/a9n/ext/hash.rb
31
+ - lib/a9n/loader.rb
31
32
  - lib/a9n/struct.rb
32
33
  - lib/a9n/version.rb
33
- - spec/a9n_spec.rb
34
- - spec/fixtures/configuration.yml
34
+ - spec/integration/a9n_spec.rb
35
35
  - spec/spec_helper.rb
36
- - spec/struct_spec.rb
36
+ - spec/unit/a9n_spec.rb
37
+ - spec/unit/loader_spec.rb
38
+ - spec/unit/struct_spec.rb
39
+ - test_app/config/a9n/mandrill.yml
40
+ - test_app/config/configuration.yml
41
+ - test_app/config/configuration.yml.example
37
42
  homepage: https://github.com/knapo/a9n
38
43
  licenses:
39
44
  - MIT
@@ -59,7 +64,8 @@ signing_key:
59
64
  specification_version: 4
60
65
  summary: a9n is a simple tool for managing extra configuration in ruby/rails apps
61
66
  test_files:
62
- - spec/a9n_spec.rb
63
- - spec/fixtures/configuration.yml
67
+ - spec/integration/a9n_spec.rb
64
68
  - spec/spec_helper.rb
65
- - spec/struct_spec.rb
69
+ - spec/unit/a9n_spec.rb
70
+ - spec/unit/loader_spec.rb
71
+ - spec/unit/struct_spec.rb
@@ -1,17 +0,0 @@
1
- class Hash
2
- # Hash#deep_symbolize_keys
3
- # based on
4
- # https://github.com/svenfuchs/i18n/blob/master/lib/i18n/core_ext/hash.rb
5
- def deep_symbolize_keys
6
- inject({}) { |result, (key, value)|
7
- value = value.deep_symbolize_keys if value.is_a?(self.class)
8
- result[(key.to_sym rescue key) || key] = value
9
- result
10
- }
11
- end unless self.method_defined?(:deep_symbolize_keys)
12
-
13
- def self.merge(*items)
14
- return nil if items.compact.empty?
15
- items.compact.inject({}){|sum, item| sum.merge!(item)}
16
- end
17
- end
@@ -1,295 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe A9n do
4
- describe '.local_app' do
5
- context 'when rails not found' do
6
- before {
7
- expect(described_class).to receive(:get_rails).and_return(nil)
8
- }
9
- specify {
10
- expect(described_class.local_app).to be_nil
11
- }
12
- end
13
-
14
- context 'when custom non-rails app is being used' do
15
- let(:local_app) { double(env: 'test', root: '/apps/a9n') }
16
- before { described_class.local_app = local_app }
17
-
18
- specify { expect(described_class.local_app).to eq(local_app) }
19
- end
20
-
21
- after { described_class.local_app = nil }
22
- end
23
-
24
- describe '.root' do
25
- let(:local_app) { double(env: 'test', root: '/apps/a9n') }
26
- before { described_class.local_app = local_app }
27
-
28
- context 'with custom path' do
29
- before {
30
- described_class.root = '/home/knapo/workspace/a9n'
31
- }
32
- specify {
33
- expect(described_class.root).to eq(Pathname.new('/home/knapo/workspace/a9n'))
34
- }
35
- end
36
-
37
- context 'with local app path' do
38
- specify {
39
- expect(described_class.root).to eq('/apps/a9n')
40
- }
41
- end
42
-
43
- after {
44
- described_class.root = nil
45
- described_class.local_app = nil
46
- }
47
- end
48
-
49
- describe '.load' do
50
- let(:base_file) { described_class::DEFAULT_FILE }
51
- let(:extra_file) { 'mongo.yml' }
52
- let(:base_sample_config){
53
- { app_url: 'http://127.0.0.1:3000', api_key: 'base1234' }
54
- }
55
- let(:local_sample_config){
56
- { app_host: '127.0.0.1:3000', api_key: 'local1234' }
57
- }
58
- let(:base_default_config){
59
- { page_title: 'Base Kielbasa', api_key: 'base1234default' }
60
- }
61
- let(:local_default_config){
62
- { page_title: 'Local Kielbasa', api_key: 'local1234default' }
63
- }
64
- let(:env){
65
- 'tropical'
66
- }
67
- subject {
68
- described_class
69
- }
70
- before do
71
- allow(described_class).to receive(:env).and_return(env)
72
- end
73
-
74
- context 'when no configuration file exists' do
75
- before do
76
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", env).and_return(nil)
77
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", env).and_return(nil)
78
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", 'defaults').and_return(nil)
79
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", 'defaults').and_return(nil)
80
- expect(described_class).to receive(:verify!).never
81
- end
82
- it 'raises expection' do
83
- expect {
84
- described_class.load
85
- }.to raise_error(described_class::MissingConfigurationData)
86
- end
87
- end
88
-
89
- context 'when base configuration file exists with defaults' do
90
- before do
91
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", env).and_return(base_sample_config)
92
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", env).and_return(nil)
93
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", 'defaults').and_return(base_default_config)
94
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", 'defaults').and_return(nil)
95
-
96
- expect(described_class).to receive(:verify!).never
97
- described_class.load
98
- end
99
-
100
- its(:app_url) { should_not be_nil }
101
- its(:app_url) { should == subject.fetch(:app_url) }
102
- its(:page_title) { should == 'Base Kielbasa' }
103
- its(:api_key) { should == 'base1234' }
104
- specify {
105
- expect(subject.instance_variable_get("@configuration")).to be_kind_of(A9n::Struct)
106
- }
107
- specify {
108
- expect { subject.app_host }.to raise_error(described_class::NoSuchConfigurationVariable)
109
- }
110
- end
111
-
112
- context 'when local configuration file exists with defaults' do
113
- before do
114
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", env).and_return(nil)
115
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", env).and_return(local_sample_config)
116
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", 'defaults').and_return(nil)
117
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", 'defaults').and_return(local_default_config)
118
- expect(described_class).to receive(:verify!).never
119
- described_class.load
120
- end
121
-
122
- its(:app_host) { should_not be_nil }
123
- its(:page_title) { should == 'Local Kielbasa' }
124
- its(:api_key) { should == 'local1234' }
125
- specify {
126
- expect { subject.app_url }.to raise_error(described_class::NoSuchConfigurationVariable)
127
- }
128
- end
129
-
130
- context 'when both local and base configuration file exists without defaults' do
131
- context 'with same data' do
132
- before do
133
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", env).and_return(base_sample_config)
134
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", env).and_return(base_sample_config)
135
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", 'defaults').and_return(nil)
136
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", 'defaults').and_return(nil)
137
- described_class.load
138
- end
139
-
140
- its(:app_url) { should_not be_nil }
141
- its(:api_key) { should == 'base1234' }
142
- specify {
143
- expect { subject.page_title }.to raise_error(described_class::NoSuchConfigurationVariable)
144
- }
145
- specify {
146
- expect { subject.app_host }.to raise_error(described_class::NoSuchConfigurationVariable)
147
- }
148
- end
149
-
150
- context 'with different data' do
151
- before do
152
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", env).and_return(base_sample_config)
153
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", env).and_return(local_sample_config)
154
- expect(described_class).to receive(:load_yml).with("config/#{base_file}.example", 'defaults').and_return(nil)
155
- expect(described_class).to receive(:load_yml).with("config/#{base_file}", 'defaults').and_return(nil)
156
- end
157
- it 'raises expection' do
158
- expect {
159
- described_class.load
160
- }.to raise_error(described_class::MissingConfigurationVariables)
161
- end
162
- end
163
- end
164
-
165
- context 'when extra file is loaded' do
166
- before do
167
- expect(described_class).to receive(:load_yml).with("config/#{extra_file}.example", env).and_return(base_sample_config)
168
- expect(described_class).to receive(:load_yml).with("config/#{extra_file}", env).and_return(nil)
169
- expect(described_class).to receive(:load_yml).with("config/#{extra_file}.example", 'defaults').and_return(base_default_config)
170
- expect(described_class).to receive(:load_yml).with("config/#{extra_file}", 'defaults').and_return(nil)
171
-
172
- expect(described_class).to receive(:verify!).never
173
- described_class.load('mongo.yml')
174
- end
175
-
176
- it { expect(subject.mongo).to be_kind_of(A9n::Struct) }
177
- it { expect(subject.instance_variable_get("@mongo")).to be_kind_of(A9n::Struct) }
178
- it { expect(subject.mongo.app_url).to eq("http://127.0.0.1:3000") }
179
- it { expect(subject.mongo.page_title).to eq('Base Kielbasa') }
180
- it { expect(subject.mongo.api_key).to eq('base1234') }
181
- specify {
182
- expect { subject.mongo.app_host }.to raise_error(described_class::NoSuchConfigurationVariable)
183
- }
184
- end
185
- end
186
-
187
- describe '.load_yml' do
188
- let(:root) { File.dirname(__FILE__) }
189
- let(:env) { 'test' }
190
- subject { described_class.load_yml(file_path, env) }
191
-
192
- before do
193
- expect(described_class).to receive(:root).at_least(:once).and_return(root)
194
- expect(described_class).to receive(:env).never
195
- end
196
-
197
- context 'when file not exists' do
198
- let(:file_path) { 'file_not_existing_in_universe.yml' }
199
-
200
- it 'returns nil' do
201
- expect(subject).to be_nil
202
- end
203
- end
204
-
205
- context 'when file exists' do
206
- let(:file_path) { 'fixtures/configuration.yml'}
207
- before {
208
- ENV['DWARF'] = 'erbized dwarf'
209
- }
210
-
211
- context 'and has data' do
212
- it 'returns non-empty hash' do
213
- expect(subject).to be_kind_of(Hash)
214
- expect(subject.keys).to_not be_empty
215
- end
216
-
217
- it 'has symbolized keys' do
218
- expect(subject.keys.first).to be_kind_of(Symbol)
219
- expect(subject[:hash_dwarf]).to be_kind_of(Hash)
220
- expect(subject[:hash_dwarf].keys.first).to be_kind_of(Symbol)
221
- end
222
-
223
- it 'parses erb' do
224
- expect(subject[:erb_dwarf]).to eq('erbized dwarf')
225
- end
226
- end
227
-
228
- context 'and has no data' do
229
- let(:env) { 'production' }
230
- it { should be_nil }
231
- end
232
- end
233
- end
234
-
235
- describe '.env' do
236
- before {
237
- described_class.instance_variable_set(:@env, nil)
238
- }
239
-
240
- context 'local_app_env is set' do
241
- before {
242
- expect(described_class).to receive(:local_app).and_return(double(env: 'dwarf_env')).exactly(3).times
243
- expect(described_class).to receive(:get_env_var).never
244
- }
245
-
246
- describe '#env' do
247
- subject { super().env }
248
- it { should == 'dwarf_env' }
249
- end
250
- end
251
-
252
- context "when APP_ENV is set" do
253
- before {
254
- expect(described_class).to receive(:local_app_env).and_return(nil)
255
- expect(described_class).to receive(:get_env_var).with('RAILS_ENV').and_return(nil)
256
- expect(described_class).to receive(:get_env_var).with('RACK_ENV').and_return(nil)
257
- expect(described_class).to receive(:get_env_var).with('APP_ENV').and_return('dwarf_env')
258
- }
259
-
260
- describe '#env' do
261
- subject { super().env }
262
- it { should == 'dwarf_env' }
263
- end
264
- end
265
- end
266
-
267
- describe '.get_env_var' do
268
- before { ENV['DWARF'] = 'little dwarf' }
269
- it { expect(described_class.get_env_var('DWARF')).to eq('little dwarf')}
270
- it { expect(described_class.get_env_var('IS_DWARF')).to be_nil}
271
- end
272
-
273
- describe '.get_rails' do
274
- context 'when defined' do
275
- before {
276
- Object.const_set(:Rails, Module.new)
277
- }
278
- after {
279
- Object.send(:remove_const, :Rails)
280
- }
281
- it {
282
- expect(described_class.get_rails).to be_kind_of(Module)
283
- }
284
- end
285
- context 'when not defined' do
286
- it { expect(described_class.get_rails).to be_nil }
287
- end
288
- end
289
-
290
- describe '.var_name_for' do
291
- it { expect(described_class.var_name_for(:configuration)).to eq(:@configuration) }
292
- it { expect(described_class.var_name_for('configuration.yml')).to eq(:@configuration) }
293
- it { expect(described_class.var_name_for('custom_dir/extra.yml')).to eq(:@extra) }
294
- end
295
- end