a9n 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hound.yml +4 -0
- data/.rubocop.yml +43 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +3 -3
- data/README.md +7 -7
- data/Rakefile +1 -2
- data/a9n.gemspec +14 -13
- data/lib/a9n.rb +29 -23
- data/lib/a9n/capistrano.rb +1 -1
- data/lib/a9n/capistrano/tasks.cap +3 -3
- data/lib/a9n/capistrano/ver2x.rb +2 -2
- data/lib/a9n/ext/hash.rb +4 -2
- data/lib/a9n/ext/string_inquirer.rb +2 -2
- data/lib/a9n/loader.rb +24 -19
- data/lib/a9n/scope.rb +2 -3
- data/lib/a9n/struct.rb +4 -2
- data/lib/a9n/version.rb +1 -1
- data/spec/integration/a9n_spec.rb +33 -33
- data/spec/spec_helper.rb +9 -12
- data/spec/unit/a9n_spec.rb +55 -55
- data/spec/unit/loader_spec.rb +71 -71
- data/spec/unit/scope_spec.rb +9 -9
- data/spec/unit/struct_spec.rb +68 -55
- data/test_app/benchmark.rb +4 -2
- metadata +26 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d6b3183902e220ce9b3159af50976f1a13ad2e7eecd37b8ddc63cc461b2cbd3
|
4
|
+
data.tar.gz: cae9ed3830fe21623e856349703378526b38f653bcd9ca9393d102050ae5b798
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a436c129417fefef0e2fbdb728b83b18cf9e1ee9192303737fa9d00bde7bc2189f98228a58a0db44e6b6a233524d7e8a94d418de37f83c68c28634a4e459cbb8
|
7
|
+
data.tar.gz: af4055cef9c76f29d9146ed4c3471b6885491d5c193c641a24bc432ed69bf7578dda31b10a5fbb69ed75a9924a33d0d02cfe2365245f844bf5d64033e9b80261
|
data/.hound.yml
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
Gemspec/RequiredRubyVersion:
|
2
|
+
Include:
|
3
|
+
- 2.4
|
4
|
+
- 2.5
|
5
|
+
- 2.6
|
6
|
+
|
7
|
+
Metrics/BlockLength:
|
8
|
+
Max: 30
|
9
|
+
Exclude:
|
10
|
+
- '**/*_spec.rb'
|
11
|
+
|
12
|
+
Metrics/LineLength:
|
13
|
+
Max: 140
|
14
|
+
|
15
|
+
Security/YAMLLoad:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/BlockDelimiters:
|
19
|
+
EnforcedStyle: braces_for_chaining
|
20
|
+
|
21
|
+
Style/Documentation:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/GuardClause:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/EachWithObject:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/MethodMissingSuper:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Style/MissingRespondToMissing:
|
34
|
+
Enabled: false
|
35
|
+
|
36
|
+
Style/FrozenStringLiteralComment:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
Style/SymbolArray:
|
40
|
+
EnforcedStyle: brackets
|
41
|
+
|
42
|
+
Style/TrivialAccessors:
|
43
|
+
Enabled: false
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
a9n
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.3
|
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
|
13
|
+
A9n is a simple tool to keep ruby/rails apps configuration maintanable and verifiable. It supports Rails 4+ and Ruby 2.4+.
|
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
|
|
@@ -34,7 +34,7 @@ keys `A9n::MissingConfigurationVariablesError` is thrown with the explanation wh
|
|
34
34
|
Set application root and load configuration by adding to your `application.rb` or `environment.rb` right
|
35
35
|
after budler requires:
|
36
36
|
|
37
|
-
A9n.root = File.expand_path('
|
37
|
+
A9n.root = File.expand_path('..', __dir__)
|
38
38
|
A9n.load
|
39
39
|
|
40
40
|
This step is not required ,if you don't use `a9n` in the environment settings or initializers.
|
@@ -64,16 +64,16 @@ is accessible by:
|
|
64
64
|
|
65
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.
|
66
66
|
|
67
|
-
E.g. if you have `config/a9n/
|
67
|
+
E.g. if you have `config/a9n/mail.yml`:
|
68
68
|
|
69
69
|
defaults:
|
70
|
-
|
71
|
-
|
70
|
+
email_from: 'knapo@knapo.net'
|
71
|
+
delivery_method: 'smtp'
|
72
72
|
|
73
73
|
You can access it by:
|
74
74
|
|
75
|
-
A9n.
|
76
|
-
A9n.
|
75
|
+
A9n.mail.email_from # => `knapo@knapo.net`
|
76
|
+
A9n.mail.delivery_method # => `smtp`
|
77
77
|
|
78
78
|
## Mapping ENV variables
|
79
79
|
|
data/Rakefile
CHANGED
data/a9n.gemspec
CHANGED
@@ -1,24 +1,25 @@
|
|
1
|
-
require File.expand_path('
|
1
|
+
require File.expand_path('lib/a9n/version', __dir__)
|
2
2
|
|
3
3
|
Gem::Specification.new do |gem|
|
4
|
-
gem.authors = [
|
5
|
-
gem.email = [
|
6
|
-
gem.description =
|
7
|
-
gem.summary =
|
8
|
-
gem.homepage =
|
4
|
+
gem.authors = ['Krzysztof Knapik']
|
5
|
+
gem.email = ['knapo@knapo.net']
|
6
|
+
gem.description = 'a9n - ruby/rails apps configuration manager'
|
7
|
+
gem.summary = 'a9n is a tool to keep ruby/rails apps extra configuration easily maintainable and verifiable'
|
8
|
+
gem.homepage = 'https://github.com/knapo/a9n'
|
9
9
|
gem.license = 'MIT'
|
10
|
-
gem.files = `git ls-files`.split(
|
11
|
-
gem.executables = gem.files.grep(%r{^
|
10
|
+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
11
|
+
gem.executables = gem.files.grep(%r{^exe/}).map { |f| File.basename(f) }
|
12
12
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
|
-
gem.name =
|
14
|
-
gem.require_paths = [
|
13
|
+
gem.name = 'a9n'
|
14
|
+
gem.require_paths = ['lib']
|
15
15
|
gem.version = A9n::VERSION
|
16
16
|
|
17
|
-
gem.required_ruby_version =
|
17
|
+
gem.required_ruby_version = '>= 2.4'
|
18
18
|
|
19
|
+
gem.add_development_dependency 'codeclimate-test-reporter'
|
20
|
+
gem.add_development_dependency 'pry'
|
19
21
|
gem.add_development_dependency 'rake'
|
20
22
|
gem.add_development_dependency 'rspec'
|
23
|
+
gem.add_development_dependency 'rubocop'
|
21
24
|
gem.add_development_dependency 'simplecov'
|
22
|
-
gem.add_development_dependency 'codeclimate-test-reporter'
|
23
|
-
gem.add_development_dependency 'pry'
|
24
25
|
end
|
data/lib/a9n.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
1
|
+
require 'forwardable'
|
2
|
+
require 'pathname'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'yaml'
|
5
|
+
require 'erb'
|
6
|
+
require 'a9n/version'
|
7
|
+
require 'a9n/exceptions'
|
8
|
+
require 'a9n/struct'
|
9
|
+
require 'a9n/scope'
|
10
|
+
require 'a9n/ext/string_inquirer'
|
11
|
+
require 'a9n/ext/hash'
|
12
|
+
require 'a9n/loader'
|
13
13
|
|
14
14
|
module A9n
|
15
15
|
extend SingleForwardable
|
16
16
|
|
17
|
-
EXTENSION_LIST =
|
18
|
-
STRICT_MODE =
|
17
|
+
EXTENSION_LIST = '{yml,yml.erb,yml.example,yml.erb.example}'.freeze
|
18
|
+
STRICT_MODE = 'strict'.freeze
|
19
19
|
|
20
20
|
class << self
|
21
21
|
def env
|
22
|
-
@env ||= ::A9n::StringInquirer.new(app_env || env_var(
|
22
|
+
@env ||= ::A9n::StringInquirer.new(app_env || env_var('RAILS_ENV') || env_var('RACK_ENV') || env_var('APP_ENV'))
|
23
23
|
end
|
24
24
|
|
25
25
|
def env=(value)
|
@@ -27,7 +27,7 @@ module A9n
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def app_env
|
30
|
-
app
|
30
|
+
app&.env
|
31
31
|
end
|
32
32
|
|
33
33
|
def app
|
@@ -43,7 +43,7 @@ module A9n
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def app_root
|
46
|
-
app
|
46
|
+
app&.root
|
47
47
|
end
|
48
48
|
|
49
49
|
def root=(path)
|
@@ -55,7 +55,8 @@ module A9n
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def env_var(name, strict: false)
|
58
|
-
|
58
|
+
raise A9n::MissingEnvVariableError, name if strict && !ENV.key?(name)
|
59
|
+
|
59
60
|
if ENV[name].is_a?(::String)
|
60
61
|
ENV[name].dup.force_encoding(Encoding::UTF_8)
|
61
62
|
else
|
@@ -66,7 +67,7 @@ module A9n
|
|
66
67
|
def default_files
|
67
68
|
files = Dir[root.join("config/{#{A9n::Scope::ROOT_NAMES.join(',')}}.#{EXTENSION_LIST}").to_s]
|
68
69
|
files += Dir[root.join("config/a9n/*.#{EXTENSION_LIST}")]
|
69
|
-
files.map{ |f| f.sub(/.example$/,'') }.uniq
|
70
|
+
files.map { |f| f.sub(/.example$/, '') }.uniq
|
70
71
|
end
|
71
72
|
|
72
73
|
def load(*files)
|
@@ -109,23 +110,28 @@ module A9n
|
|
109
110
|
storage[scope.name] = data
|
110
111
|
define_root_geters(scope.name)
|
111
112
|
end
|
112
|
-
|
113
|
+
|
114
|
+
data
|
113
115
|
end
|
114
116
|
|
115
117
|
def absolute_paths_for(files)
|
116
|
-
files.map { |file| Pathname.new(file).absolute? ? file :
|
118
|
+
files.map { |file| Pathname.new(file).absolute? ? file : root.join('config', file).to_s }
|
117
119
|
end
|
118
120
|
|
119
121
|
def require_local_extension
|
120
|
-
return if
|
121
|
-
|
122
|
+
return if root.nil?
|
123
|
+
|
124
|
+
local_extension_file = File.join(root, 'config/a9n.rb')
|
125
|
+
|
122
126
|
return unless File.exist?(local_extension_file)
|
127
|
+
|
123
128
|
require local_extension_file
|
124
129
|
end
|
125
130
|
|
126
131
|
def define_root_geters(*names)
|
127
132
|
names.each do |name|
|
128
133
|
next if respond_to?(name)
|
134
|
+
|
129
135
|
define_singleton_method(name) { storage.fetch(name) }
|
130
136
|
end
|
131
137
|
end
|
data/lib/a9n/capistrano.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
namespace :a9n do
|
2
|
-
desc
|
2
|
+
desc 'Copy stage configuration to the base file.'
|
3
3
|
task :copy_stage_config do
|
4
4
|
on roles(:app) do
|
5
5
|
within fetch(:release_path) do
|
6
|
-
execute :cp,
|
6
|
+
execute :cp, 'config/configuration.yml.#{fetch(:stage)}', 'config/configuration.yml'
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
after
|
12
|
+
after 'deploy:updating', 'a9n:copy_stage_config'
|
data/lib/a9n/capistrano/ver2x.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Capistrano::Configuration.instance.load do
|
2
|
-
after
|
2
|
+
after 'deploy:update_code', 'a9n:copy_stage_config'
|
3
3
|
|
4
4
|
namespace :a9n do
|
5
|
-
desc
|
5
|
+
desc 'Copy stage configuration to base file.'
|
6
6
|
task :copy_stage_config, roles: :app do
|
7
7
|
run "cp #{fetch(:release_path)}/config/configuration.yml.#{fetch(:stage)} #{fetch(:release_path)}/config/configuration.yml"
|
8
8
|
end
|
data/lib/a9n/ext/hash.rb
CHANGED
@@ -3,14 +3,16 @@ module A9n
|
|
3
3
|
class << self
|
4
4
|
def deep_prepare(hash, scope)
|
5
5
|
hash.inject({}) do |result, (key, value)|
|
6
|
-
|
6
|
+
key_name = key.respond_to?(:to_sym) ? key.to_sym : key
|
7
|
+
result[key_name] = get_value(key, value, scope)
|
7
8
|
result
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
11
12
|
def merge(*items)
|
12
13
|
return nil if items.compact.empty?
|
13
|
-
|
14
|
+
|
15
|
+
items.compact.inject({}) { |sum, item| sum.merge!(item) }
|
14
16
|
end
|
15
17
|
|
16
18
|
private
|
@@ -4,11 +4,11 @@ module A9n
|
|
4
4
|
private
|
5
5
|
|
6
6
|
def respond_to_missing?(method_name, include_private = false)
|
7
|
-
(method_name[-1] ==
|
7
|
+
(method_name[-1] == '?') || super
|
8
8
|
end
|
9
9
|
|
10
10
|
def method_missing(method_name, *arguments)
|
11
|
-
if method_name[-1] ==
|
11
|
+
if method_name[-1] == '?'
|
12
12
|
self == method_name[0..-2]
|
13
13
|
else
|
14
14
|
super
|
data/lib/a9n/loader.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module A9n
|
2
2
|
class Loader
|
3
|
-
attr_reader :scope, :env, :local_file, :example_file
|
3
|
+
attr_reader :scope, :env, :local_file, :example_file, :struct
|
4
4
|
|
5
|
-
COMMON_SCOPE =
|
5
|
+
COMMON_SCOPE = 'defaults'.freeze
|
6
6
|
|
7
7
|
def initialize(file_path, scope, env)
|
8
8
|
@scope = scope
|
@@ -12,20 +12,15 @@ module A9n
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def get
|
15
|
-
|
15
|
+
struct || load
|
16
16
|
end
|
17
17
|
|
18
18
|
def load
|
19
19
|
local_config = self.class.load_yml(local_file, scope, env)
|
20
20
|
example_config = self.class.load_yml(example_file, scope, env)
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
if !local_config.nil? && !example_config.nil?
|
27
|
-
verify!(local_config, example_config)
|
28
|
-
end
|
22
|
+
ensure_data_presence!(local_config, example_config)
|
23
|
+
ensure_keys_presence!(local_config, example_config)
|
29
24
|
|
30
25
|
@struct = A9n::Struct.new(local_config || example_config)
|
31
26
|
end
|
@@ -33,6 +28,7 @@ module A9n
|
|
33
28
|
class << self
|
34
29
|
def load_yml(file_path, scope, env)
|
35
30
|
return nil unless File.exist?(file_path)
|
31
|
+
|
36
32
|
yml = YAML.load(ERB.new(File.read(file_path)).result)
|
37
33
|
|
38
34
|
common_scope = prepare_yml_scope(yml, scope, COMMON_SCOPE)
|
@@ -42,21 +38,30 @@ module A9n
|
|
42
38
|
end
|
43
39
|
|
44
40
|
def prepare_yml_scope(yml, scope, env)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
nil
|
49
|
-
end
|
41
|
+
return nil unless yml[env].is_a?(::Hash)
|
42
|
+
|
43
|
+
A9n::Hash.deep_prepare(yml[env], scope)
|
50
44
|
end
|
51
45
|
end
|
52
46
|
|
53
47
|
private
|
54
48
|
|
55
|
-
def
|
49
|
+
def ensure_data_presence!(local, example)
|
50
|
+
return unless local.nil?
|
51
|
+
return unless example.nil?
|
52
|
+
|
53
|
+
raise A9n::MissingConfigurationDataError, "Configuration data for *#{env}* env was not found in neither *#{example}* nor *#{local}*"
|
54
|
+
end
|
55
|
+
|
56
|
+
def ensure_keys_presence!(local, example)
|
57
|
+
return if local.nil?
|
58
|
+
return if example.nil?
|
59
|
+
|
56
60
|
missing_keys = example.keys - local.keys
|
57
|
-
|
58
|
-
|
59
|
-
|
61
|
+
|
62
|
+
return if missing_keys.empty?
|
63
|
+
|
64
|
+
raise A9n::MissingConfigurationVariablesError, "Following variables are missing in #{local_file} file: #{missing_keys.join(',')}"
|
60
65
|
end
|
61
66
|
end
|
62
67
|
end
|
data/lib/a9n/scope.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module A9n
|
2
2
|
class Scope
|
3
|
-
ROOT_NAMES = [:configuration, :a9n]
|
3
|
+
ROOT_NAMES = [:configuration, :a9n].freeze
|
4
4
|
|
5
5
|
attr_reader :name
|
6
6
|
|
@@ -17,8 +17,7 @@ module A9n
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.form_file_path(path)
|
20
|
-
|
21
|
-
self.new(name)
|
20
|
+
new(File.basename(path.to_s).split('.').first.to_sym)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|