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
@@ -120,6 +120,49 @@ function summaryToggle() {
120
120
  } else { localStorage.summaryCollapsed = "expand"; }
121
121
  }
122
122
 
123
+ function constantSummaryToggle() {
124
+ $('.constants_summary_toggle').click(function(e) {
125
+ e.preventDefault();
126
+ localStorage.summaryCollapsed = $(this).text();
127
+ $('.constants_summary_toggle').each(function() {
128
+ $(this).text($(this).text() == "collapse" ? "expand" : "collapse");
129
+ var next = $(this).parent().parent().nextAll('dl.constants').first();
130
+ if (next.hasClass('compact')) {
131
+ next.toggle();
132
+ next.nextAll('dl.constants').first().toggle();
133
+ }
134
+ else if (next.hasClass('constants')) {
135
+ var list = $('<dl class="constants compact" />');
136
+ list.html(next.html());
137
+ list.find('dt').each(function() {
138
+ $(this).addClass('summary_signature');
139
+ $(this).text( $(this).text().split('=')[0]);
140
+ if ($(this).has(".deprecated").length) {
141
+ $(this).addClass('deprecated');
142
+ };
143
+ });
144
+ // Add the value of the constant as "Tooltip" to the summary object
145
+ list.find('pre.code').each(function() {
146
+ console.log($(this).parent());
147
+ var dt_element = $(this).parent().prev();
148
+ var tooltip = $(this).text();
149
+ if (dt_element.hasClass("deprecated")) {
150
+ tooltip = 'Deprecated. ' + tooltip;
151
+ };
152
+ dt_element.attr('title', tooltip);
153
+ });
154
+ list.find('.docstring, .tags, dd').remove();
155
+ next.before(list);
156
+ next.toggle();
157
+ }
158
+ });
159
+ return false;
160
+ });
161
+ if (localStorage.summaryCollapsed == "collapse") {
162
+ $('.constants_summary_toggle').first().click();
163
+ } else { localStorage.summaryCollapsed = "expand"; }
164
+ }
165
+
123
166
  function generateTOC() {
124
167
  if ($('#filecontents').length === 0) return;
125
168
  var _toc = $('<ol class="top"></ol>');
@@ -128,6 +171,7 @@ function generateTOC() {
128
171
  var counter = 0;
129
172
  var tags = ['h2', 'h3', 'h4', 'h5', 'h6'];
130
173
  var i;
174
+ var curli;
131
175
  if ($('#filecontents h1').length > 1) tags.unshift('h1');
132
176
  for (i = 0; i < tags.length; i++) { tags[i] = '#filecontents ' + tags[i]; }
133
177
  var lastTag = parseInt(tags[0][1], 10);
@@ -147,15 +191,25 @@ function generateTOC() {
147
191
  }
148
192
  if (thisTag > lastTag) {
149
193
  for (i = 0; i < thisTag - lastTag; i++) {
150
- var tmp = $('<ol/>'); toc.append(tmp); toc = tmp;
194
+ if ( typeof(curli) == "undefined" ) {
195
+ curli = $('<li/>');
196
+ toc.append(curli);
197
+ }
198
+ toc = $('<ol/>');
199
+ curli.append(toc);
200
+ curli = undefined;
151
201
  }
152
202
  }
153
203
  if (thisTag < lastTag) {
154
- for (i = 0; i < lastTag - thisTag; i++) toc = toc.parent();
204
+ for (i = 0; i < lastTag - thisTag; i++) {
205
+ toc = toc.parent();
206
+ toc = toc.parent();
207
+ }
155
208
  }
156
209
  var title = $(this).attr('toc-title');
157
210
  if (typeof(title) == "undefined") title = $(this).text();
158
- toc.append('<li><a href="#' + this.id + '">' + title + '</a></li>');
211
+ curli =$('<li><a href="#' + this.id + '">' + title + '</a></li>');
212
+ toc.append(curli);
159
213
  lastTag = thisTag;
160
214
  });
161
215
  if (!show) return;
@@ -232,6 +286,16 @@ function mainFocus() {
232
286
  setTimeout(function() { $('#main').focus(); }, 10);
233
287
  }
234
288
 
