env_parser 0.8.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +7 -6
  3. data/.rubocop.yml +56 -57
  4. data/.ruby-version +1 -1
  5. data/Gemfile.lock +58 -28
  6. data/README.md +252 -176
  7. data/docs/EnvParser.html +305 -169
  8. data/docs/EnvParser/AutoregisterFileNotFound.html +143 -0
  9. data/docs/EnvParser/Error.html +9 -9
  10. data/docs/EnvParser/TypeAlreadyDefinedError.html +143 -0
  11. data/docs/EnvParser/Types.html +128 -0
  12. data/docs/EnvParser/Types/BaseTypes.html +177 -0
  13. data/docs/EnvParser/Types/ChronologyTypes.html +159 -0
  14. data/docs/EnvParser/Types/InternetTypes.html +159 -0
  15. data/docs/EnvParser/UnknownTypeError.html +143 -0
  16. data/docs/EnvParser/UnparseableAutoregisterSpec.html +143 -0
  17. data/docs/EnvParser/ValueNotAllowedError.html +143 -0
  18. data/docs/EnvParser/ValueNotConvertibleError.html +143 -0
  19. data/docs/EnvParserTypes.html +129 -0
  20. data/docs/EnvParserTypes/BaseTypes.html +178 -0
  21. data/docs/EnvParserTypes/ChronologyTypes.html +159 -0
  22. data/docs/EnvParserTypes/InternetTypes.html +159 -0
  23. data/docs/EnvParserTypes/TimeTypes.html +158 -0
  24. data/docs/_index.html +108 -9
  25. data/docs/class_list.html +3 -3
  26. data/docs/css/style.css +7 -9
  27. data/docs/file.README.html +215 -226
  28. data/docs/file_list.html +2 -2
  29. data/docs/frames.html +2 -2
  30. data/docs/index.html +215 -226
  31. data/docs/js/app.js +69 -3
  32. data/docs/method_list.html +13 -5
  33. data/docs/top-level-namespace.html +9 -9
  34. data/env_parser.gemspec +6 -2
  35. data/lib/env_parser.rb +111 -83
  36. data/lib/env_parser/autoregister.rb +3 -0
  37. data/lib/env_parser/errors.rb +40 -0
  38. data/lib/env_parser/types.rb +3 -0
  39. data/lib/env_parser/types/base_types.rb +99 -26
  40. data/lib/env_parser/types/chronology_types.rb +104 -0
  41. data/lib/env_parser/types/internet_types.rb +99 -0
  42. data/lib/env_parser/version.rb +1 -1
  43. metadata +85 -10
@@ -0,0 +1,3 @@
1
+ require 'env_parser'
2
+
3
+ EnvParser.autoregister
@@ -0,0 +1,40 @@
1
+ class EnvParser
2
+ ## Base error class for EnvParser.
3
+ ##
4
+ class Error < ::StandardError
5
+ end
6
+
7
+ ## Error class used to indicate a type has already been defined.
8
+ ##
9
+ class TypeAlreadyDefinedError < Error
10
+ end
11
+
12
+ ## Error class used to indicate the requested "as" type has not been defined.
13
+ ##
14
+ class UnknownTypeError < Error
15
+ end
16
+
17
+ ## Error class used to indicate value given is not convertible to the requested type.
18
+ ##
19
+ class ValueNotConvertibleError < Error
20
+ end
21
+
22
+ ## Error class used to indicate parsed values that do not pass user-validation, either by not
23
+ ## being part of the given "from_set" list, or by failing the "validated_by" Proc or yield-block
24
+ ## check.
25
+ ##
26
+ class ValueNotAllowedError < Error
27
+ end
28
+
29
+ ## Error class used to indicate a missing auto-registration spec file (used by the "autoregister"
30
+ ## feature).
31
+ ##
32
+ class AutoregisterFileNotFound < Error
33
+ end
34
+
35
+ ## Error class used to indicate an unparseable auto-registration spec (used by the "autoregister"
36
+ ## feature).
37
+ ##
38
+ class UnparseableAutoregisterSpec < Error
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ ## Load all files listed in "/lib/env_parser/types".
2
+ ##
3
+ Dir.glob(File.join(__dir__, 'types', '*.rb')).each { |filename| require_relative filename }
@@ -1,39 +1,112 @@
1
1
  require 'env_parser'
