environment_config 1.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 94877d33ba5f427d4f5d7c8098a1b472c59a873ea53b037e661a5aa564b37e79
4
+ data.tar.gz: 8bd759e5b24c255952398bb3cb72776d0e49c7eb6618a77058b9a440c875ba09
5
+ SHA512:
6
+ metadata.gz: 36f865ef45d16d05b5706048b41afb743ae5af8b30d43a0e4c45f0ae3aa9c1594030a432e9ab37442823bbf96f621420fb13ccf281b2f62dfab354e09d6c4804
7
+ data.tar.gz: 0afe62776474b2cc33e9be969133fdd507ecf9f6e01a5ef1c3f4f3c3278d326b9b64c279cb4618d99356f732d487aaf6d479c9bc966dc8f546052f303fae7a3f
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ Gemfile.lock
2
+
3
+ spec/examples.txt
4
+
5
+ *.gem
data/.gitlab-ci.yml ADDED
@@ -0,0 +1,17 @@
1
+ image: ruby:2.4.1
2
+
3
+ variables:
4
+ SSH_AUTH_SOCK: "/ssh-agent"
5
+
6
+ before_script:
7
+ - mkdir -p /root/.ssh && ssh-keyscan -H codevault.io > /root/.ssh/known_hosts 2>/dev/null
8
+ - bundle config jobs 8
9
+ - bundle install --path=/tmp/bundler --quiet
10
+
11
+ rspec:
12
+ script:
13
+ - bundle exec rspec
14
+
15
+ rubocop:
16
+ script:
17
+ - bundle exec rubocop
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,3 @@
1
+ inherit_gem:
2
+ kp_cop:
3
+ - default.yml
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ ## 1.2.0
2
+
3
+ * Add support for complex datatypes via `json` and `yaml` parsers
4
+
5
+ ## 1.1.0
6
+
7
+ * Add `EnvironmentConfig.ensure`, which is a more expressive way to
8
+ check the presence of environment variables without using them
9
+ * Add `EnvironmentConfig.fetch_*` methods that will fetch a single
10
+ environment variable expecting the given type (e.g. `fetch_string`).
11
+ * Improve error readout for unparsable integer
12
+
13
+ ## 1.0.1
14
+
15
+ * Make method private that should never have been public
16
+ * The `store` and `fetch` methods are not really part of the public interface
17
+ of the config, however since `fetch` is equal to a method on hashes,
18
+ it is quite likely to be called by accident
19
+
20
+ ## 1.0.0
21
+
22
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ git_source(:codevault) { |repo_name| "git@codevault.io:#{repo_name}.git" }
5
+
6
+ # Specify your gem's dependencies in auth_connector.gemspec
7
+ gemspec
8
+
9
+ # Need to mention it here instead of gemspec, since we pull it from a git repo
10
+ gem 'kp_cop', codevault: 'kaeuferportal/kp_cop'
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # EnvironmentConfig
2
+
3
+ This gem provides a unified way to read configuration parameters
4
+ from environment variables.
5
+
6
+
7
+ ## Usage
8
+
9
+ You can load a configuration from the environment using a `load` block:
10
+
11
+ ```ruby
12
+ config = EnvironmentConfig.load do |c|
13
+ c.string 'FOO'
14
+ c.integer 'BAR', 42
15
+ end
16
+ ```
17
+
18
+ If any required variable is missing, an error will be raised. Errors can be
19
+ suppressed by providing a default value as second parameter.
20
+
21
+ Albeit environment variables being string only, you can specify types when
22
+ defining parameters:
23
+
24
+ * `string`
25
+ * `symbol`
26
+ * `integer` (base 10 encoded)
27
+ * `boolean` (only accepts `true` and `false`)
28
+ * `string_list` (comma separated)
29
+ * `json`
30
+ * `yaml`
31
+
32
+ Type conversions try to be strict and will throw an error, if the conversion
33
+ can't be performed safely (e.g. an integer receiving `abc` will raise an error
34
+ instead of parsing to `0`).
35
+
36
+ Be mindful, that `json` and `yaml` allow you to pass rather complex configuration, but will not be able to validate the content beyond checking it is valid JSON/YAML.
37
+
38
+ ### Accessing values
39
+
40
+ After building the configuration, your values will be available:
41
+
42
+ * as methods on the config object
43
+ * as hash with string keys using `to_string_hash`
44
+ * as hash with symbol keys using `to_symbol_hash`
45
+
46
+ All keys are lower cased, so `FOO_ARG` will become `config.foo_arg` or `config.to_string_hash['foo']`.
47
+
48
+ ### Avoiding prefix duplication
49
+
50
+ For environment variables you will usually want to assign common prefixes, e.g.
51
+
52
+ ```bash
53
+ export FOO_HOST=host
54
+ export FOO_PASSWORD=password
55
+ ```
56
+
57
+ Oftentimes you don't want to have those prefixes in your application config,
58
+ so you can strip them using `strip_prefix`:
59
+
60
+ ```ruby
61
+ # defining it
62
+ foo_config = EnvironmentConfig.load(strip_prefix: 'FOO_') do |c|
63
+ c.string 'FOO_HOST', 'default_host'
64
+ c.string 'FOO_PASSWORD'
65
+ c.string 'OTHER_FOO_VALUE', 'test'
66
+ end
67
+
68
+ # using it
69
+ foo_config.host # default_host
70
+ foo_config.other_foo_value # test
71
+ ```
72
+
73
+ ### Accessing single values
74
+
75
+ Sometimes you just want to fetch a few unrelated values from the environment,
76
+ but not create a configuration object.
77
+
78
+ There are fetch methods for all known types. For example:
79
+
80
+ ```ruby
81
+ EnvironmentConfig.fetch_integer('MY_PORT')
82
+ => 42
83
+ ```
84
+
85
+ ### Expecting environment variables
86
+
87
+ If you want to check the correct definition of environment variables that you do
88
+ not consume yourself (e.g. you know that a gem will consume them), just use
89
+ the `ensure` helper method:
90
+
91
+ ```ruby
92
+ EnvironmentConfig.ensure do |c|
93
+ c.string 'MY_GEM_CONFIG_VALUE'
94
+ c.integer 'MY_GEM_OTHER_VALUE'
95
+ end
96
+ ```
97
+
98
+ ### Integration with Rails
99
+
100
+ One possible integration into a Rails application could look like this:
101
+
102
+ ```ruby
103
+ # config/application.rb
104
+ module MyRailsApp
105
+ class Application < Rails::Application
106
+ # ...
107
+
108
+ config.some_sub_configuration = EnvironmentConfig.load do |c|
109
+ c.string 'FOO_HOST', 'default_host'
110
+ c.string 'FOO_PASSWORD'
111
+ c.boolean 'SOME_BOOL', false
112
+ end
113
+ end
114
+ end
115
+
116
+ # somewhere else
117
+ Rails.application.config.some_sub_configuration.foo
118
+ ```
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'environment_config'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require 'pry'
10
+ Pry.start
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('lib', __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'environment_config/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'environment_config'
10
+ spec.version = EnvironmentConfig::VERSION
11
+ spec.authors = ['Jan Sandbrink']
12
+ spec.email = ['jan.sandbrink@kaeuferportal.de']
13
+
14
+ spec.summary = 'Gem to unify reading configuration from env variables.'
15
+ spec.homepage = 'https://codevault.io/kaeuferportal/environment_config'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ .reject { |f| f.match(%r{^spec/}) }
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.10'
24
+ spec.add_development_dependency 'pry'
25
+ spec.add_development_dependency 'rspec', '~> 3.6'
26
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'environment_config/builder'
4
+ require 'environment_config/type_fetcher_methods'
5
+
6
+ class EnvironmentConfig
7
+ include TypeFetcherMethods
8
+
9
+ class << self
10
+ # Accepts the same block as `load` does, but only validates
11
+ # the presence and type of environment variables. Does not return
12
+ # a configuration.
13
+ def ensure(&block)
14
+ load(&block)
15
+ nil
16
+ end
17
+
18
+ # Loads a configuration from environment variables as specified
19
+ # by the block given (using the environment config DSL)
20
+ def load(**options)
21
+ builder = Builder.new(**options)
22
+ yield builder
23
+ builder.config
24
+ end
25
+ end
26
+
27
+ def initialize
28
+ @store = {}
29
+ end
30
+
31
+ def method_missing(method_name, *_arguments, &block)
32
+ return fetch method_name if known_key?(method_name)
33
+
34
+ super
35
+ end
36
+
37
+ def respond_to_missing?(method_name, _include_private = false)
38
+ known_key?(method_name) || super
39
+ end
40
+
41
+ def to_h
42
+ # This method solely exists for purposes of "irb discoverability"
43
+ raise NotImplementedError,
44
+ 'Please choose between to_string_hash and to_symbol_hash, ' \
45
+ 'depending on the key type you want to get.'
46
+ end
47
+
48
+ def to_string_hash
49
+ @store.dup
50
+ end
51
+
52
+ def to_symbol_hash
53
+ @store.map { |k, v| [k.to_sym, v] }.to_h
54
+ end
55
+
56
+ def store(key, value)
57
+ @store[key.to_s] = value
58
+ end
59
+
60
+ private
61
+
62
+ def fetch(key)
63
+ @store[key.to_s]
64
+ end
65
+
66
+ def known_key?(key)
67
+ @store.key? key.to_s
68
+ end
69
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'environment_config/typed_env'
4
+ require 'environment_config/types'
5
+
6
+ class EnvironmentConfig
7
+ class Builder
8
+ attr_reader :config
9
+
10
+ def initialize(strip_prefix: nil)
11
+ @strip_prefix = strip_prefix
12
+ @config = EnvironmentConfig.new
13
+ end
14
+
15
+ def method_missing(method_name, *args)
16
+ if Types.known_type?(method_name)
17
+ type = method_name
18
+ key = args.shift
19
+ convert_and_store(type, key, *args)
20
+ else
21
+ super
22
+ end
23
+ end
24
+
25
+ def respond_to_missing?(method_name, *args)
26
+ Types.known_type?(method_name) || super
27
+ end
28
+
29
+ private
30
+
31
+ def convert_and_store(type, key, *args)
32
+ value = TypedEnv.fetch(type, key, *args)
33
+ store(key, value)
34
+ end
35
+
36
+ def store(key, value)
37
+ if @strip_prefix && key.start_with?(@strip_prefix)
38
+ key = key[@strip_prefix.size..-1]
39
+ end
40
+ config.store(key.downcase, value)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'environment_config/typed_env'
4
+ require 'environment_config/types'
5
+
6
+ class EnvironmentConfig
7
+ module TypeFetcherMethods
8
+ class << self
9
+ def included(base)
10
+ base.extend(ClassMethods)
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ def method_missing(method_name, *args)
16
+ type = type_of_fetch_method(method_name)
17
+ if type && Types.known_type?(type)
18
+ key = args.shift
19
+ TypedEnv.fetch(type, key, *args)
20
+ else
21
+ super
22
+ end
23
+ end
24
+
25
+ def respond_to_missing?(method_name, *_args)
26
+ type = type_of_fetch_method(method_name)
27
+ (type && Types.known_type?(type)) || super
28
+ end
29
+
30
+ private
31
+
32
+ def type_of_fetch_method(method_name)
33
+ match = /\Afetch_(.+)/.match(method_name)
34
+ return nil unless match
35
+
36
+ match[1]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'environment_config/types'
4
+
5
+ class EnvironmentConfig
6
+ class TypedEnv
7
+ class << self
8
+ def fetch(type, key, *args)
9
+ Types.convert(type, key, fetch_raw(key, *args))
10
+ end
11
+
12
+ private
13
+
14
+ def fetch_raw(key, *args)
15
+ ENV.fetch(key, *args)
16
+ rescue KeyError => e
17
+ raise e,
18
+ "Expected environment variable #{key} to be set, but was missing."
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'environment_config/types/boolean'
4
+ require 'environment_config/types/integer'
5
+ require 'environment_config/types/json'
6
+ require 'environment_config/types/string'
7
+ require 'environment_config/types/string_list'
8
+ require 'environment_config/types/symbol'
9
+ require 'environment_config/types/yaml'
10
+
11
+ require 'environment_config/types/type_error'
12
+
13
+ class EnvironmentConfig
14
+ module Types
15
+ ALL = [
16
+ ::EnvironmentConfig::Types::Boolean,
17
+ ::EnvironmentConfig::Types::Integer,
18
+ ::EnvironmentConfig::Types::Json,
19
+ ::EnvironmentConfig::Types::String,
20
+ ::EnvironmentConfig::Types::StringList,
21
+ ::EnvironmentConfig::Types::Symbol,
22
+ ::EnvironmentConfig::Types::Yaml
23
+ ].freeze
24
+
25
+ class << self
26
+ def convert(type_name, key, value)
27
+ type = type_map.fetch(type_name.to_sym)
28
+ type.convert(value)
29
+ rescue Types::TypeError => e
30
+ raise ArgumentError,
31
+ "Environment variable #{key} could not be read as #{type_name}." \
32
+ " Expected: #{e.expected_message}" \
33
+ " Got: #{e.value}"
34
+ end
35
+
36
+ def type_names
37
+ type_map.keys
38
+ end
39
+
40
+ def known_type?(type_name)
41
+ type_map.key?(type_name.to_sym)
42
+ end
43
+
44
+ private
45
+
46
+ def type_map
47
+ @type_map ||= ALL.map { |type| [type.name, type] }.to_h
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EnvironmentConfig
4
+ module Types
5
+ class Boolean
6
+ BOOLEAN_VALUES = %w[true false].freeze
7
+
8
+ class << self
9
+ def name
10
+ :boolean
11
+ end
12
+
13
+ def convert(value)
14
+ # ensure compatibility when receiving correctly typed default value
15
+ value = value.to_s
16
+
17
+ unless BOOLEAN_VALUES.include?(value)
18
+ raise TypeError.new(BOOLEAN_VALUES.join('/'), value)
19
+ end
20
+
21
+ value == 'true'
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EnvironmentConfig
4
+ module Types
5
+ class Integer
6
+ class << self
7
+ def name
8
+ :integer
9
+ end
10
+
11
+ def convert(value)
12
+ Integer(value)
13
+ rescue ArgumentError
14
+ raise TypeError.new('base 10 integer', value)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ class EnvironmentConfig
6
+ module Types
7
+ class Json
8
+ class << self
9
+ def name
10
+ :json
11
+ end
12
+
13
+ def convert(value)
14
+ return value if value.is_a?(Array) || value.is_a?(Hash)
15
+ return nil if value.empty?
16
+
17
+ JSON.parse(value)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EnvironmentConfig
4
+ module Types
5
+ class String
6
+ class << self
7
+ def name
8
+ :string
9
+ end
10
+
11
+ def convert(value)
12
+ value.to_s
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EnvironmentConfig
4
+ module Types
5
+ class StringList
6
+ class << self
7
+ def name
8
+ :string_list
9
+ end
10
+
11
+ def convert(value)
12
+ return value if value.is_a? Array
13
+
14
+ value.split(',')
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EnvironmentConfig
4
+ module Types
5
+ class Symbol
6
+ class << self
7
+ def name
8
+ :symbol
9
+ end
10
+
11
+ def convert(value)
12
+ value.to_sym
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EnvironmentConfig
4
+ module Types
5
+ class TypeError < StandardError
6
+ attr_reader :expected_message, :value
7
+
8
+ def initialize(expected_message, value)
9
+ @expected_message = expected_message
10
+ @value = value
11
+
12
+ super()
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ class EnvironmentConfig
6
+ module Types
7
+ class Yaml
8
+ class << self
9
+ def name
10
+ :yaml
11
+ end
12
+
13
+ def convert(value)
14
+ return value if value.is_a?(Array) || value.is_a?(Hash)
15
+ return nil if value.empty?
16
+
17
+ YAML.safe_load(value)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EnvironmentConfig
4
+ VERSION = '1.2.0'.freeze
5
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: environment_config
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan Sandbrink
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-11-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.6'
55
+ description:
56
+ email:
57
+ - jan.sandbrink@kaeuferportal.de
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".gitlab-ci.yml"
64
+ - ".rspec"
65
+ - ".rubocop.yml"
66
+ - CHANGELOG.md
67
+ - Gemfile
68
+ - README.md
69
+ - bin/console
70
+ - environment_config.gemspec
71
+ - lib/environment_config.rb
72
+ - lib/environment_config/builder.rb
73
+ - lib/environment_config/type_fetcher_methods.rb
74
+ - lib/environment_config/typed_env.rb
75
+ - lib/environment_config/types.rb
76
+ - lib/environment_config/types/boolean.rb
77
+ - lib/environment_config/types/integer.rb
78
+ - lib/environment_config/types/json.rb
79
+ - lib/environment_config/types/string.rb
80
+ - lib/environment_config/types/string_list.rb
81
+ - lib/environment_config/types/symbol.rb
82
+ - lib/environment_config/types/type_error.rb
83
+ - lib/environment_config/types/yaml.rb
84
+ - lib/environment_config/version.rb
85
+ homepage: https://codevault.io/kaeuferportal/environment_config
86
+ licenses: []
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.7.6
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Gem to unify reading configuration from env variables.
108
+ test_files: []