a9n 0.6.2 → 0.7.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 +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
|