2
2
 
3
- EnvParser.define_type(:string, if_unset: '') do |value|
4
- value
5
- end
3
+ ## The parent module for all EnvParser type definition modules.
4
+ ## Exists only for documentation's sake.
5
+ ##
6
+ module EnvParser::Types
7
+ ## Defines types for primitive classes, adding the following:
8
+ ##
9
+ ## <table>
10
+ ## <tbody>
11
+ ## <tr>
12
+ ## <th><code>:as</code> value</th>
13
+ ## <th>type returned</th>
14
+ ## <th>default</th>
15
+ ## <th>notes</th>
16
+ ## </tr>
17
+ ## </tbody>
18
+ ## <tbody>
19
+ ## <tr>
20
+ ## <td>:string</td>
21
+ ## <td>String</td>
22
+ ## <td><code>''</code></td>
23
+ ## <td></td>
24
+ ## </tr>
25
+ ## <tr>
26
+ ## <td>:symbol</td>
27
+ ## <td>Symbol</td>
28
+ ## <td><code>:''</code></td>
29
+ ## <td></td>
30
+ ## </tr>
31
+ ## <tr>
32
+ ## <td>:boolean</td>
33
+ ## <td>TrueValue / FalseValue</td>
34
+ ## <td><code>false</code></td>
35
+ ## <td></td>
36
+ ## </tr>
37
+ ## <tr>
38
+ ## <td>:int / :integer</td>
39
+ ## <td>Integer</td>
40
+ ## <td><code>0</code></td>
41
+ ## <td></td>
42
+ ## </tr>
43
+ ## <tr>
44
+ ## <td>:float / :decimal / :number</td>
45
+ ## <td>Float</td>
46
+ ## <td><code>0.0</code></td>
47
+ ## <td></td>
48
+ ## </tr>
49
+ ## <tr>
50
+ ## <td>:json</td>
51
+ ## <td>&lt; depends on JSON given &gt;</td>
52
+ ## <td><code>nil</code></td>
53
+ ## <td></td>
54
+ ## </tr>
55
+ ## <tr>
56
+ ## <td>:array</td>
57
+ ## <td>Array</td>
58
+ ## <td><code>[]</code></td>
59
+ ## <td></td>
60
+ ## </tr>
61
+ ## <tr>
62
+ ## <td>:hash</td>
63
+ ## <td>Hash</td>
64
+ ## <td><code>{}</code></td>
65
+ ## <td></td>
66
+ ## </tr>
67
+ ## </tbody>
68
+ ## </table>
69
+ ##
70
+ ## Note JSON is parsed using *quirks-mode* (meaning 'true', '25', and 'null' are all considered
71
+ ## valid, parseable JSON).
72
+ ##
73
+ module BaseTypes
74
+ EnvParser.define_type(:string, if_unset: '') do |value|
75
+ value
76
+ end
6
77
 
7
- EnvParser.define_type(:symbol, if_unset: :'', &:to_sym)
78
+ EnvParser.define_type(:symbol, if_unset: :'', &:to_sym)
8
79
 
9
- EnvParser.define_type(:boolean, if_unset: false) do |value|
10
- case value
11
- when '', '0', 'f', 'false' then false
12
- else true
13
- end
14
- end
80
+ EnvParser.define_type(:boolean, if_unset: false) do |value|
81
+ case value
82
+ when '', '0', 'f', 'false' then false
83
+ else true
84
+ end
85
+ end
15
86
 
16
- EnvParser.define_type(:integer, aliases: %i[int], if_unset: 0, &:to_i)
87
+ EnvParser.define_type(:integer, aliases: %i[int], if_unset: 0, &:to_i)
17
88
 
18
- EnvParser.define_type(:float, aliases: %i[decimal number], if_unset: 0.0, &:to_f)
89
+ EnvParser.define_type(:float, aliases: %i[decimal number], if_unset: 0.0, &:to_f)
19
90
 
20
- EnvParser.define_type(:json, if_unset: nil) do |value|
21
- require 'json'
91
+ EnvParser.define_type(:json, if_unset: nil) do |value|
92
+ require 'json'
22
93
 
