consul_application_settings 2.1.1 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e6c1820af05af3bcc9adca21879296cbcfc8898e69ffa7136bac1d785ae1bb1
4
- data.tar.gz: 0e8b5c29bf2992a6d36e7a064b8887922b69ec5f820073157b53a21dc5905089
3
+ metadata.gz: fa6d5fd8ad5cf3b9df7fcbc3622a6efac55fc1bc3fcc81327ea061eab9a1ae77
4
+ data.tar.gz: 93434d8c607adf270f4acb503cc93bed92850b0fb366fb2d4513f679f39154e3
5
5
  SHA512:
6
- metadata.gz: 976fec73196a99d0a642d9f4d9243beb0a0925f44f140ff6e77091dac1919f36472b1467344e190da01b071803412d6ec612ccb31f8751a9d06d3f471a49fd1f
7
- data.tar.gz: e0d2e02025e94d2d5fc6ac4dd24a4073f9ac228969e67afbd9afb5cafa297c6a347dd68a53fac9a4ad6b4c2ce875b036372894b175e1bd36e6db138c1b31e9d9
6
+ metadata.gz: e86cddbca8a53cf608b72abb8dbe8c1aefdee558636f21cad45b6b3111ced6159f758175e4a377c55fbe2eba0ffab729b7fe427cf84ae43e77e25818a06593d1
7
+ data.tar.gz: 1ad1589204db6e0fa56ca42300f68c32a796a8c181b03c2aebde4104b66568c9100c2cec032b76ade78b811106228611717f92900f96fa451721be4fafebd240
data/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [3.0.0]
4
+ ### Breaking Changes
5
+ - Use Preloaded Consul Settings Provider by default
6
+ ### New features
7
+ - Configurable setting providers
8
+ - Preloaded Consul Settings Provider to prioritize performance over consistency
9
+ - Performance tests in spec
10
+ - Benchmarking script
11
+ ### Fixes
12
+ - Return nil instead of empty hash when reading missing setting from file
13
+ - Return nil instead of empty string when reading missing value from Consul
14
+ - Add missing load method on Settings Reader to create object with narrow scope
15
+
16
+ ## [2.1.1]
17
+ ### Changes
18
+ - Update Diplomat to latest version
19
+
20
+ ## [2.1.0]
21
+ ### Fixes
22
+ - Return nil for unknown keys
23
+
24
+ ## [2.0.0]
25
+ ### Breaking Changes
26
+ - Change default naming for setting files
27
+
28
+ ## [1.0.0]
29
+ ### Features
30
+ - Add support for second settings file (local settings for development)
31
+
32
+ ## [0.1.4]
33
+ ### Fixes
34
+ - Clone values before returning
35
+
3
36
  ## [0.1.3]
4
37
  ### Fixes
5
38
  - Add `Diplomat::PathNotFound` to the list of caught exceptions
@@ -22,7 +55,13 @@
22
55
  - Support deep settings search
23
56
  - Support nested configs
24
57
 
25
- [Unreleased]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.3...HEAD
58
+ [Unreleased]: https://github.com/matic-insurance/consul_application_settings/compare/3.0.0...HEAD
59
+ [3.0.0]: https://github.com/matic-insurance/consul_application_settings/compare/2.0.0...3.0.0
60
+ [2.1.1]: https://github.com/matic-insurance/consul_application_settings/compare/2.1.0...2.1.1
61
+ [2.1.0]: https://github.com/matic-insurance/consul_application_settings/compare/2.0.0...2.1.0
62
+ [2.0.0]: https://github.com/matic-insurance/consul_application_settings/compare/1.0.0...2.0.0
63
+ [1.0.0]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.4...1.0.0
64
+ [0.1.4]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.3...0.1.4
26
65
  [0.1.3]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.2...0.1.3
27
66
  [0.1.2]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.1...0.1.2
28
67
  [0.1.1]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.0...0.1.1
data/README.md CHANGED
@@ -45,6 +45,11 @@ ConsulApplicationSettings.configure do |config|
45
45
  config.local_file_path = Rails.root.join('config/my_settings.local.yml')
46
46
  # Specify whether exceprion should be thrown on Consul connection errors. Default: false
47
47
  config.disable_consul_connection_errors = true
48
+ # Specify setting providers. Default: [ConsulApplicationSettings::Providers::ConsulPreloaded, ConsulApplicationSettings::Providers::LocalStorage]
49
+ config.settings_providers = [
50
+ ConsulApplicationSettings::Providers::Consul,
51
+ ConsulApplicationSettings::Providers::LocalStorage
52
+ ]
48
53
  end
49
54
 
50
55
  APP_SETTINGS = ConsulApplicationSettings.load
