nero 0.6.0 → 0.7.0.rc2
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 +4 -4
- data/.envrc +3 -1
- data/CHANGELOG.md +26 -1
- data/README.md +51 -49
- data/lib/nero/base_tag.rb +9 -0
- data/lib/nero/context.rb +17 -0
- data/lib/nero/deferred.rb +9 -0
- data/lib/nero/env_tag.rb +32 -0
- data/lib/nero/error.rb +22 -0
- data/lib/nero/format_tag.rb +16 -0
- data/lib/nero/parser.rb +139 -0
- data/lib/nero/proc_tag.rb +9 -0
- data/lib/nero/rails/credentials_tag.rb +24 -0
- data/lib/nero/rails/duration_tag.rb +26 -0
- data/lib/nero/rails/string_inquirer_tag.rb +16 -0
- data/lib/nero/rails.rb +35 -0
- data/lib/nero/railtie.rb +4 -5
- data/lib/nero/ref.rb +9 -0
- data/lib/nero/ref_tag.rb +9 -0
- data/lib/nero/result.rb +19 -0
- data/lib/nero/root_path_tag.rb +26 -0
- data/lib/nero/version.rb +1 -1
- data/lib/nero/visitor.rb +54 -0
- data/lib/nero.rb +28 -574
- data/rakelib/gem.rake +4 -10
- metadata +20 -18
- data/lib/nero/util.rb +0 -29
data/lib/nero.rb
CHANGED
|
@@ -1,588 +1,42 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "zeitwerk"
|
|
4
|
-
loader = Zeitwerk::Loader.for_gem
|
|
5
|
-
loader.do_not_eager_load("#{__dir__}/nero/railtie.rb")
|
|
6
|
-
loader.setup
|
|
7
|
-
|
|
8
|
-
require "uri"
|
|
9
|
-
require "yaml"
|
|
10
3
|
require "pathname"
|
|
4
|
+
require "psych"
|
|
5
|
+
require "set"
|
|
6
|
+
|
|
7
|
+
require_relative "nero/version"
|
|
8
|
+
require_relative "nero/error"
|
|
9
|
+
require_relative "nero/result"
|
|
10
|
+
require_relative "nero/context"
|
|
11
|
+
require_relative "nero/ref"
|
|
12
|
+
require_relative "nero/deferred"
|
|
13
|
+
require_relative "nero/base_tag"
|
|
14
|
+
require_relative "nero/proc_tag"
|
|
15
|
+
require_relative "nero/ref_tag"
|
|
16
|
+
require_relative "nero/env_tag"
|
|
17
|
+
require_relative "nero/root_path_tag"
|
|
18
|
+
require_relative "nero/format_tag"
|
|
19
|
+
require_relative "nero/visitor"
|
|
20
|
+
require_relative "nero/parser"
|
|
11
21
|
|
|
12
|
-
# TODO fail on unknown tag
|
|
13
|
-
# TODO show missing env's at once
|
|
14
|
-
# TODO raise when missing arg(s) for tag
|
|
15
22
|
module Nero
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
module DigExt
|
|
19
|
-
# ⛏️💥 Like `dig`, but raises `ArgumentError` when `path` does not exist.
|
|
20
|
-
# @example like dig
|
|
21
|
-
# {a: {b: 2}}.dig!(:a, :b) #=> 2
|
|
22
|
-
# {a: {b: 2}}.dig!(:a, :c) #=> ArgumentError, path not found [:a, :c] (ArgumentError)
|
|
23
|
-
# @raise [ArgumentError] when `path` does not exist.
|
|
24
|
-
# @overload dig!(*path)
|
|
25
|
-
# @param path nested keys into config
|
|
26
|
-
def dig!(k0, *k)
|
|
27
|
-
k.unshift(k0)
|
|
28
|
-
|
|
29
|
-
unless paths.include?(k)
|
|
30
|
-
raise ArgumentError, "path not found #{k}"
|
|
31
|
-
end
|
|
32
|
-
dig(*k)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def paths
|
|
38
|
-
@paths ||= gather_paths(self).to_set
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def gather_paths(item, acc: [], path: [])
|
|
42
|
-
acc += [path]
|
|
43
|
-
|
|
44
|
-
case item
|
|
45
|
-
when NilClass
|
|
46
|
-
[]
|
|
47
|
-
when Hash
|
|
48
|
-
item.flat_map { |(k, v)| gather_paths(v, acc: acc, path: path + [k]) }
|
|
49
|
-
when Array
|
|
50
|
-
item.each_with_index.flat_map do |item, ix|
|
|
51
|
-
gather_paths(item, acc: acc, path: path + [ix])
|
|
52
|
-
end
|
|
53
|
-
else
|
|
54
|
-
acc
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
class Config < Hash
|
|
60
|
-
include DigExt
|
|
61
|
-
|
|
62
|
-
def self.for(v)
|
|
63
|
-
case v
|
|
64
|
-
when self then v
|
|
65
|
-
when Hash then self.[](v)
|
|
66
|
-
else
|
|
67
|
-
v
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
module Resolvable
|
|
73
|
-
def try_resolve(object)
|
|
74
|
-
if object.respond_to?(:resolve)
|
|
75
|
-
object.resolve
|
|
76
|
-
else
|
|
77
|
-
object
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def deep_resolve(object)
|
|
82
|
-
Util.deep_transform_values(object, &method(:try_resolve))
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def resolve_nested!(coder)
|
|
86
|
-
case coder.type
|
|
87
|
-
when :seq
|
|
88
|
-
coder.seq.map!(&method(:try_resolve))
|
|
89
|
-
when :map
|
|
90
|
-
coder.map = deep_resolve(coder.map)
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
extend Resolvable
|
|
95
|
-
private_class_method \
|
|
96
|
-
:deep_resolve,
|
|
97
|
-
:try_resolve
|
|
98
|
-
|
|
99
|
-
class Configuration
|
|
100
|
-
attr_reader :config_dir
|
|
101
|
-
|
|
102
|
-
def tags
|
|
103
|
-
@tags ||= {}
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def config_dir=(dir)
|
|
107
|
-
@config_dir = Pathname(dir).expand_path
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def add_tag(name, klass: BaseTag, &block)
|
|
111
|
-
klass, klass_options = klass
|
|
112
|
-
|
|
113
|
-
tags[name] = {klass:}.tap do |h|
|
|
114
|
-
h[:block] = block if block
|
|
115
|
-
h[:options] = klass_options if klass_options
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def self.configuration
|
|
121
|
-
@configuration ||= Configuration.new
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def self.add_tags!
|
|
125
|
-
configuration.tags.each do |tag_name, tag|
|
|
126
|
-
YAML.add_tag("!#{tag_name}", tag[:klass])
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
private_class_method :add_tags!
|
|
130
|
-
|
|
131
|
-
def self.configure
|
|
132
|
-
yield configuration if block_given?
|
|
133
|
-
ensure
|
|
134
|
-
add_tags!
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Superclass for all tags.
|
|
138
|
-
#
|
|
139
|
-
# Writing your own tag-class would look something like this:
|
|
140
|
-
#
|
|
141
|
-
# Wanted usage in YAML:
|
|
142
|
-
# ```ruby
|
|
143
|
-
# Nero.load(<<~YAML)
|
|
144
|
-
# secret: !rot/12 "some message"
|
|
145
|
-
# other_secret: !rot/13 [ !env [SECRET, some message] ]
|
|
146
|
-
# YAML
|
|
147
|
-
# ```
|
|
148
|
-
#
|
|
149
|
-
# Required config:
|
|
150
|
-
# ```ruby
|
|
151
|
-
# config.add_tag("rot/12", klass: RotTag[n: 12])
|
|
152
|
-
# config.add_tag("rot/13", klass: RotTag[n: 13]) do |secret|
|
|
153
|
-
# "#{secret} (try breaking this!)"
|
|
154
|
-
# end
|
|
155
|
-
# ```
|
|
156
|
-
# The class then would look like this:
|
|
157
|
-
# ```ruby
|
|
158
|
-
# class RotTag < Nero::BaseTag
|
|
159
|
-
# attr_reader :n
|
|
160
|
-
#
|
|
161
|
-
# # Overriding this method...:
|
|
162
|
-
# # - restricts options
|
|
163
|
-
# # ie `RotTag[x: 1]` would raise.
|
|
164
|
-
# # - sets default values
|
|
165
|
-
# # - makes options available via getters
|
|
166
|
-
# # (otherwise available via `options[:n]`).
|
|
167
|
-
# def init_options(n: 10)
|
|
168
|
-
# super
|
|
169
|
-
# @n = n
|
|
170
|
-
# end
|
|
171
|
-
#
|
|
172
|
-
# # This is where the magic happens.
|
|
173
|
-
# # (Accepting any keyword arguments keeps the method fw-compatible).
|
|
174
|
-
# def resolve(**)
|
|
175
|
-
# # `args` are the resolved arguments (Array or Hash).
|
|
176
|
-
# # `config` the config of the tag (containing e.g. the proc).
|
|
177
|
-
# block = config.fetch(:block, :itself.to_proc)
|
|
178
|
-
# args.join.tr(chars.join, chars.rotate(n).join).then(&block)
|
|
179
|
-
# end
|
|
180
|
-
#
|
|
181
|
-
# # Just some helper method with all characters that can be rotated.
|
|
182
|
-
# def chars
|
|
183
|
-
# %w(a b c) # etc
|
|
184
|
-
# end
|
|
185
|
-
# end
|
|
186
|
-
# ```
|
|
187
|
-
#
|
|
188
|
-
class BaseTag
|
|
189
|
-
include Resolvable
|
|
190
|
-
|
|
191
|
-
attr_reader :coder, :options, :ctx
|
|
192
|
-
|
|
193
|
-
# Convenience method simplifying {Nero::Configuration#add_tag}:
|
|
194
|
-
#
|
|
195
|
-
# ```ruby
|
|
196
|
-
# config.add_tag("foo", klass: SomeTag[some_option: 1])
|
|
197
|
-
# ```
|
|
198
|
-
def self.[](**options)
|
|
199
|
-
[self, options]
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
# @private used by YAML
|
|
203
|
-
def init_with(coder)
|
|
204
|
-
@coder = coder
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
def init(ctx:, options:)
|
|
208
|
-
init_ctx(ctx)
|
|
209
|
-
init_options(**options)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
def init_ctx(ctx)
|
|
213
|
-
@ctx = ctx
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def init_options(**options)
|
|
217
|
-
@options = options
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def tag_name
|
|
221
|
-
coder.tag[1..]
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def args
|
|
225
|
-
@args ||= begin
|
|
226
|
-
resolve_nested!(coder)
|
|
227
|
-
case coder.type
|
|
228
|
-
when :map then Util.deep_symbolize_keys(coder.map)
|
|
229
|
-
else
|
|
230
|
-
Array(coder.public_send(coder.type))
|
|
231
|
-
end
|
|
232
|
-
end
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def config
|
|
236
|
-
ctx.dig(:tags, tag_name)
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def resolve(**)
|
|
240
|
-
if (block = config[:block])
|
|
241
|
-
if block.parameters.map(&:last).include?(:coder)
|
|
242
|
-
# legacy
|
|
243
|
-
block.call(coder, ctx)
|
|
244
|
-
else
|
|
245
|
-
block.call(self)
|
|
246
|
-
end
|
|
247
|
-
else
|
|
248
|
-
args
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
# Requires an env-var to be available and coerces the value.
|
|
254
|
-
# When tag-name ends with "?", the env-var is optional.
|
|
255
|
-
#
|
|
256
|
-
# Given config:
|
|
257
|
-
# ```ruby
|
|
258
|
-
# config.add_tag("env/upcase", klass: Nero::EnvTag[coerce: :upcase])
|
|
259
|
-
# config.add_tag("env/upcase?", klass: Nero::EnvTag[coerce: :upcase])
|
|
260
|
-
# ```
|
|
261
|
-
#
|
|
262
|
-
# Then YAML => result:
|
|
263
|
-
# ```ruby
|
|
264
|
-
# "--- env/upcase [MSG, Hello World]" #=> "HELLO WORLD"
|
|
265
|
-
# "--- env/upcase MSG" #=> raises when not ENV.has_key? "MSG"
|
|
266
|
-
# "--- env/upcase? MSG" #=> nil
|
|
267
|
-
# ```
|
|
268
|
-
#
|
|
269
|
-
# YAML-args supported:
|
|
270
|
-
# - scalar —
|
|
271
|
-
# name of env-var, e.g. `!env HOME`
|
|
272
|
-
# - seq —
|
|
273
|
-
# name of env-var and fallback, e.g. `!env [HOME, /root]`
|
|
274
|
-
#
|
|
275
|
-
# Options:
|
|
276
|
-
# - `coerce` —
|
|
277
|
-
# symbol or proc to be applied to value of env-var.
|
|
278
|
-
# when using coerce, the block is ignored.
|
|
279
|
-
#
|
|
280
|
-
class EnvTag < BaseTag
|
|
281
|
-
def resolve(**)
|
|
282
|
-
if coercer
|
|
283
|
-
coercer.call(env_value) unless env_value.nil?
|
|
284
|
-
elsif ctx.dig(:tags, tag_name, :block)
|
|
285
|
-
super
|
|
286
|
-
else
|
|
287
|
-
env_value
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
def coercer
|
|
292
|
-
return unless @coerce
|
|
293
|
-
|
|
294
|
-
@coercer ||= case @coerce
|
|
295
|
-
when Symbol then @coerce.to_proc
|
|
296
|
-
else
|
|
297
|
-
@coerce
|
|
298
|
-
end
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
def init_options(coerce: nil)
|
|
302
|
-
@coerce = coerce
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
def optional
|
|
306
|
-
tag_name.end_with?("?") || !!ENV["NERO_ENV_ALL_OPTIONAL"]
|
|
307
|
-
end
|
|
308
|
-
alias_method :optional?, :optional
|
|
309
|
-
|
|
310
|
-
def env_value
|
|
311
|
-
self.class.env_value(*args, optional:)
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
def self.env_value(k, fallback = nil, optional: false)
|
|
315
|
-
if fallback.nil? && !optional
|
|
316
|
-
ENV.fetch(k)
|
|
317
|
-
else
|
|
318
|
-
ENV.fetch(k, fallback)
|
|
319
|
-
end
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
def self.coerce_bool(v)
|
|
323
|
-
return false unless v
|
|
324
|
-
|
|
325
|
-
re_true = /y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON/
|
|
326
|
-
re_false = /n|N|no|No|NO|false|False|FALSE|off|Off|OFF/
|
|
327
|
-
|
|
328
|
-
case v
|
|
329
|
-
when TrueClass, FalseClass then v
|
|
330
|
-
when re_true then true
|
|
331
|
-
when re_false then false
|
|
332
|
-
else
|
|
333
|
-
raise "bool value should be one of y(es)/n(o), on/off, true/false (got #{v.inspect})"
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
# Construct path relative to some root-path.
|
|
339
|
-
# Root-paths are expected to be ancestors of the yaml-file being parsed.
|
|
340
|
-
# They are found by traversing up and checking for specific files/folders, e.g. '.git' or 'Gemfile'.
|
|
341
|
-
# Any argument is appended to the root-path, constructing a path-instance that may exist.
|
|
342
|
-
class PathRootTag < BaseTag
|
|
343
|
-
# Config:
|
|
344
|
-
# config.add_tag("path/git_root", klass: PathRootTag[containing: ".git"])
|
|
345
|
-
# config.add_tag("path/rails_root", klass: PathRootTag[containing: "Gemfile"])
|
|
346
|
-
#
|
|
347
|
-
# YAML:
|
|
348
|
-
# project_root: !path/git_root
|
|
349
|
-
# config_path: !path/git_root [ config ]
|
|
350
|
-
def init_options(containing:)
|
|
351
|
-
super
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
def resolve(**)
|
|
355
|
-
# TODO validate upfront
|
|
356
|
-
raise <<~ERR unless root_path
|
|
357
|
-
#{tag_name}: failed to find root-path (ie an ancestor of #{ctx[:yaml_file]} containing #{options[:containing].inspect}).
|
|
358
|
-
ERR
|
|
359
|
-
root_path.join(*args).then(&config.fetch(:block, :itself.to_proc))
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
def root_path
|
|
363
|
-
find_up(ctx[:yaml_file], options[:containing])
|
|
364
|
-
end
|
|
365
|
-
|
|
366
|
-
def find_up(path, containing)
|
|
367
|
-
(path = path.parent) until path.root? || (path / containing).exist?
|
|
368
|
-
path unless path.root?
|
|
369
|
-
end
|
|
23
|
+
def self.parse(yaml, **opts, &block)
|
|
24
|
+
Parser.new(**opts, &block).parse(yaml).value!
|
|
370
25
|
end
|
|
371
26
|
|
|
372
|
-
def self.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
# validate: non-empty coder.seq, only strs, path must exists in ctx[:config]
|
|
376
|
-
|
|
377
|
-
path = tag.args.map(&:to_sym)
|
|
378
|
-
deep_resolve(tag.ctx[:yaml].dig(*path))
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
config.add_tag("env", klass: EnvTag)
|
|
382
|
-
config.add_tag("env?", klass: EnvTag)
|
|
383
|
-
config.add_tag("env/float", klass: EnvTag[coerce: :to_f])
|
|
384
|
-
config.add_tag("env/float?", klass: EnvTag[coerce: :to_f])
|
|
385
|
-
|
|
386
|
-
config.add_tag("env/integer", klass: EnvTag[coerce: :to_i])
|
|
387
|
-
config.add_tag("env/integer?", klass: EnvTag[coerce: :to_i])
|
|
388
|
-
|
|
389
|
-
config.add_tag("env/bool", klass: EnvTag) do |tag|
|
|
390
|
-
EnvTag.coerce_bool(tag.env_value)
|
|
391
|
-
end
|
|
392
|
-
config.add_tag("env/bool?", klass: EnvTag) do |tag|
|
|
393
|
-
EnvTag.coerce_bool(tag.env_value)
|
|
394
|
-
end
|
|
395
|
-
|
|
396
|
-
config.add_tag("path") do |tag|
|
|
397
|
-
Pathname.new(tag.args.join("/"))
|
|
398
|
-
end
|
|
399
|
-
config.add_tag("path/git_root", klass: PathRootTag[containing: ".git"])
|
|
400
|
-
config.add_tag("path/rails_root", klass: PathRootTag[containing: "config.ru"])
|
|
401
|
-
|
|
402
|
-
config.add_tag("uri") do |tag|
|
|
403
|
-
URI.join(*tag.args.join)
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
config.add_tag("str/format") do |tag|
|
|
407
|
-
case tag.args
|
|
408
|
-
when Hash
|
|
409
|
-
fmt = tag.args.delete(:fmt)
|
|
410
|
-
sprintf(fmt, tag.args)
|
|
411
|
-
else
|
|
412
|
-
sprintf(*tag.args)
|
|
413
|
-
end
|
|
414
|
-
end
|
|
415
|
-
end
|
|
27
|
+
def self.parse_file(path, env: nil, root: nil, &block)
|
|
28
|
+
root ||= env&.to_s
|
|
29
|
+
Parser.new(root: root, &block).parse_file(path).value!
|
|
416
30
|
end
|
|
417
|
-
private_class_method :add_default_tags!
|
|
418
31
|
|
|
419
|
-
def self.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
configure do |config|
|
|
423
|
-
config.config_dir = Pathname.new("config").expand_path
|
|
424
|
-
end
|
|
425
|
-
|
|
426
|
-
add_default_tags!
|
|
427
|
-
add_tags!
|
|
428
|
-
end
|
|
429
|
-
reset_configuration!
|
|
430
|
-
|
|
431
|
-
def self.default_yaml_options
|
|
432
|
-
{
|
|
433
|
-
permitted_classes: [Symbol] + configuration.tags.values.map { _1[:klass] },
|
|
434
|
-
aliases: true
|
|
435
|
-
}
|
|
436
|
-
end
|
|
437
|
-
private_class_method :default_yaml_options
|
|
438
|
-
|
|
439
|
-
def self.yaml_options(yaml_options)
|
|
440
|
-
epc = yaml_options.delete(:extra_permitted_classes)
|
|
441
|
-
default_yaml_options.merge(yaml_options).tap do
|
|
442
|
-
_1[:permitted_classes].push(*epc)
|
|
443
|
-
end
|
|
444
|
-
end
|
|
445
|
-
private_class_method :yaml_options
|
|
446
|
-
|
|
447
|
-
# Like `YAML.load` with extra options.
|
|
448
|
-
#
|
|
449
|
-
# @param [Symbol, String] root return the value of this root key.
|
|
450
|
-
# @param [Boolean] resolve (for debug purposes) not resolving would leave the Nero-tags as-is.
|
|
451
|
-
# @param [Array<ClassName>] extra_permitted_classes classes that are added
|
|
452
|
-
# to the default permitted_classes and passed to `YAML.load`.
|
|
453
|
-
# @param [Hash] yaml_options options passed to `YAML.load`.
|
|
454
|
-
# @return [Nero::Config (when the data is a Hash)]
|
|
455
|
-
# @example
|
|
456
|
-
# Nero.load(<<~YAML, extra_permitted_classes: [Time])
|
|
457
|
-
# home: !env HOME,
|
|
458
|
-
# created_at: 2010-02-11 11:02:57
|
|
459
|
-
# project_root: !path/git_root
|
|
460
|
-
# YAML
|
|
461
|
-
# #=> {
|
|
462
|
-
# # home: "/Users/gert",
|
|
463
|
-
# # created_at: 2010-02-11 12:02:57 +0100,
|
|
464
|
-
# # project_root: #<Pathname:/Users/gert/projects/nero>
|
|
465
|
-
# # }
|
|
466
|
-
def self.load(yaml, root: nil, resolve: true, **yaml_options)
|
|
467
|
-
process_yaml(yaml_load(yaml, yaml_options(yaml_options)), root:, resolve:)
|
|
468
|
-
end
|
|
469
|
-
|
|
470
|
-
# Like `YAML.load_file`. See {load} for options.
|
|
471
|
-
# @return [Nero::Config (when the YAML-data is a Hash)]
|
|
472
|
-
def self.load_file(file, root: nil, resolve: true, **yaml_options)
|
|
473
|
-
config_file = (file.is_a?(Pathname) ? file : Pathname.new(file)).expand_path
|
|
474
|
-
process_yaml(yaml_load_file(config_file, yaml_options(yaml_options)), root:, config_file:, resolve:)
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
# Convenience wrapper for {load_file} that works like `Rails.application.config_for`.
|
|
478
|
-
# @see https://api.rubyonrails.org/classes/Rails/Application.html#method-i-config_for Rails' config_for documentation
|
|
479
|
-
#
|
|
480
|
-
# The file-argument is expanded like so `(configuration.config_dir / "#{file}.yml").expand_path`.
|
|
481
|
-
#
|
|
482
|
-
# @param [Symbol, String, Pathname] file `Symbol` or `String` are expanded as shown above. A `Pathname` is used as-is.
|
|
483
|
-
# @param [Symbol, String] env return the value of this root key.
|
|
484
|
-
# @param [Symbol, String] root return the value of this root key.
|
|
485
|
-
# @param [Boolean] resolve (for debug purposes) not resolving would leave the Nero-tags as-is.
|
|
486
|
-
# @param [Array<ClassName>] extra_permitted_classes classes that are added
|
|
487
|
-
# to the default permitted_classes and passed to `YAML.load`.
|
|
488
|
-
# @param [Hash] yaml_options options passed to `YAML.load_file`.
|
|
489
|
-
# @return [Nero::Config (when the data is a Hash)]
|
|
490
|
-
# @example
|
|
491
|
-
# Nero.config_for(:app, env: Rails.env) #=> {...}
|
|
492
|
-
def self.config_for(file, root: nil, env: nil, **yaml_options)
|
|
493
|
-
root ||= env
|
|
494
|
-
|
|
495
|
-
load_file(resolve_file(file), root:, **yaml_options)
|
|
496
|
-
end
|
|
497
|
-
|
|
498
|
-
# @deprecated Use `load_file` or `config_for` instead.
|
|
499
|
-
def self.load_config(file, root: nil, env: nil, resolve: true)
|
|
500
|
-
warn "[DEPRECATION] `load_config` is deprecated. Use `load_file` or `config_for` instead."
|
|
501
|
-
root ||= env
|
|
502
|
-
add_tags!
|
|
503
|
-
|
|
504
|
-
config_file = resolve_file(file)
|
|
505
|
-
|
|
506
|
-
if config_file.exist?
|
|
507
|
-
process_yaml(yaml_load_file(config_file, yaml_options), root:, config_file:, resolve:)
|
|
508
|
-
else
|
|
509
|
-
raise "Can't find file #{config_file}"
|
|
510
|
-
end
|
|
511
|
-
end
|
|
512
|
-
|
|
513
|
-
def self.resolve_file(file)
|
|
514
|
-
case file
|
|
32
|
+
def self.config_for(file, env: nil, root: nil, &block)
|
|
33
|
+
root ||= env&.to_s
|
|
34
|
+
path = case file
|
|
515
35
|
when Pathname then file
|
|
516
|
-
else
|
|
517
|
-
(configuration.config_dir / "#{file}.yml").expand_path
|
|
36
|
+
else Pathname.new("config") / "#{file}.yml"
|
|
518
37
|
end
|
|
38
|
+
parse_file(path.expand_path, root: root, &block)
|
|
519
39
|
end
|
|
520
|
-
private_class_method :resolve_file
|
|
521
|
-
|
|
522
|
-
def self.process_yaml(yaml, root: nil, resolve: true, config_file: nil)
|
|
523
|
-
config_file ||= (Pathname.pwd / __FILE__)
|
|
524
|
-
|
|
525
|
-
unresolved = Util.deep_symbolize_keys(yaml).then do
|
|
526
|
-
root ? _1[root.to_sym] : _1
|
|
527
|
-
end
|
|
528
|
-
ctx = {tags: configuration.tags, yaml: unresolved, yaml_file: config_file}
|
|
529
|
-
init_tags!(collect_tags(unresolved), ctx:)
|
|
530
|
-
|
|
531
|
-
return unresolved unless resolve
|
|
532
|
-
|
|
533
|
-
Config.for(deep_resolve(unresolved))
|
|
534
|
-
end
|
|
535
|
-
private_class_method :process_yaml
|
|
536
|
-
|
|
537
|
-
def self.init_tags!(tags, ctx:)
|
|
538
|
-
tags.each do |tag|
|
|
539
|
-
options = ctx.dig(:tags, tag.tag_name, :options) || {}
|
|
540
|
-
tag.init(ctx:, options:)
|
|
541
|
-
end
|
|
542
|
-
end
|
|
543
|
-
private_class_method :init_tags!
|
|
544
|
-
|
|
545
|
-
def self.yaml_load_file(file, opts = {})
|
|
546
|
-
if Psych::VERSION < "4"
|
|
547
|
-
YAML.load_file(file)
|
|
548
|
-
else
|
|
549
|
-
YAML.load_file(file, **opts)
|
|
550
|
-
end
|
|
551
|
-
end
|
|
552
|
-
private_class_method :yaml_load_file
|
|
553
|
-
|
|
554
|
-
def self.yaml_load(file, opts = {})
|
|
555
|
-
if Psych::VERSION < "4"
|
|
556
|
-
YAML.load(file)
|
|
557
|
-
else
|
|
558
|
-
YAML.load(file, **opts)
|
|
559
|
-
end
|
|
560
|
-
end
|
|
561
|
-
private_class_method :yaml_load
|
|
562
|
-
|
|
563
|
-
def self.collect_tags(obj)
|
|
564
|
-
case obj
|
|
565
|
-
when Hash
|
|
566
|
-
obj.each_value.flat_map { collect_tags(_1) }.compact
|
|
567
|
-
when Nero::BaseTag
|
|
568
|
-
[obj] +
|
|
569
|
-
case obj.coder.type
|
|
570
|
-
when :seq
|
|
571
|
-
collect_tags(obj.coder.seq)
|
|
572
|
-
when :map
|
|
573
|
-
collect_tags(obj.coder.map)
|
|
574
|
-
else
|
|
575
|
-
[]
|
|
576
|
-
end
|
|
577
|
-
when Array
|
|
578
|
-
obj.flat_map { collect_tags(_1) }.compact
|
|
579
|
-
else
|
|
580
|
-
[]
|
|
581
|
-
end
|
|
582
|
-
end
|
|
583
|
-
private_class_method :collect_tags
|
|
584
40
|
end
|
|
585
41
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
loader.eager_load if ENV.key?("CI")
|
|
42
|
+
require_relative "nero/railtie" if defined?(Rails::Railtie)
|
data/rakelib/gem.rake
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
namespace :gem do
|
|
2
|
+
desc "Write new version to version.rb"
|
|
2
3
|
task "write_version", [:version] do |_task, args|
|
|
3
4
|
if args[:version]
|
|
4
5
|
version = args[:version].split("=").last
|
|
@@ -8,18 +9,11 @@ namespace :gem do
|
|
|
8
9
|
ruby -pi -e 'gsub(/VERSION = ".*"/, %{VERSION = "#{version}"})' #{version_file}
|
|
9
10
|
CMD
|
|
10
11
|
Bundler.ui.confirm "Version #{version} written to #{version_file}."
|
|
12
|
+
|
|
13
|
+
system("bundle install", exception: true)
|
|
14
|
+
Bundler.ui.confirm "Gemfile.lock updated."
|
|
11
15
|
else
|
|
12
16
|
Bundler.ui.warn "No version provided, keeping version.rb as is."
|
|
13
17
|
end
|
|
14
18
|
end
|
|
15
|
-
|
|
16
|
-
desc "Build [version]"
|
|
17
|
-
task "build", [:version] => %w[write_version] do
|
|
18
|
-
Rake::Task["build"].invoke
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
desc "Build and push [version] to rubygems"
|
|
22
|
-
task "release", [:version] => %w[build] do
|
|
23
|
-
Rake::Task["release:rubygem_push"].invoke
|
|
24
|
-
end
|
|
25
19
|
end
|
metadata
CHANGED
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nero
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0.rc2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gert Goet
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
-
- !ruby/object:Gem::Dependency
|
|
13
|
-
name: zeitwerk
|
|
14
|
-
requirement: !ruby/object:Gem::Requirement
|
|
15
|
-
requirements:
|
|
16
|
-
- - ">="
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0'
|
|
19
|
-
type: :runtime
|
|
20
|
-
prerelease: false
|
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
-
requirements:
|
|
23
|
-
- - ">="
|
|
24
|
-
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0'
|
|
26
12
|
- !ruby/object:Gem::Dependency
|
|
27
13
|
name: appraisal
|
|
28
14
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -62,9 +48,25 @@ files:
|
|
|
62
48
|
- gemfiles/psych_3.gemfile
|
|
63
49
|
- gemfiles/psych_4.gemfile
|
|
64
50
|
- lib/nero.rb
|
|
51
|
+
- lib/nero/base_tag.rb
|
|
52
|
+
- lib/nero/context.rb
|
|
53
|
+
- lib/nero/deferred.rb
|
|
54
|
+
- lib/nero/env_tag.rb
|
|
55
|
+
- lib/nero/error.rb
|
|
56
|
+
- lib/nero/format_tag.rb
|
|
57
|
+
- lib/nero/parser.rb
|
|
58
|
+
- lib/nero/proc_tag.rb
|
|
59
|
+
- lib/nero/rails.rb
|
|
60
|
+
- lib/nero/rails/credentials_tag.rb
|
|
61
|
+
- lib/nero/rails/duration_tag.rb
|
|
62
|
+
- lib/nero/rails/string_inquirer_tag.rb
|
|
65
63
|
- lib/nero/railtie.rb
|
|
66
|
-
- lib/nero/
|
|
64
|
+
- lib/nero/ref.rb
|
|
65
|
+
- lib/nero/ref_tag.rb
|
|
66
|
+
- lib/nero/result.rb
|
|
67
|
+
- lib/nero/root_path_tag.rb
|
|
67
68
|
- lib/nero/version.rb
|
|
69
|
+
- lib/nero/visitor.rb
|
|
68
70
|
- rakelib/gem.rake
|
|
69
71
|
- rakelib/yard.rake
|
|
70
72
|
- sig/nero.rbs
|
|
@@ -89,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
89
91
|
- !ruby/object:Gem::Version
|
|
90
92
|
version: '0'
|
|
91
93
|
requirements: []
|
|
92
|
-
rubygems_version: 3.6.
|
|
94
|
+
rubygems_version: 3.6.7
|
|
93
95
|
specification_version: 4
|
|
94
96
|
summary: Convenience YAML-tags
|
|
95
97
|
test_files: []
|