23
- decoded_json = JSON.parse(value, quirks_mode: true)
24
- { decoded_json: decoded_json }.with_indifferent_access[:decoded_json]
25
- end
94
+ decoded_json = JSON.parse(value, quirks_mode: true)
95
+ { decoded_json: decoded_json }.with_indifferent_access[:decoded_json]
96
+ end
26
97
 
27
- EnvParser.define_type(:array, if_unset: []) do |value|
28
- decoded_json = EnvParser.parse(value, as: :json)
29
- raise(ArgumentError, 'non-array value') unless decoded_json.is_a? Array
98
+ EnvParser.define_type(:array, if_unset: []) do |value|
99
+ decoded_json = EnvParser.parse(value, as: :json)
100
+ raise(EnvParser::ValueNotConvertibleError, 'non-array value') unless decoded_json.is_a? Array
30
101
 
31
- decoded_json
32
- end
102
+ decoded_json
103
+ end
33
104
 
34
- EnvParser.define_type(:hash, if_unset: {}) do |value|
35
- decoded_json = EnvParser.parse(value, as: :json)
36
- raise(ArgumentError, 'non-hash value') unless decoded_json.is_a? Hash
105
+ EnvParser.define_type(:hash, if_unset: {}) do |value|
106
+ decoded_json = EnvParser.parse(value, as: :json)
107
+ raise(EnvParser::ValueNotConvertibleError, 'non-hash value') unless decoded_json.is_a? Hash
37
108
 
38
- decoded_json
109
+ decoded_json
110
+ end
111
+ end
39
112
  end