289
+ function navigationChange() {
290
+ // This works around the broken anchor navigation with the YARD template.
291
+ window.onpopstate = function() {
292
+ var hash = window.location.hash;
293
+ if (hash !== '' && $(hash)[0]) {
294
+ $(hash)[0].scrollIntoView();
295
+ }
296
+ };
297
+ }
298
+
235
299
  $(document).ready(function() {
236
300
  navResizer();
237
301
  navExpander();
@@ -241,8 +305,10 @@ $(document).ready(function() {
241
305
  searchFrameButtons();
242
306
  linkSummaries();
243
307
  summaryToggle();
308
+ constantSummaryToggle();
244
309
  generateTOC();
245
310
  mainFocus();
311
+ navigationChange();
246
312
  });
247
313
 
248
314
  })();
@@ -4,9 +4,9 @@
4
4
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
5
  <meta charset="utf-8" />
6
6
 
7
- <link rel="stylesheet" href="css/full_list.css" type="text/css" media="screen" charset="utf-8" />
7
+ <link rel="stylesheet" href="css/full_list.css" type="text/css" media="screen" />
8
8
 
9
- <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
9
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" />
10
10
 
11
11
 
12
12
 
@@ -54,13 +54,21 @@
54
54
 
55
55
  <li class="even ">
56
56
  <div class="item">
57
- <span class='object_link'><a href="EnvParser.html#define_type-class_method" title="EnvParser.define_type (method)">define_type</a></span>
57
+ <span class='object_link'><a href="EnvParser.html#autoregister-class_method" title="EnvParser.autoregister (method)">autoregister</a></span>
58
58
  <small>EnvParser</small>
59
59
  </div>
60
60
  </li>
61
61
 
62
62
 
63
63
  <li class="odd ">
64
+ <div class="item">
65
+ <span class='object_link'><a href="EnvParser.html#define_type-class_method" title="EnvParser.define_type (method)">define_type</a></span>
66
+ <small>EnvParser</small>
67
+ </div>
68
+ </li>
69
+
70
+
71
+ <li class="even ">
64
72
  <div class="item">
65
73
  <span class='object_link'><a href="top-level-namespace.html#filename-instance_method" title="#filename (method)">#filename</a></span>
66
74
  <small>Top Level Namespace</small>
@@ -68,7 +76,7 @@
68
76
  </li>
69
77
 
70
78
 
71
- <li class="even ">
79
+ <li class="odd ">
72
80
  <div class="item">
73
81
  <span class='object_link'><a href="EnvParser.html#parse-class_method" title="EnvParser.parse (method)">parse</a></span>
74
82
  <small>EnvParser</small>
@@ -76,7 +84,7 @@
76
84
  </li>
77
85
 
78
86
 
79
- <li class="odd ">
87
+ <li class="even ">
80
88
  <div class="item">
81
89
  <span class='object_link'><a href="EnvParser.html#register-class_method" title="EnvParser.register (method)">register</a></span>
82
90
  <small>EnvParser</small>
@@ -6,15 +6,15 @@
6
6
  <title>
7
7
  Top Level Namespace
8
8
 
9
- &mdash; Documentation by YARD 0.9.11
9
+ &mdash; Documentation by YARD 0.9.26
10
10
 
11
11
  </title>
12
12
 
13
- <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
14
 
15
- <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
16
 
17
- <script type="text/javascript" charset="utf-8">
17
+ <script type="text/javascript">
18
18
  pathId = "";
19
19
  relpath = '';
20
20
  </script>
@@ -162,12 +162,12 @@
162
162
  <pre class="lines">
163
163
 
164
164
 
165
- 315</pre>
165
+ 3</pre>
166
166
  </td>
167
167
  <td>
168
- <pre class="code"><span class="info file"># File 'lib/env_parser.rb', line 315</span>
168
+ <pre class="code"><span class="info file"># File 'lib/env_parser/types.rb', line 3</span>
169
169
 
