consul_application_settings 0.1.4 → 1.0.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
  SHA256:
3
- metadata.gz: 8abc7a1661131160a981373791ccc8ac3a48323283803b32f04a866ae33d3cd2
4
- data.tar.gz: f6c70feae5652f55c564a98df789518efe4e3980ded79c09f51c7429811444b6
3
+ metadata.gz: b20ecf9e1eb2ea05e41f729b69082093305d3ce5291d8fb45a64f185ba8ae44f
4
+ data.tar.gz: ce5d16eb80ae0a9c0be78dda05d06e350546328d737ae1dd65309120f17fa814
5
5
  SHA512:
6
- metadata.gz: 20fd00d62bbb2c98fcb4566fe505d67b208190c6bf7b5240520c410d11822252caad6b5574d08a7603f0c5400d75dc80d4b566c1524bd8181ae912fb5d6d3d48
7
- data.tar.gz: a0d0d0ed7fbdb672bb4252558875d01638d78fb4a92c5179f661192fc4125db47800aff3c6b27b1a185f85cde38c356f096fc6ee1ee09b1c05f24c8718dd6abe
6
+ metadata.gz: eac2795525ed07225d749c836781c5f9bebdfcaa55a8490d4cdca4ce2463db3130cd94e081cd87bd808743d7d1acf29729ccb5002000d5efc79a76b87a57bc0a
7
+ data.tar.gz: ed28b56badd89cbf5097ac4d061eadd43e7aa1da37aefec1693d5b17b4bb78996e9cc8c9f4b38564b6c0383ea77ac65f89c8b66bb4e7ba4af8c7e8c7820dc2d7
data/Gemfile.lock CHANGED
@@ -24,7 +24,7 @@ GEM
24
24
  ast (~> 2.4.0)
25
25
  psych (3.1.0)
26
26
  rainbow (3.0.0)
27
- rake (10.5.0)
27
+ rake (13.0.1)
28
28
  rspec (3.8.0)
29
29
  rspec-core (~> 3.8.0)
30
30
  rspec-expectations (~> 3.8.0)
@@ -62,7 +62,7 @@ PLATFORMS
62
62
  DEPENDENCIES
63
63
  bundler (~> 2.0)
64
64
  consul_application_settings!
65
- rake (~> 10.0)
65
+ rake (~> 13.0)
66
66
  rspec (~> 3.0)
67
67
  rubocop (~> 0.66)
68
68
  rubocop-rspec (~> 1.32.0)
data/README.md CHANGED
@@ -23,7 +23,7 @@ Example use cases:
23
23
 
24
24
  Gem reads any particular setting from consul and if it is missing tries to find value in YAML defaults file
25
25
 
26
- **NOTE** Consul is requested every time you query the settings. Defaults YAML file loaded in memory and not changing.
26
+ **NOTE** Consul is requested every time you query the settings. Defaults YAML file is loaded in memory and is not changing.
27
27
 
28
28
  ## Installation
29
29
 
@@ -40,13 +40,17 @@ gem 'consul_application_settings'
40
40
  At the load of application:
41
41
  ```ruby
42
42
  ConsulApplicationSettings.configure do |config|
43
- # Specify path to defaults file
44
- config.defaults = Rails.root.join('config/settings.yml')
45
- # Specify namespace to consul settings
46
- config.namespace = 'staging/my_cool_app'
43
+ # Specify path to the base settings YML. Default: 'config/application_settings.yml'
44
+ config.base_file_path = Rails.root.join('config/my_settings.yml')
45
+ # Specify path to the local settings YML, which overrides the base file. Default: 'config/application_settings.local.yml'
46
+ config.local_file_path = Rails.root.join('config/my_settings.local.yml')
47
+ # Specify whether exceprion should be thrown on Consul connection errors. Default: false
48
+ config.disable_consul_connection_errors = true
47
49
  end
48
50
 
49
51
  APP_SETTINGS = ConsulApplicationSettings.load
52
+ # Specify path to settings both in YML files and Consul
53
+ AUTH_SETTIGNS = ConsulApplicationSettings.load('authentication')
50
54
  ```
51
55
 
52
56
  **NOTE** For rails you can add this code to custom initializer `console_application_settings.rb` in `app/config/initializers`
@@ -55,7 +59,7 @@ APP_SETTINGS = ConsulApplicationSettings.load
55
59
 
56
60
  ### Settings structure
57
61
 
58
- Assuming your defaults file in repository `config/settings.yml` looks like:
62
+ Assuming your defaults file in repository `config/application_settings.yml` looks like:
59
63
  ```yaml
60
64
  staging:
61
65
  my_cool_app:
@@ -98,15 +102,13 @@ Anywhere in your code base, after initialization, you can use
98
102
  previously loaded settings to query any key by full path
99
103
 
100
104
  ```ruby
101
- APP_SETTINGS.app_name # "MyCoolApp"
105
+ APP_SETTINGS['app_name'] # "MyCoolApp"
102
106
  APP_SETTINGS.get(:hostname) # "https://mycoolapp.com"