@@ -0,0 +1,104 @@
1
+ require 'env_parser'
2
+
3
+ ## The parent module for all EnvParser type definition modules.
4
+ ## Exists only for documentation's sake.
5
+ ##
6
+ module EnvParser::Types
7
+ ## Defines types for date/time-related values, adding the following:
8
+ ##
9
+ ## <table>
10
+ ## <tbody>
11
+ ## <tr>
12
+ ## <th><code>:as</code> value</th>
13
+ ## <th>type returned</th>
14
+ ## <th>default</th>
15
+ ## <th>notes</th>
16
+ ## </tr>
17
+ ## </tbody>
18
+ ## <tbody>
19
+ ## <tr>
20
+ ## <td>:date</td>
21
+ ## <td>Date</td>
22
+ ## <td><code>nil</code></td>
23
+ ## <td>
24
+ ## A natural language or ISO8601-parseable date.
25
+ ## <br />
26
+ ## Actual interpretation of the value is handled by the *chronic* gem.
27
+ ## </td>
28
+ ## </tr>
29
+ ## <tr>
30
+ ## <td>:time / :datetime</td>
31
+ ## <td>Time</td>
32
+ ## <td><code>nil</code></td>
33
+ ## <td>
34
+ ## A natural language or ISO8601-parseable date and time.
35
+ ## <br />
36
+ ## Actual interpretation of the value is handled by the *chronic* gem.
37
+ ## </td>
38
+ ## </tr>
39
+ ## <tr>
40
+ ## <td>:duration</td>
41
+ ## <td>Numeric</td>
42
+ ## <td><code>nil</code></td>
43
+ ## <td>
44
+ ## A natural language or ISO8601-parseable period.
45
+ ## <br />
46
+ ## Value returned is the number of seconds in the given period.
47
+ ## <br />
48
+ ## Actual interpretation of the value is handled by the *chronic_duration* gem.
49
+ ## </td>
50
+ ## </tr>
51
+ ## </tbody>
52
+ ## </table>
53
+ ##
54
+ module ChronologyTypes
55
+ EnvParser.define_type(:date, if_unset: nil) do |value|
56
+ require 'chronic'
57
+
58
+ begin
59
+ value = Chronic.parse value, guess: :begin
60
+ raise StandardError unless value.is_a? Time
61
+ rescue StandardError
62
+ raise EnvParser::ValueNotConvertibleError, 'non-date value'
63
+ end
64
+
65
+ value.to_date
66
+ end
67
+
68
+ EnvParser.define_type(:time, aliases: :datetime, if_unset: nil) do |value|
69
+ require 'chronic'
70
+
71
+ begin
72
+ value = Chronic.parse value, guess: :begin
73
+ raise StandardError unless value.is_a? Time
74
+ rescue StandardError
75
+ raise EnvParser::ValueNotConvertibleError, 'non-time value'
76
+ end
77
+
78
+ value
79
+ end
80
+
81
+ EnvParser.define_type(:duration, if_unset: nil) do |value|
82
+ require 'chronic_duration'
83
+
84
+ begin
85
+ original_raise_setting = ChronicDuration.raise_exceptions
86
+ ChronicDuration.raise_exceptions = true
87
+
88
+ ## With `raise_exceptions` set, ChronicDuration will fail on the "P" and "T" in ISO8601
89
+ ## periods, so we have to check for and remove them.
90
+ ##
91
+ iso_period = %r{^\s*P(?:[0-9.]Y)?(?:[0-9.]M)?(?:[0-9.]W)?(?:[0-9.]D)?(?:T(?:[0-9.]H)?(?:[0-9.]M)?(?:[0-9.]S)?)?\s*$} ## rubocop:disable Layout/LineLength
92
+ value = value.delete 'PT' if value =~ iso_period
93
+
94
+ value = ChronicDuration.parse value, keep_zero: true
95
+ rescue StandardError
96
+ raise EnvParser::ValueNotConvertibleError, 'non-duration value'
97
+ ensure
98
+ ChronicDuration.raise_exceptions = original_raise_setting
99
+ end
100
+
101
+ value
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,99 @@
1
+ require 'env_parser'
2
+
3
+ ## The parent module for all EnvParser type definition modules.
4
+ ## Exists only for documentation's sake.
5
+ ##
6
+ module EnvParser::Types
7
+ ## Defines types for internet-related values, adding the following:
8
+ ##
9
+ ## <table>
10
+ ## <tbody>
11
+ ## <tr>
12
+ ## <th><code>:as</code> value</th>
13
+ ## <th>type returned</th>
14
+ ## <th>default</th>
15
+ ## <th>notes</th>
16
+ ## </tr>
17
+ ## </tbody>
18
+ ## <tbody>
19
+ ## <tr>
20
+ ## <td>:ipv4_address</td>
21
+ ## <td>String</td>
22
+ ## <td><code>nil</code></td>
23
+ ## <td>
24
+ ## An IPv4 address in 4-octet dot-decimal notation,
25
+ ## <br />
26
+ ## with no CIDR or subnet suffix (e.g. <code>'192.168.0.1'</code>).
27
+ ## </td>
28
+ ## </tr>
29
+ ## <tr>
30
+ ## <td>:ipv6_address</td>
31
+ ## <td>String</td>
32
+ ## <td><code>nil</code></td>
33
+ ## <td>An IPv6 address, in RFC5952 format.</td>
34
+ ## </tr>
35
+ ## <tr>
36
+ ## <td>:network_port / :port</td>
37
+ ## <td>Integer</td>
38
+ ## <td><code>nil</code></td>
39
+ ## <td></td>
40
+ ## </tr>
41
+ ## <tr>
42
+ ## <td>:email_address</td>
43
+ ## <td>String</td>
44
+ ## <td><code>nil</code></td>
45
+ ## <td>
46
+ ## A "simple" email address, containing only a username and a domain.
47
+ ## <br />
48
+ ## Note this does not guarantee RFC5322-conformity.
49
+ ## </td>
50
+ ## </tr>
51
+ ## </tbody>
52
+ ## </table>
53
+ ##
54
+ module InternetTypes
55
+ EnvParser.define_type(:ipv4_address, if_unset: nil) do |value|
56
+ begin
57
+ require 'socket'
58
+ address = Addrinfo.getaddrinfo(value, nil, Socket::AF_INET, nil, Socket::IPPROTO_TCP).first
59
+ raise StandardError unless address.ipv4?
60
+ rescue StandardError
61
+ raise EnvParser::ValueNotConvertibleError, 'non-ip value'
62
+ end
63
+
64
+ address.ip_address
65
+ end
66
+
67
+ EnvParser.define_type(:ipv6_address, if_unset: nil) do |value|
68
+ begin
69
+ require 'socket'
70
+ address = Addrinfo.getaddrinfo(value, nil, Socket::AF_INET6, nil, Socket::IPPROTO_TCP).first
71
+ raise StandardError unless address.ipv6?
72
+ rescue StandardError
73
+ raise EnvParser::ValueNotConvertibleError, 'non-ip value'
74
+ end
75
+
76
+ address.ip_address
77
+ end
78
+
79
+ EnvParser.define_type(:network_port, aliases: :port, if_unset: nil) do |value|
80
+ begin
81
+ Integer(value)
82
+ rescue ArgumentError
83
+ raise(EnvParser::ValueNotConvertibleError, 'non-numeric value')
84
+ end
85
+
86
+ value = value.to_i
87
+ raise(EnvParser::ValueNotAllowedError, 'value out of range') unless (0..65535).cover?(value)
88
+
89
+ value
90
+ end
91
+
92
+ EnvParser.define_type(:email_address, if_unset: nil) do |value|
93
+ simple_email = %r[^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$]i ## rubocop:disable Layout/LineLength
94
+ raise(EnvParser::ValueNotConvertibleError, 'not an email') unless value.match?(simple_email)
95
+
96
+ value
97
+ end
98
+ end
99
+ end
@@ -1,3 +1,3 @@
1
1
  class EnvParser