@@ -139,6 +144,10 @@ All Gem configurations
139
144
  | base_file_path | no | 'config/application_settings.yml' | String | Path to the file with base settings |
140
145
  | local_file_path | no | 'config/application_settings.local.yml' | String | Path to the file with local settings overriding the base settings |
141
146
  | disable_consul_connection_errors | no | true | Boolean | Do not raise exception when consul is not available (useful for development) |
147
+ | settings_providers | no | Array(Provider) | Array | Specify custom setting provider lists |
148
+
149
+ ### Performance vs Consistency
150
+ To be defined in future iterations on Consul Providers
142
151
 
143
152
  ## Development
144
153
 
data/bin/benchmark ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'consul_application_settings'
5
+ require 'benchmark'
6
+
7
+ ITERATIONS = 1000
8
+
9
+ def kill_consul
10
+ `pgrep consul | xargs kill`
11
+ end
12
+
13
+ def start_consul
14
+ spawn('consul agent -dev -node machine > /dev/null 2>&1')
15
+ end
16
+
17
+ def application_settings(providers)
18
+ file = 'spec/fixtures/base_application_settings.yml'
19
+ ConsulApplicationSettings.configure do |config|
20
+ config.settings_providers = providers
21
+ end
22
+ ConsulApplicationSettings.load(file)
23
+ end
24
+
25
+ def benchmark_gem
26
+ Benchmark.bm(20) do |x|
27
+ x.report('Real Time Consul') do
28
+ ca = application_settings([
29
+ ConsulApplicationSettings::Providers::Consul,
30
+ ConsulApplicationSettings::Providers::LocalStorage
31
+ ])
32
+ ITERATIONS.times { ca.get('application/name') }
33
+ end
34
+
35
+ x.report('Preloaded Consul') do
36
+ ca = application_settings([
37
+ ConsulApplicationSettings::Providers::ConsulPreloaded,
38
+ ConsulApplicationSettings::Providers::LocalStorage
39
+ ])
40
+ ITERATIONS.times { ca.get('application/name') }
41
+ end
42
+
43
+ x.report('File Only') do
44
+ ca = application_settings([
45
+ ConsulApplicationSettings::Providers::ConsulPreloaded
46
+ ])
47
+ ITERATIONS.times { ca.get('application/name') }
48
+ end
49
+ end
50
+ end
51
+
52
+ puts '-' * 80
53
+ puts 'Benchmark without consul agent'
54
+ kill_consul
55
+ benchmark_gem
56
+
57
+
58
+ puts '-' * 80
59
+ puts 'Benchmark with consul agent running'
60
+ start_consul
61
+ benchmark_gem
62
+
63
+ kill_consul
@@ -1,8 +1,10 @@
1
1
  require 'consul_application_settings/version'
2
+ require 'consul_application_settings/providers/abstract'
3
+ require 'consul_application_settings/providers/consul'
4
+ require 'consul_application_settings/providers/consul_preloaded'
5
+ require 'consul_application_settings/providers/local_storage'
2
6
  require 'consul_application_settings/configuration'
3
- require 'consul_application_settings/consul_provider'
4
- require 'consul_application_settings/file_provider'
5
- require 'consul_application_settings/settings_provider'
7
+ require 'consul_application_settings/reader'
6
8
  require 'consul_application_settings/utils'
7
9
 
8
10
  # The gem provides possibility to load settings from Consul and automatically fall back to data stored in file system
@@ -21,6 +23,6 @@ module ConsulApplicationSettings
21
23
  end
22
24
 
23
25
  def self.load(path = '')
24
- SettingsProvider.new(path, config)
26
+ Reader.new(path, config)
25
27
  end
26
28
  end
@@ -3,12 +3,17 @@ module ConsulApplicationSettings
3
3
  class Configuration
4
4
  DEFAULT_BASE_FILE_PATH = 'config/app_settings.yml'.freeze
5
5
  DEFAULT_LOCAL_FILE_PATH = 'config/app_settings.local.yml'.freeze
6
- attr_accessor :base_file_path, :local_file_path, :disable_consul_connection_errors
6
+ DEFAULT_PROVIDERS = [
7
+ ConsulApplicationSettings::Providers::ConsulPreloaded,
8
+ ConsulApplicationSettings::Providers::LocalStorage
9
+ ]
10
+ attr_accessor :base_file_path, :local_file_path, :disable_consul_connection_errors, :settings_providers
7
11
 
8
12
  def initialize
9
13
  @base_file_path = DEFAULT_BASE_FILE_PATH
10
14
  @local_file_path = DEFAULT_LOCAL_FILE_PATH
11
15
  @disable_consul_connection_errors = true
16
+ @settings_providers = DEFAULT_PROVIDERS
12
17
  end
13
18
  end
14
19
  end