103
107
 
104
108
  APP_SETTINGS.get('integrations/database/user') # "app"
105
109
  APP_SETTINGS['integrations/slack/enabled'] # true
106
110
  ```
107
111
 
108
- **NOTE** Gem is pulling settings from consul with namespace but ignores namespace for defaults
109
-
110
112
  ### Nested settings
111
113
 
112
114
  Assuming some part of your code needs to work with smaller part of settings -
@@ -114,36 +116,30 @@ gem provides interface to avoid duplicating absolute path
114
116
 
115
117
  ```ruby
116
118
  # You can load subsettings from root object
117
- db_settings = APP_SETTINGS.load_from('integrations/database')
118
- db_settings.domain # "194.78.92.19"
119
- db_settings['user'] # "app"
120
-
121
- # You can load subsettings from subsettings
122
- integrations_settings = APP_SETTINGS.load_from('integrations')
123
- slack_settings = integrations_settings.load_from('slack')
124
- slack_settings.enabled # true
125
- slack_settings.get('webhook_url') # "https://hooks.slack.com/services/XXXXXX/XXXXX/XXXXXXX"
119
+ db_settings = APP_SETTINGS.load('integrations/database')
120
+ db_settings.get(:domain) # "194.78.92.19"
121
+ db_settings['user'] # "app"
126
122
  ```
127
123
 
128
124
  ### Gem Configuration
129
125
  You can configure gem with block:
130
126
  ```ruby
131
127
  ConsulApplicationSettings.configure do |config|
132
- config.namespace = 'staging/my_cool_app'
128
+ config.local_file_path = 'config/config.yml'
133
129
  end
134
130
  ```
135
131
  or one option at a time
136
132
  ```ruby