170
- <span class='const'>Dir</span><span class='period'>.</span><span class='id identifier rubyid_glob'>glob</span><span class='lparen'>(</span><span class='const'>File</span><span class='period'>.</span><span class='id identifier rubyid_join'>join</span><span class='lparen'>(</span><span class='id identifier rubyid___dir__'>__dir__</span><span class='comma'>,</span> <span class='qwords_beg'>%w[</span><span class='tstring_content'>env_parser</span><span class='words_sep'> </span><span class='tstring_content'>types</span><span class='words_sep'> </span><span class='tstring_content'>*.rb</span><span class='words_sep'>]</span><span class='rparen'>)</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_each'>each</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_filename'>filename</span><span class='op'>|</span> <span class='id identifier rubyid_require_relative'>require_relative</span> <span class='id identifier rubyid_filename'>filename</span> <span class='rbrace'>}</span></pre>
170
+ <span class='const'>Dir</span><span class='period'>.</span><span class='id identifier rubyid_glob'>glob</span><span class='lparen'>(</span><span class='const'>File</span><span class='period'>.</span><span class='id identifier rubyid_join'>join</span><span class='lparen'>(</span><span class='id identifier rubyid___dir__'>__dir__</span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>types</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>*.rb</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_each'>each</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_filename'>filename</span><span class='op'>|</span> <span class='id identifier rubyid_require_relative'>require_relative</span> <span class='id identifier rubyid_filename'>filename</span> <span class='rbrace'>}</span></pre>
171
171
  </td>
172
172
  </tr>
173
173
  </table>
@@ -178,9 +178,9 @@
178
178
  </div>
179
179
 
180
180
  <div id="footer">
181
- Generated on Sun Dec 24 19:33:35 2017 by
181
+ Generated on Sat Jan 2 17:31:17 2021 by
182
182
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
183
- 0.9.11 (ruby-2.4.2).
183
+ 0.9.26 (ruby-2.7.2).
184
184
  </div>
185
185
 
186
186
  </div>
@@ -20,10 +20,14 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_development_dependency 'bundler', '~> 1.16'
24
- spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'bundler', '~> 2.0'
24
+ spec.add_development_dependency 'rake'
25
25
  spec.add_development_dependency 'rspec', '~> 3.0'
26
26
  spec.add_development_dependency 'rspec_junit_formatter'
27
+ spec.add_development_dependency 'rubocop', '1.7'
28
+ spec.add_development_dependency 'yard'
27
29
 
28
30
  spec.add_dependency 'activesupport', '>= 5.0.0'
31
+ spec.add_dependency 'chronic'
32
+ spec.add_dependency 'chronic_duration'
29
33
  end
@@ -1,61 +1,52 @@
1
+ require 'env_parser/errors'
1
2
  require 'env_parser/version'
2
3
  require 'active_support/all'
4
+ require 'psych'
3
5
 
4
6
  ## The EnvParser class simplifies parsing of environment variables as different data types.
5
7
  ##
6
8
  class EnvParser
7
- ## Base exception class for EnvParser.
9
+ ## The default filename to use for {.autoregister} requests.
8
10
  ##
9
- class Error < ::StandardError
10
- end
11
-
12
- ## Exception class used to indicate parsed values not allowed per a "from_set" option.
13
- ##
14
- class ValueNotAllowed < Error
15
- end
16
-
17
- ## Exception class used to indicate a type has already been defined.
18
- ##
19
- class TypeAlreadyDefined < Error
20
- end
11
+ AUTOREGISTER_FILE = '.env_parser.yml'.freeze
21
12
 
22
13
  class << self
23
- ## Defines a new type for use as the "as" option on a subsequent `.parse` or `.register` call.
14
+ ## Defines a new type for use as the "as" option on a subsequent {.parse} or {.register} call.
24
15
  ##
25
16
  ## @param name [Symbol]
26
17
  ## The name to assign to the type.
27
18
  ##
28
- ## @option options aliases [Array<Symbol>]
19
+ ## @option options [Array<Symbol>] aliases
29
20
  ## An array of additional names you'd like to see refer to this same type.
30
21
  ##
31
- ## @option options if_unset
22
+ ## @option options if_unset (nil)
32
23
  ## Specifies a "sensible default" to return for this type if the value being parsed (via
33
- ## `.parse` or `.register`) is either unset (`nil`) or blank (`''`). Note this may be
34
- ## overridden by the user via the `.parse`/`.register` "if_unset" option.
24
+ ## {.parse} or {.register}) is either unset (`nil`) or blank (`''`). Note this may be
25
+ ## overridden by the user via the {.parse}/{.register} "if_unset" option.
35
26
  ##
36
- ## @yield
27
+ ## @yield [value]
37
28
  ## A block to act as the parser for the this type. If no block is given, an ArgumentError is
38
29
  ## raised.
39
30
  ##
40
- ## When the type defined is used via a `.parse`/`.register` call, this block is invoked with
31
+ ## When the type defined is used via a {.parse}/{.register} call, this block is invoked with
41
32
  ## the value to be parsed. Said value is guaranteed to be a non-empty String (the "if_unset"
42
33
  ## check will have already run), but no other assurances as to content are given. The block
43
34
  ## should return the final output of parsing the given String value as the type being defined.
44
35
  ##
45
36
  ## If the value given cannot be sensibly parsed into the type defined, the block should raise
46
- ## an EnvParser::ValueNotAllowed exception.
37
+ ## an {EnvParser::ValueNotConvertibleError}.
47
38
  ##
48
39
  ## @return [nil]
49
40
  ## This generates no usable value.
50
41
  ##
51
- ## @raise [ArgumentError, EnvParser::TypeAlreadyDefined]
42
+ ## @raise [ArgumentError, EnvParser::TypeAlreadyDefinedError]
52
43
  ##
53
44
  def define_type(name, options = {}, &parser)
54
45
  raise(ArgumentError, 'no parsing block given') unless block_given?
55
46
 
56
47
  given_types = (Array(name) + Array(options[:aliases])).map(&:to_s).map(&:to_sym)
57
48
  given_types.each do |type|
58
- raise(TypeAlreadyDefined, "cannot redefine #{type.inspect}") if known_types.key?(type)
49
+ raise(TypeAlreadyDefinedError, "cannot redefine #{type.inspect}") if known_types.key?(type)
59
50
 
60
51
  known_types[type] = {
61
52
  parser: parser,
@@ -72,12 +63,13 @@ class EnvParser
72
63
  ## The value to parse/interpret. If a String is given, the value will be used as-is. If a
73
64
  ## Symbol is given, the ENV value for the matching string key will be used.
74
65
  ##
75
- ## @option options as [Symbol]
66
+ ## @option options [Symbol] as
76
67
  ## The expected return type. A best-effort attempt is made to convert the source String to the
77
68
  ## requested type.
78
69
  ##
79
- ## If no "as" option is given (or the "as" value given has not been defined), an ArgumentError
80
- ## exception is raised.
70
+ ## If no "as" option is given, an ArgumentError is raised. If the "as" option given is unknown
71
+ ## (the given type has not been previously defined via {.define_type}), an
72
+ ## {EnvParser::UnknownTypeError} is raised.
81
73
  ##
82
74
  ## @option options if_unset
83
75
  ## Specifies the default value to return if the given "value" is either unset (`nil`) or blank
@@ -85,43 +77,45 @@ class EnvParser
85
77
  ## change having been made. If unspecified, the "default" value for `nil`/`''` input will
86
78
  ## depend on the "as" type.
87
79
  ##
88
- ## @option options from_set [Array, Range]
80
+ ## @option options [Array, Range] from_set
89
81
  ## Gives a limited set of allowed values (after type conversion). If, after parsing, the final
90
- ## value is not included in the "from_set" list/range, an EnvParser::ValueNotAllowed exception
91
- ## is raised.
82
+ ## value is not included in the "from_set" list/range, an {EnvParser::ValueNotAllowedError} is
83
+ ## raised.
92
84
  ##
93
85
  ## Note that if the "if_unset" option is given and the value to parse is `nil`/`''`, the
94
86
  ## "if_unset" value will be returned, even if it is not part of the "from_set" list/range.
95
87
  ##
96
88
  ## Also note that, due to the nature of the lookup, the "from_set" option is only available
97
89
  ## for scalar values (i.e. not arrays, hashes, or other enumerables). An attempt to use the
98
- ## "from_set" option with a non-scalar value will raise an ArgumentError exception.
90
+ ## "from_set" option with a non-scalar value will raise an ArgumentError.
99
91
  ##
100
- ## @option options validated_by [Proc]
101
- ## If given, the "validated_by" proc is called with the parsed value (after type conversion)
92
+ ## @option options [Proc] validated_by
93
+ ## If given, the "validated_by" Proc is called with the parsed value (after type conversion)
102
94
  ## as its sole argument. This allows for user-defined validation of the parsed value beyond
103
- ## what can be enforced by use of the "from_set" option alone. If the proc's return value is
104
- ## `#blank?`, an EnvParser::ValueNotAllowed exception is raised. To accomodate your syntax of
105
- ## choice, this validation proc may be given as a yield block instead.
95
+ ## what can be enforced by use of the "from_set" option alone. If the Proc's return value is
96
+ ## `#blank?`, an {EnvParser::ValueNotAllowedError} is raised. To accomodate your syntax of
97
+ ## choice, this validation Proc may be given as a block instead.
106
98
  ##
107
99
  ## Note that this option is intended to provide an inspection mechanism only -- no mutation
108
- ## of the parsed value should occur within the given proc. To that end, the argument passed is
100
+ ## of the parsed value should occur within the given Proc. To that end, the argument passed is
109
101
  ## a *frozen* duplicate of the parsed value.
110
102
  ##
111
103
  ## @yield [value]
112
- ## A block (if given) is treated exactly as the "validated_by" Proc would. Although there is
113
- ## no compelling reason to provide both a "validated_by" proc *and* a validation block, there
114
- ## is no technical limitation preventing this. **If both are given, both validation checks
115
- ## must pass.**
104
+ ## A block (if given) is treated exactly as the "validated_by" Proc would.
116
105
  ##
117
- ## @raise [ArgumentError, EnvParser::ValueNotAllowed]
106
+ ## Although there is no compelling reason to provide both a "validated_by" Proc *and* a
107
+ ## validation block, there is no technical limitation preventing this. **If both are given,
108
+ ## both validation checks must pass.**
109
+ ##
110
+ ## @raise [ArgumentError, EnvParser::UnknownTypeError, EnvParser::ValueNotAllowedError]
118
111
  ##
119
112
  def parse(value, options = {}, &validation_block)
120
113
  value = ENV[value.to_s] if value.is_a? Symbol
121
114
  value = value.to_s
122
115
 
123
116
  type = known_types[options[:as]]
124
- raise(ArgumentError, "invalid `as` parameter: #{options[:as].inspect}") unless type
117
+ raise(ArgumentError, 'missing `as` parameter') unless options.key?(:as)
118
+ raise(UnknownTypeError, "invalid `as` parameter: #{options[:as].inspect}") unless type
125
119
 
126
120
  return (options.key?(:if_unset) ? options[:if_unset] : type[:if_unset]) if value.blank?
127
121
 
@@ -134,9 +128,8 @@ class EnvParser
134
128
 
135
129
  ## Parses the referenced value and creates a matching constant in the requested context.
136
130
  ##
137
- ## Multiple calls to "register" may be shortcutted by passing in a Hash with the same keys as
138
- ## those in the "from" Hash and each value being the "register" options set for each variable's
139
- ## "register" call.
131
+ ## Multiple calls to {.register} may be shortcutted by passing in a Hash whose keys are the
132
+ ## variable names and whose values are the options set for each variable's {.register} call.
140
133
  ##
141
134
  ## <pre>
142
135
  ## ## Example shortcut usage:
@@ -153,40 +146,39 @@ class EnvParser
153
146
  ## </pre>
154
147
  ##
155
148
  ## @param name
156
- ## The name of the value to parse/interpret from the "from" Hash. If the "from" value is ENV,
157
- ## you may give a Symbol and the corresponding String key will be used instead.
149
+ ## The name of the value to parse/interpret from the "from" Hash. If the "from" value is
150
+ ## `ENV`, you may give a Symbol and the corresponding String key will be used instead.
158
151
  ##
159
- ## @option options from [Hash]
160
- ## The source Hash from which to pull the value referenced by the "name" key. Defaults to ENV.
152
+ ## @option options [Hash] from (ENV)
153
+ ## The source Hash from which to pull the value referenced by the "name" key.
161
154
  ##
162
- ## @option options within [Module, Class]
163
- ## The module or class in which the constant should be created. Defaults to Kernel (making it
164
- ## a global constant).
155
+ ## @option options [Module, Class] within (Kernel)
156
+ ## The module or class in which the constant should be created. Creates global constants by
157
+ ## default.
165
158
  ##
166
- ## @option options as [Symbol]
167
- ## See `.parse`.
159
+ ## @option options [Symbol] as
160
+ ## See {.parse}.
168
161
  ##
169
162
  ## @option options if_unset
170
- ## See `.parse`.
163
+ ## See {.parse}.
171
164
  ##
172
- ## @option options from_set [Array, Range]
173
- ## See `.parse`.
165
+ ## @option options [Array, Range] from_set
166
+ ## See {.parse}.
174
167
  ##
175
- ## @option options validated_by [Proc]
176
- ## See `.parse`.
168
+ ## @option options [Proc] validated_by
169
+ ## See {.parse}.
177
170
  ##
178
171
  ## @yield [value]
179
- ## A block (if given) is treated exactly as in `.parse`. Note, however, that a single yield
180
- ## block cannot be used to register multiple constants simultaneously -- each value needing
181
- ## validation must give its own "validated_by" proc.
172
+ ## A block (if given) is treated exactly as in {.parse}. Note, however, that a single block
173
+ ## cannot be used to register multiple constants simultaneously -- each value needing
174
+ ## validation must give its own "validated_by" Proc.
182
175
  ##
183
176
  ## @raise [ArgumentError]
184
177
  ##
185
178
  def register(name, options = {}, &validation_block)
186
- ## We want to allow for registering multiple variables simultaneously via a single `.register`
187
- ## method call.
179
+ ## Allow for registering multiple variables simultaneously via a single call.
188
180
  if name.is_a? Hash
189
- raise ArgumentError, 'cannot register multiple values with one yield block' if block_given?
181
+ raise(ArgumentError, 'cannot register multiple values with one block') if block_given?
190
182
  return register_all(name)
191
183
  end
192
184
 
@@ -202,9 +194,7 @@ class EnvParser
202
194
  name = name.to_s
203
195
  end
204
196
 
205
- unless from.is_a?(Hash)
206
- raise ArgumentError, "invalid `from` parameter: #{from.class}"
207
- end
197
+ raise ArgumentError, "invalid `from` parameter: #{from.class}" unless from.is_a? Hash
208
198
 
209
199
  unless within.is_a?(Module) || within.is_a?(Class)
210
200
  raise ArgumentError, "invalid `within` parameter: #{within.inspect}"
@@ -217,14 +207,14 @@ class EnvParser
217
207
  value
218
208
  end
219
209
 
220
- ## Creates ENV bindings for EnvParser.parse and EnvParser.register proxy methods.
210
+ ## Creates ENV bindings for {.parse} and {.register} proxy methods.
221
211
  ##
222
212
  ## The sole difference between these proxy methods and their EnvParser counterparts is that
223
213
  ## ENV.parse will interpret any value given as an ENV key (as a String), not the given value
224
214
  ## itself. i.e. ENV.parse('XYZ', ...) is equivalent to EnvParser.parse(ENV['XYZ'], ...)
225
215
  ##
226
216
  ## @return [ENV]
227
- ## This generates no usable value, so we may as well return ENV for chaining?
217
+ ## This generates no usable value.
228
218
  ##
229
219
  def add_env_bindings
230
220
  ENV.instance_eval do
@@ -240,6 +230,47 @@ class EnvParser
240
230
  ENV
241
231
  end
242
232
 
233
+ ## Reads an "autoregister" file and registers the ENV constants defined therein.
234
+ ##
235
+ ## The "autoregister" file is read, parsed as YAML, sanitized for use as a parameter to
236
+ ## {.register_all}, and then passed along for processing. The return value from that
237
+ ## {.register_all} call is passed through.
238
+ ##
239
+ ## @param filename [String]
240
+ ## A path for the autoregister file to parse and process. Defaults to
241
+ ## {EnvParser::AUTOREGISTER_FILE} if unset.
242
+ ##
243
+ ## @return [Hash]
244
+ ## The return value from the {.register_all} call that handles the actual registration.
245
+ ##
246
+ ## @raise [EnvParser::AutoregisterFileNotFound, EnvParser::UnparseableAutoregisterSpec]
247
+ ##
248
+ def autoregister(filename = nil)
249
+ filename ||= AUTOREGISTER_FILE
250
+ autoregister_spec = Psych.load_file(filename)
251
+
252
+ autoregister_spec.deep_symbolize_keys!
253
+ autoregister_spec.transform_values! do |spec|
254
+ sanitized = spec.slice(:as, :within, :if_unset, :from_set)
255
+ sanitized[:as] = sanitized[:as].to_sym if sanitized.key? :as
256
+ sanitized[:within] = sanitized[:within].constantize if sanitized.key? :within
257
+
258
+ sanitized
259
+ end
260
+
261
+ register_all autoregister_spec
262
+
263
+ ## Psych raises an Errno::ENOENT on file-not-found.
264
+ ##
265
+ rescue Errno::ENOENT
266
+ raise EnvParser::AutoregisterFileNotFound, %(file not found: "#{filename}")
267
+
268
+ ## Psych raises a Psych::SyntaxError on unparseable YAML.
269
+ ##
270
+ rescue Psych::SyntaxError => e
271
+ raise EnvParser::UnparseableAutoregisterSpec, "malformed YAML in spec file: #{e.message}"
272
+ end
273
+
243
274
  private
244
275
 
245
276
  ## Class instance variable for storing known type data.
@@ -256,7 +287,7 @@ class EnvParser
256
287
  ## @return [nil]
257
288
  ## This generates no usable value.
258
289
  ##
259
- ## @raise [ArgumentError, EnvParser::ValueNotAllowed]
290
+ ## @raise [ArgumentError, EnvParser::ValueNotAllowedError]
260
291
  ##
261
292
  def check_for_set_inclusion(value, set: nil)
262
293
  if value.respond_to?(:each)
@@ -267,9 +298,7 @@ class EnvParser
267
298
  raise ArgumentError, "invalid `from_set` parameter type: #{set.class}"
268
299
  end
269
300
 
270
- raise ValueNotAllowed, 'parsed value not in allowed list/range' unless set.include?(value)
271
-
272
- nil
301
+ raise(ValueNotAllowedError, 'parsed value not in allowed set') unless set.include?(value)
273
302
  end
274
303
 
275
304
  ## Verifies that the given "value" passes both the "proc" and "block" validations.
@@ -281,18 +310,17 @@ class EnvParser
281
310
  ## @return [nil]
282
311
  ## This generates no usable value.
283
312
  ##
284
- ## @raise [EnvParser::ValueNotAllowed]
313
+ ## @raise [EnvParser::ValueNotAllowedError]
285
314
  ##
286
315
  def check_user_defined_validations(value, proc: nil, block: nil)
287
316
  immutable_value = value.dup.freeze
288
- error = 'parsed value failed user validation'
289
- raise ValueNotAllowed, error unless [proc, block].compact.all? { |i| i.call(immutable_value) }
317
+ all_tests_passed = [proc, block].compact.all? { |i| i.call(immutable_value) }
290
318
 
291
- nil
319
+ raise(ValueNotAllowedError, 'parsed value failed user validation') unless all_tests_passed
292
320
  end
293
321
 
294
- ## Receives a list of "register" calls to make, as a Hash keyed with variable names and the
295
- ## values being each "register" call's option set.
322
+ ## Receives a list of {.register} calls to make, as a Hash keyed with variable names and the
323
+ ## values being each {.register} call's option set.
296
324
  ##
297
325
  ## @param list [Hash]
298
326
  ##
@@ -301,7 +329,7 @@ class EnvParser
301
329
  ## @raise [ArgumentError]
302
330
  ##
303
331
  def register_all(list)
304
- raise ArgumentError, "invalid 'list' parameter type: #{list.class}" unless list.is_a?(Hash)
332
+ raise(ArgumentError, "invalid 'list' parameter type: #{list.class}") unless list.is_a? Hash
305
333
 
306
334
  list.to_a.each_with_object({}) do |tuple, output|
307
335
  output[tuple.first] = register(tuple.first, tuple.second)
@@ -310,6 +338,6 @@ class EnvParser
310
338
  end
311
339
  end
312
340
 
313
- ## Load all files listed in "/lib/env_parser/types".
341
+ ## Load predefined types.
314
342
  ##
315
- Dir.glob(File.join(__dir__, %w[env_parser types *.rb])).each { |filename| require_relative filename }
343
+ require 'env_parser/types'