@@ -0,0 +1,25 @@
1
+ module ConsulApplicationSettings
2
+ module Providers
3
+ # Abstract class with basic functionality
4
+ class Abstract
5
+ def initialize(base_path, config)
6
+ @base_path, @config = base_path, config
7
+ end
8
+
9
+ def get(path)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ protected
14
+
15
+ def absolute_key_path(path)
16
+ ConsulApplicationSettings::Utils.generate_path(@base_path, path)
17
+ end
18
+
19
+ def get_value_from_hash(path, data)
20
+ parts = ConsulApplicationSettings::Utils.decompose_path(path)
21
+ data.dig(*parts).clone
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ require 'diplomat'
2
+
3
+ module ConsulApplicationSettings
4
+ module Providers
5
+ # Provides access to settings stored in Consul
6
+ class Consul < Abstract
7
+ def get(path)
8
+ full_path = absolute_key_path(path)
9
+ value = get_from_consul(full_path)
10
+ ConsulApplicationSettings::Utils.cast_consul_value(value)
11
+ end
12
+
13
+ private
14
+
15
+ def get_from_consul(path)
16
+ Diplomat::Kv.get(path, {})
17
+ rescue Diplomat::KeyNotFound
18
+ return nil
19
+ rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
20
+ raise e unless @config.disable_consul_connection_errors
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ module ConsulApplicationSettings
2
+ module Providers
3
+ # Provides access to settings stored in Consul. Loads them once
4
+ class ConsulPreloaded < Abstract
5
+ def initialize(base_path, config)
6
+ super
7
+ @data = get_all_from_consul
8
+ end
9
+
10
+ def get(path)
11
+ value = get_value_from_hash(absolute_key_path(path), @data)
12
+ ConsulApplicationSettings::Utils.cast_consul_value(value)
13
+ end
14
+
15
+ protected
16
+
17
+ def get_all_from_consul
18
+ Diplomat::Kv.get_all(@base_path, { convert_to_hash: true })
19
+ rescue Diplomat::KeyNotFound
20
+ {}
21
+ rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
22
+ raise e unless @config.disable_consul_connection_errors
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ require 'yaml'
2
+
3
+ module ConsulApplicationSettings
4
+ module Providers
5
+ # Provides access to settings stored in file system with support of base and local files
6
+ class LocalStorage < Abstract
7
+ def initialize(base_path, config)
8
+ super
9
+ @data = load
10
+ end
11
+
12
+ def get(path)
13
+ get_value_from_hash(absolute_key_path(path), @data)
14
+ end
15
+
16
+ private
17
+
18
+ def load
19
+ base_yml = read_yml(base_file_path)
20
+ local_yml = read_yml(local_file_path)
21
+ DeepMerge.deep_merge!(local_yml, base_yml, preserve_unmergeables: false, overwrite_arrays: true,
22
+ merge_nil_values: true)
23
+ end
24
+
25
+ def base_file_path
26
+ @config.base_file_path
27
+ end
28
+
29
+ def local_file_path
30
+ @config.local_file_path
31
+ end
32
+
33
+ def read_yml(path)
34
+ return {} unless File.exist?(path)
35
+
36
+ YAML.safe_load(IO.read(path))
37
+ rescue Psych::SyntaxError, Errno::ENOENT => e
38
+ raise ConsulApplicationSettings::Error, "Cannot read settings file at #{path}: #{e.message}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ module ConsulApplicationSettings
2
+ # Provides access to settings stored in Consul or in file system
3
+ class Reader
4
+ def initialize(base_path, config)
5
+ @base_path = base_path
6
+ @config = config
7
+ @providers = config.settings_providers.map { |provider| provider.new(base_path, config) }
8
+ end
9
+
10
+ def get(path)
11
+ @providers.each do |provider|
12
+ value = provider.get(path)
13
+ return value unless value.nil?
14
+ end
15
+ nil
16
+ end
17
+
18
+ alias [] get
19
+
20
+ def load(sub_path)
21
+ new_path = ConsulApplicationSettings::Utils.generate_path(@base_path, sub_path)
22
+ self.class.new(new_path, @config)
23
+ end
24
+ end
25
+ end
@@ -6,6 +6,7 @@ module ConsulApplicationSettings
6
6
 
7
7
  class << self
8
8
  def cast_consul_value(value)
9
+ return nil if value.nil?
9
10
  return false if value == 'false'
10
11
  return true if value == 'true'
11
12
 
@@ -1,3 +1,3 @@
1
1
  module ConsulApplicationSettings
2
- VERSION = '2.1.1'.freeze
2
+ VERSION = '3.0.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: consul_application_settings
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Volodymyr Mykhailyk
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-22 00:00:00.000000000 Z
11
+ date: 2021-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diplomat
@@ -143,15 +143,18 @@ files:
143
143
  - LICENSE.txt