2
- VERSION = '0.8.0'.freeze
2
+ VERSION = '1.3.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: env_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nestor Custodio
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-12-25 00:00:00.000000000 Z
11
+ date: 2021-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.16'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.16'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: activesupport
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +108,34 @@ dependencies:
80
108
  - - ">="
81
109
  - !ruby/object:Gem::Version
82
110
  version: 5.0.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: chronic
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: chronic_duration
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
83
139
  description:
84
140
  email:
85
141
  - sakimorix@gmail.com
@@ -102,9 +158,24 @@ files:
102
158
  - bin/console
103
159
  - bin/setup
104
160
  - docs/EnvParser.html
161
+ - docs/EnvParser/AutoregisterFileNotFound.html
105
162
  - docs/EnvParser/Error.html
106
163
  - docs/EnvParser/TypeAlreadyDefined.html
164
+ - docs/EnvParser/TypeAlreadyDefinedError.html
165
+ - docs/EnvParser/Types.html
166
+ - docs/EnvParser/Types/BaseTypes.html
167
+ - docs/EnvParser/Types/ChronologyTypes.html
168
+ - docs/EnvParser/Types/InternetTypes.html
169
+ - docs/EnvParser/UnknownTypeError.html
170
+ - docs/EnvParser/UnparseableAutoregisterSpec.html
107
171
  - docs/EnvParser/ValueNotAllowed.html
172
+ - docs/EnvParser/ValueNotAllowedError.html
173
+ - docs/EnvParser/ValueNotConvertibleError.html
174
+ - docs/EnvParserTypes.html
175
+ - docs/EnvParserTypes/BaseTypes.html
176
+ - docs/EnvParserTypes/ChronologyTypes.html
177
+ - docs/EnvParserTypes/InternetTypes.html
178
+ - docs/EnvParserTypes/TimeTypes.html
108
179
  - docs/_index.html
109
180
  - docs/class_list.html
110
181
  - docs/css/common.css
@@ -121,7 +192,12 @@ files:
121
192
  - docs/top-level-namespace.html
122
193
  - env_parser.gemspec
123
194
  - lib/env_parser.rb
195
+ - lib/env_parser/autoregister.rb
196
+ - lib/env_parser/errors.rb
197
+ - lib/env_parser/types.rb
124
198
  - lib/env_parser/types/base_types.rb
199
+ - lib/env_parser/types/chronology_types.rb
200
+ - lib/env_parser/types/internet_types.rb
125
201
  - lib/env_parser/version.rb
126
202
  homepage: https://github.com/nestor-custodio/env_parser
127
203
  licenses:
@@ -142,8 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
218
  - !ruby/object:Gem::Version
143
219
  version: '0'
144
220
  requirements: []
145
- rubyforge_project:
146
- rubygems_version: 2.6.13
221
+ rubygems_version: 3.1.4
147
222
  signing_key:
148
223
  specification_version: 4
149
224
  summary: A tool for painless parsing and validation of environment variables.