137
- ConsulApplicationSettings.config.namespace = 'staging/my_cool_app'
133
+ ConsulApplicationSettings.config.local_file_path = 'config/config.yml'
138
134
  ```
139
135
 
140
136
  All Gem configurations
141
137
 
142
- | Configuration | Required | Default | Type | Description |
143
- |----------------------------------|----------|---------|---------|------------------------------------------------------------------------------|
144
- | defaults | yes | | String | Path to the file with default settings |
145
- | namespace | no | | String | Base path to read settings from in consul and defaults |
146
- | disable_consul_connection_errors | no | false | Boolean | Do not raise exception when consul is not available (useful for development) |
138
+ | Configuration | Required | Default | Type | Description |
139
+ |----------------------------------|----------|-----------------------------------------|---------|------------------------------------------------------------------------------|
140
+ | base_file_path | no | 'config/application_settings.yml' | String | Path to the file with base settings |
141
+ | local_file_path | no | 'config/application_settings.local.yml' | String | Path to the file with local settings overriding the base settings |
142
+ | disable_consul_connection_errors | no | true | Boolean | Do not raise exception when consul is not available (useful for development) |
147
143
 
148
144
  ## Development
149
145
 
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_dependency 'diplomat', '~> 2.1.3'
33
33
 
34
34
  spec.add_development_dependency 'bundler', '~> 2.0'
35
- spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rake', '~> 13.0'
36
36
  spec.add_development_dependency 'rspec', '~> 3.0'
37
37
  spec.add_development_dependency 'rubocop', '~> 0.66'
38
38
  spec.add_development_dependency 'rubocop-rspec', '~> 1.32.0'
@@ -1,11 +1,11 @@
1
1
  require 'consul_application_settings/version'
2
2
  require 'consul_application_settings/configuration'
3
- require 'consul_application_settings/defaults'
4
- require 'consul_application_settings/options'
3
+ require 'consul_application_settings/consul_provider'
4
+ require 'consul_application_settings/file_provider'
5
+ require 'consul_application_settings/settings_provider'
5
6
  require 'consul_application_settings/utils'
6
- require 'diplomat'
7
7
 
8
- # Main class used to configure defaults file path and load initial settings
8
+ # The gem provides possibility to load settings from Consul and automatically fall back to data stored in file system
9
9
  module ConsulApplicationSettings
10
10
  class Error < StandardError; end
11
11
 
@@ -16,19 +16,11 @@ module ConsulApplicationSettings
16
16
 
17
17
  self.config ||= ConsulApplicationSettings::Configuration.new
18
18
 
19
- class << self
20
- def configure
21
- yield(config)
22
- self.defaults = ConsulApplicationSettings::Defaults.read(config.defaults_path)
23
- end
24
-
25
- def load_from(path)
26
- settings_path = ConsulApplicationSettings::Utils.generate_path(config.namespace, path)
27
- ConsulApplicationSettings::Options.new(settings_path, defaults)
28
- end
19
+ def self.configure
20
+ yield(config)
21
+ end
29
22
 
30
- def load
31
- load_from('')
32
- end
23
+ def self.load(path = '')
24
+ SettingsProvider.new(path, config)
33
25
  end
34
26
  end
@@ -1,14 +1,14 @@
1
1
  module ConsulApplicationSettings
2
2
  # All gem configuration settings
3
3
  class Configuration
4
- # Required attributes
5
- attr_accessor :defaults_path
6
- # Optional attributes
7
- attr_accessor :namespace
8
- attr_accessor :disable_consul_connection_errors
4
+ DEFAULT_BASE_FILE_PATH = 'config/application_settings.yml'.freeze
5
+ DEFAULT_LOCAL_FILE_PATH = 'config/application_settings.local.yml'.freeze
6
+ attr_accessor :base_file_path, :local_file_path, :disable_consul_connection_errors
9
7
 
10
8
  def initialize
11
- self.namespace = ''
9
+ @base_file_path = DEFAULT_BASE_FILE_PATH
10
+ @local_file_path = DEFAULT_LOCAL_FILE_PATH
11
+ @disable_consul_connection_errors = true
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,33 @@
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
@@ -0,0 +1,54 @@
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)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,16 @@
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
@@ -1,3 +1,3 @@
1
1
  module ConsulApplicationSettings
2
- VERSION = '0.1.4'.freeze
2
+ VERSION = '1.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: 0.1.4
4
+ version: 1.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: 2020-04-16 00:00:00.000000000 Z
11
+ date: 2020-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diplomat
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '13.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '13.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -136,8 +136,9 @@ files:
136
136
  - consul_application_settings.gemspec
137
137
  - lib/consul_application_settings.rb
138
138
  - lib/consul_application_settings/configuration.rb
139
- - lib/consul_application_settings/defaults.rb
140
- - lib/consul_application_settings/options.rb
139
+ - lib/consul_application_settings/consul_provider.rb
140
+ - lib/consul_application_settings/file_provider.rb
141
+ - lib/consul_application_settings/settings_provider.rb
141
142
  - lib/consul_application_settings/utils.rb
142
143
  - lib/consul_application_settings/version.rb
143
144
  homepage: https://github.com/matic-insurance/consul_application_settings
@@ -1,43 +0,0 @@
1
- require 'yaml'
2
-
3
- module ConsulApplicationSettings
4
- # Reading default file from YAML file and providing interface to query them
5
- class Defaults
6
- attr_reader :contents
7
-
8
- def initialize(hash)
9
- @contents = hash
10
- end
11
-
12
- def get(name)
13
- read_path(name, contents).clone
14
- end
15
-
16
- def load_from(path)
17
- keys = ConsulApplicationSettings::Utils.decompose_path(path)
18
- new_defaults = keys.reduce(contents) { |hash, key| read_path(key, hash, {}) }
19
- self.class.new(new_defaults)
20
- end
21
-
22
- def self.read(path)
23
- new YAML.safe_load(IO.read(path))
24
- rescue Psych::SyntaxError, Errno::ENOENT => e
25
- raise ConsulApplicationSettings::Error, "Cannot read defaults file at #{path}: #{e.message}"
26
- end
27
-
28
- private
29
-
30
- def read_path(path, hash, default = nil)
31
- parts = ConsulApplicationSettings::Utils.decompose_path(path)
32
- result = parts.reduce(hash, &method(:read_value))
33
- result || default
34
- end
35
-
36
- def read_value(hash, key)
37
- raise ConsulApplicationSettings::Error, 'reading arrays not implemented' if hash.is_a? Array
38
- return {} if hash.nil?
39
-
40
- hash.fetch(key.to_s)
41
- end
42
- end
43
- end
@@ -1,53 +0,0 @@
1
- require 'json'
2
-
3
- module ConsulApplicationSettings
4
- # Reads settings from consul or ask defaults for value
5
- class Options
6
- attr_reader :path, :defaults
7
-
8
- def initialize(path, defaults)
9
- @path = path
10
- @defaults = defaults.load_from(path)
11
- end
12
-
13
- def load_from(new_path)
14
- full_path = ConsulApplicationSettings::Utils.generate_path(path, new_path)
15
- self.class.new(full_path, defaults)
16
- end
17
-
18
- def get(name)
19
- consul_value = key_value(name)
20
- if consul_value.nil? || consul_value.empty?
21
- defaults.get(name)
22
- else
23
- ConsulApplicationSettings::Utils.cast_consul_value(consul_value)
24
- end
25
- end
26
-
27
- def [](name)
28
- get(name)
29
- end
30
-
31
- # rubocop:disable Style/MethodMissingSuper
32
- def method_missing(name, *_args)
33
- get(name)
34
- end
35
- # rubocop:enable Style/MethodMissingSuper
36
-
37
- def respond_to_missing?(_name)
38
- true
39
- end
40
-
41
- private
42
-
43
- def key_value(name)
44
- Diplomat::Kv.get(key_path(name), {}, :return)
45
- rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
46
- raise e unless ConsulApplicationSettings.config.disable_consul_connection_errors
47
- end
48
-
49
- def key_path(name)
50
- ConsulApplicationSettings::Utils.generate_path(path, name)
51
- end
52
- end
53
- end