144
144
  - README.md
145
145
  - Rakefile
146
+ - bin/benchmark
146
147
  - bin/console
147
148
  - bin/rspec
148
149
  - bin/setup
149
150
  - consul_application_settings.gemspec
150
151
  - lib/consul_application_settings.rb
151
152
  - lib/consul_application_settings/configuration.rb
152
- - lib/consul_application_settings/consul_provider.rb
153
- - lib/consul_application_settings/file_provider.rb
154
- - lib/consul_application_settings/settings_provider.rb
153
+ - lib/consul_application_settings/providers/abstract.rb
154
+ - lib/consul_application_settings/providers/consul.rb
155
+ - lib/consul_application_settings/providers/consul_preloaded.rb
156
+ - lib/consul_application_settings/providers/local_storage.rb
157
+ - lib/consul_application_settings/reader.rb
155
158
  - lib/consul_application_settings/utils.rb
156
159
  - lib/consul_application_settings/version.rb
157
160
  homepage: https://github.com/matic-insurance/consul_application_settings
@@ -176,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
179
  - !ruby/object:Gem::Version
177
180
  version: '0'
178
181
  requirements: []
179
- rubygems_version: 3.0.3
182
+ rubygems_version: 3.0.3.1
180
183
  signing_key:
181
184
  specification_version: 4
182
185
  summary: Application settings via Consul with yaml defaults
@@ -1,33 +0,0 @@
1
- require 'diplomat'
2
-
3
- module ConsulApplicationSettings
4
- # Provides access to settings stored in Consul
5
- class ConsulProvider
6
- def initialize(base_path, config)
7
- @base_path = base_path
8
- @config = config
9
- end
10
-
11
- def get(path)
12
- value = fetch_value(path)
13
- ConsulApplicationSettings::Utils.cast_consul_value(value)
14
- end
15
-
16
- private
17
-
18
- def fetch_value(path)
19
- full_path = generate_full_path(path)
20
- Diplomat::Kv.get(full_path, {}, :return)
21
- rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
22
- raise e unless disable_consul_connection_errors?
23
- end
24
-
25
- def generate_full_path(path)
26
- ConsulApplicationSettings::Utils.generate_path(@base_path, path)
27
- end
28
-
29
- def disable_consul_connection_errors?
30
- @config.disable_consul_connection_errors
31
- end
32
- end
33
- end
@@ -1,54 +0,0 @@
1
- require 'yaml'
2
-
3
- module ConsulApplicationSettings
4
- # Provides access to settings stored in file system with support of base and local files
5
- class FileProvider
6
- def initialize(base_path, config)
7
- @base_path = base_path
8
- @config = config
9
- load
10
- end
11
-
12
- def get(path)
13
- read_path(path).clone
14
- end
15
-
16
- private
17
-
18
- def load
19
- base_yml = read_yml(base_file_path)
20
- local_yml = read_yml(local_file_path)
21
- @data = DeepMerge.deep_merge!(local_yml, base_yml, preserve_unmergeables: false, overwrite_arrays: true,
22
- merge_nil_values: true)
23
- end
24
-
25
- def base_file_path
26
- @config.base_file_path
27
- end
28
-
29
- def local_file_path
30
- @config.local_file_path
31
- end
32
-
33
- def read_yml(path)
34
- return {} unless File.exist?(path)
35
-
36
- YAML.safe_load(IO.read(path))
37
- rescue Psych::SyntaxError, Errno::ENOENT => e
38
- raise ConsulApplicationSettings::Error, "Cannot read settings file at #{path}: #{e.message}"
39
- end
40
-
41
- def read_path(path)
42
- full_path = ConsulApplicationSettings::Utils.generate_path(@base_path, path)
43
- parts = ConsulApplicationSettings::Utils.decompose_path(full_path)
44
- parts.reduce(@data, &method(:read_value))
45
- end
46
-
47
- def read_value(hash, key)
48
- raise ConsulApplicationSettings::Error, 'reading arrays not implemented' if hash.is_a? Array
49
- return {} if hash.nil?
50
-
51
- hash.fetch(key.to_s, nil)
52
- end
53
- end
54
- end
@@ -1,16 +0,0 @@
1
- module ConsulApplicationSettings
2
- # Provides access to settings stored in Consul or in file system
3
- class SettingsProvider
4
- def initialize(base_path, config)
5
- @consul_provider = ConsulProvider.new(base_path, config)
6
- @file_provider = FileProvider.new(base_path, config)
7
- end
8
-
9
- def get(path)
10
- consul_value = @consul_provider.get(path)
11
- !consul_value.nil? && consul_value != '' ? consul_value : @file_provider.get(path)
12
- end
13
-
14
- alias [] get
15
- end
16
- end