nero 0.5.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/.yardopts +6 -0
- data/CHANGELOG.md +49 -1
- data/README.md +72 -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 +9 -0
- 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 -402
- data/rakelib/gem.rake +4 -10
- data/rakelib/yard.rake +12 -0
- metadata +23 -18
- data/lib/nero/util.rb +0 -29
data/lib/nero/rails.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rails/credentials_tag"
|
|
4
|
+
require_relative "rails/duration_tag"
|
|
5
|
+
require_relative "rails/string_inquirer_tag"
|
|
6
|
+
|
|
7
|
+
module Nero
|
|
8
|
+
module Rails
|
|
9
|
+
module ParserExtension
|
|
10
|
+
def initialize(environ: ENV, root: nil, &block)
|
|
11
|
+
super(environ:, root:)
|
|
12
|
+
add_tag("credentials", Nero::Rails::CredentialsTag.new(::Rails.application.credentials))
|
|
13
|
+
add_tag("path/rails_root", RootPathTag.new(containing: "config.ru"))
|
|
14
|
+
add_tag("secret_key_base", ->(_, **) { ::Rails.application.secret_key_base })
|
|
15
|
+
add_tag("str/inquirer", Nero::Rails::StringInquirerTag.new)
|
|
16
|
+
add_tag("duration", Nero::Rails::DurationTag.new)
|
|
17
|
+
block&.call(self)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Nero::Parser.prepend(Nero::Rails::ParserExtension)
|
|
24
|
+
|
|
25
|
+
module Nero
|
|
26
|
+
module Rails
|
|
27
|
+
module DefaultEnv
|
|
28
|
+
def config_for(file, env: ::Rails.env, **opts, &block)
|
|
29
|
+
super(file, env:, **opts, &block)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Nero.singleton_class.prepend(Nero::Rails::DefaultEnv)
|
data/lib/nero/railtie.rb
ADDED
data/lib/nero/ref.rb
ADDED
data/lib/nero/ref_tag.rb
ADDED
data/lib/nero/result.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nero
|
|
4
|
+
class Result
|
|
5
|
+
attr_reader :value, :errors
|
|
6
|
+
|
|
7
|
+
def initialize(value, errors = [])
|
|
8
|
+
@value = value
|
|
9
|
+
@errors = errors
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def ok? = errors.empty?
|
|
13
|
+
|
|
14
|
+
def value!
|
|
15
|
+
raise ParseError, errors unless ok?
|
|
16
|
+
value
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nero
|
|
4
|
+
class RootPathTag < BaseTag
|
|
5
|
+
def initialize(containing:)
|
|
6
|
+
@containing = containing
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def resolve(args, context:)
|
|
10
|
+
relative_path = args[0]&.then { |p| p.empty? ? nil : p }
|
|
11
|
+
dir = context.dir
|
|
12
|
+
|
|
13
|
+
loop do
|
|
14
|
+
if File.exist?(File.join(dir, @containing))
|
|
15
|
+
return relative_path ? Pathname.new(File.join(dir, relative_path)) : Pathname.new(dir)
|
|
16
|
+
end
|
|
17
|
+
parent = File.dirname(dir)
|
|
18
|
+
if parent == dir
|
|
19
|
+
context.add_error("could not find #{@containing} in any ancestor of #{context.dir}")
|
|
20
|
+
return nil
|
|
21
|
+
end
|
|
22
|
+
dir = parent
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/nero/version.rb
CHANGED
data/lib/nero/visitor.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nero
|
|
4
|
+
class Visitor < ::Psych::Visitors::ToRuby
|
|
5
|
+
def self.build(tags, ctx)
|
|
6
|
+
visitor = create
|
|
7
|
+
visitor.instance_variable_set(:@nero_tags, tags)
|
|
8
|
+
visitor.instance_variable_set(:@nero_ctx, ctx)
|
|
9
|
+
visitor
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def visit_Psych_Nodes_Scalar(o)
|
|
13
|
+
handler = find_nero_tag(o.tag)
|
|
14
|
+
return super unless handler
|
|
15
|
+
|
|
16
|
+
o.tag = nil
|
|
17
|
+
handler.resolve([super], context: @nero_ctx)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def visit_Psych_Nodes_Sequence(o)
|
|
21
|
+
handler = find_nero_tag(o.tag)
|
|
22
|
+
return super unless handler
|
|
23
|
+
|
|
24
|
+
o.tag = nil
|
|
25
|
+
args = super
|
|
26
|
+
contains_ref?(args) ? Deferred.new(handler, args) : handler.resolve(args, context: @nero_ctx)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def visit_Psych_Nodes_Mapping(o)
|
|
30
|
+
handler = find_nero_tag(o.tag)
|
|
31
|
+
return super unless handler
|
|
32
|
+
|
|
33
|
+
o.tag = nil
|
|
34
|
+
args = super
|
|
35
|
+
contains_ref?(args) ? Deferred.new(handler, args) : handler.resolve(args, context: @nero_ctx)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def find_nero_tag(tag)
|
|
41
|
+
return nil unless tag&.start_with?("!")
|
|
42
|
+
@nero_tags[tag[1..]]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def contains_ref?(value)
|
|
46
|
+
case value
|
|
47
|
+
when Ref, Deferred then true
|
|
48
|
+
when Hash then value.values.any? { |v| contains_ref?(v) }
|
|
49
|
+
when Array then value.any? { |v| contains_ref?(v) }
|
|
50
|
+
else false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/nero.rb
CHANGED
|
@@ -1,416 +1,42 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "zeitwerk"
|
|
4
|
-
loader = Zeitwerk::Loader.for_gem
|
|
5
|
-
loader.setup
|
|
6
|
-
|
|
7
|
-
require "uri"
|
|
8
|
-
require "yaml"
|
|
9
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"
|
|
10
21
|
|
|
11
|
-
# TODO fail on unknown tag
|
|
12
|
-
# TODO show missing env's at once
|
|
13
|
-
# TODO raise when missing arg(s) for tag
|
|
14
22
|
module Nero
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
module Resolvable
|
|
18
|
-
def try_resolve(ctx, object)
|
|
19
|
-
if object.respond_to?(:resolve)
|
|
20
|
-
object.resolve(**ctx)
|
|
21
|
-
else
|
|
22
|
-
object
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def gen_resolve_tryer(ctx)
|
|
27
|
-
method(:try_resolve).curry.call(ctx)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def deep_resolve(object, **ctx)
|
|
31
|
-
Util.deep_transform_values(object, &gen_resolve_tryer(ctx))
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def resolve_nested!(coder, ctx = {})
|
|
35
|
-
case coder.type
|
|
36
|
-
when :seq
|
|
37
|
-
coder.seq.map!(&gen_resolve_tryer(ctx))
|
|
38
|
-
when :map
|
|
39
|
-
coder.map = deep_resolve(coder.map, **ctx)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
extend Resolvable
|
|
44
|
-
private_class_method \
|
|
45
|
-
:deep_resolve,
|
|
46
|
-
:gen_resolve_tryer,
|
|
47
|
-
:try_resolve
|
|
48
|
-
|
|
49
|
-
class Configuration
|
|
50
|
-
attr_reader :tags, :config_dir
|
|
51
|
-
|
|
52
|
-
def config_dir=(dir)
|
|
53
|
-
@config_dir = Pathname(dir).expand_path
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def add_tag(name, klass: BaseTag, &block)
|
|
57
|
-
klass, klass_options = klass
|
|
58
|
-
|
|
59
|
-
(@tags ||= {})[name] = {klass:}.tap do |h|
|
|
60
|
-
h[:block] = block if block
|
|
61
|
-
h[:options] = klass_options if klass_options
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def self.configuration
|
|
67
|
-
@configuration ||= Configuration.new
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def self.configure
|
|
71
|
-
yield configuration if block_given?
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
class BaseTag
|
|
75
|
-
include Resolvable
|
|
76
|
-
|
|
77
|
-
attr_reader :coder, :options, :ctx
|
|
78
|
-
|
|
79
|
-
def self.[](**options)
|
|
80
|
-
[self, options]
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# used by YAML
|
|
84
|
-
def init_with(coder)
|
|
85
|
-
@coder = coder
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def init(ctx:, options:)
|
|
89
|
-
init_ctx(ctx)
|
|
90
|
-
init_options(**options)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def init_ctx(ctx)
|
|
94
|
-
@ctx = ctx
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def init_options(**options)
|
|
98
|
-
@options = options
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def tag_name
|
|
102
|
-
coder.tag[1..]
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def args
|
|
106
|
-
@args ||= begin
|
|
107
|
-
resolve_nested!(coder, {})
|
|
108
|
-
case coder.type
|
|
109
|
-
when :map then Util.deep_symbolize_keys(coder.map)
|
|
110
|
-
else
|
|
111
|
-
Array(coder.public_send(coder.type))
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def config
|
|
117
|
-
ctx.dig(:tags, tag_name)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def resolve(**)
|
|
121
|
-
if (block = config[:block])
|
|
122
|
-
if block.parameters.map(&:last).include?(:coder)
|
|
123
|
-
# legacy
|
|
124
|
-
block.call(coder, ctx)
|
|
125
|
-
else
|
|
126
|
-
block.call(self)
|
|
127
|
-
end
|
|
128
|
-
else
|
|
129
|
-
args
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Requires an env-var to be available and coerces the value.
|
|
135
|
-
# When tag-name ends with "?", the env-var is optional.
|
|
136
|
-
#
|
|
137
|
-
# Given config:
|
|
138
|
-
# config.add_tag("env/upcase", klass: Nero::EnvTag[coerce: :upcase])
|
|
139
|
-
# config.add_tag("env/upcase?", klass: Nero::EnvTag[coerce: :upcase])
|
|
140
|
-
|
|
141
|
-
# Then YAML => result:
|
|
142
|
-
# "--- env/upcase [MSG, Hello World]" => "HELLO WORLD"
|
|
143
|
-
# "--- env/upcase MSG" => raises when not ENV.has_key? "MSG"
|
|
144
|
-
# "--- env/upcase? MSG" => nil
|
|
145
|
-
#
|
|
146
|
-
# Args supported:
|
|
147
|
-
# - scalar
|
|
148
|
-
# name of env-var, e.g. `!env HOME`
|
|
149
|
-
# - seq
|
|
150
|
-
# name of env-var and fallback, e.g. `!env [HOME, /root]`
|
|
151
|
-
|
|
152
|
-
# Options:
|
|
153
|
-
# - coerce - symbol or proc to be applied to value of env-var.
|
|
154
|
-
# when using coerce, the block is ignoerd.
|
|
155
|
-
#
|
|
156
|
-
class EnvTag < BaseTag
|
|
157
|
-
def resolve(**)
|
|
158
|
-
if coercer
|
|
159
|
-
coercer.call(env_value) unless env_value.nil?
|
|
160
|
-
elsif ctx.dig(:tags, tag_name, :block)
|
|
161
|
-
super
|
|
162
|
-
else
|
|
163
|
-
env_value
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def coercer
|
|
168
|
-
return unless @coerce
|
|
169
|
-
|
|
170
|
-
@coercer ||= case @coerce
|
|
171
|
-
when Symbol then @coerce.to_proc
|
|
172
|
-
else
|
|
173
|
-
@coerce
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def init_options(coerce: nil)
|
|
178
|
-
@coerce = coerce
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def optional
|
|
182
|
-
tag_name.end_with?("?") || !!ENV["NERO_ENV_ALL_OPTIONAL"]
|
|
183
|
-
end
|
|
184
|
-
alias_method :optional?, :optional
|
|
185
|
-
|
|
186
|
-
def env_value
|
|
187
|
-
self.class.env_value(*args, optional:)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def self.env_value(k, fallback = nil, optional: false)
|
|
191
|
-
if fallback.nil? && !optional
|
|
192
|
-
ENV.fetch(k)
|
|
193
|
-
else
|
|
194
|
-
ENV.fetch(k, fallback)
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def self.coerce_bool(v)
|
|
199
|
-
return false unless v
|
|
200
|
-
|
|
201
|
-
re_true = /y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON/
|
|
202
|
-
re_false = /n|N|no|No|NO|false|False|FALSE|off|Off|OFF/
|
|
203
|
-
|
|
204
|
-
case v
|
|
205
|
-
when TrueClass, FalseClass then v
|
|
206
|
-
when re_true then true
|
|
207
|
-
when re_false then false
|
|
208
|
-
else
|
|
209
|
-
raise "bool value should be one of y(es)/n(o), on/off, true/false (got #{v.inspect})"
|
|
210
|
-
end
|
|
211
|
-
end
|
|
23
|
+
def self.parse(yaml, **opts, &block)
|
|
24
|
+
Parser.new(**opts, &block).parse(yaml).value!
|
|
212
25
|
end
|
|
213
26
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
# Any argument is appended to the root-path, constructing a path-instance that may exist.
|
|
218
|
-
class PathRootTag < BaseTag
|
|
219
|
-
# Config:
|
|
220
|
-
# config.add_tag("path/git_root", klass: PathRootTag[containing: ".git"])
|
|
221
|
-
# config.add_tag("path/rails_root", klass: PathRootTag[containing: "Gemfile"])
|
|
222
|
-
#
|
|
223
|
-
# YAML:
|
|
224
|
-
# project_root: !path/git_root
|
|
225
|
-
# config_path: !path/git_root [ config ]
|
|
226
|
-
def init_options(containing:)
|
|
227
|
-
super
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def resolve(**)
|
|
231
|
-
# TODO validate upfront
|
|
232
|
-
raise <<~ERR unless root_path
|
|
233
|
-
#{tag_name}: failed to find root-path (ie an ancestor of #{ctx[:yaml_file]} containing #{options[:containing].inspect}).
|
|
234
|
-
ERR
|
|
235
|
-
root_path.join(*args).then(&config.fetch(:block, :itself.to_proc))
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
def root_path
|
|
239
|
-
find_up(ctx[:yaml_file], options[:containing])
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def find_up(path, containing)
|
|
243
|
-
(path = path.parent) until path.root? || (path / containing).exist?
|
|
244
|
-
path unless path.root?
|
|
245
|
-
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!
|
|
246
30
|
end
|
|
247
31
|
|
|
248
|
-
def self.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
# validate: non-empty coder.seq, only strs, path must exists in ctx[:config]
|
|
252
|
-
|
|
253
|
-
path = tag.args.map(&:to_sym)
|
|
254
|
-
deep_resolve(tag.ctx[:yaml].dig(*path), **{})
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
config.add_tag("env", klass: EnvTag)
|
|
258
|
-
config.add_tag("env?", klass: EnvTag)
|
|
259
|
-
config.add_tag("env/float", klass: EnvTag[coerce: :to_f])
|
|
260
|
-
config.add_tag("env/float?", klass: EnvTag[coerce: :to_f])
|
|
261
|
-
|
|
262
|
-
config.add_tag("env/integer", klass: EnvTag[coerce: :to_i])
|
|
263
|
-
config.add_tag("env/integer?", klass: EnvTag[coerce: :to_i])
|
|
264
|
-
|
|
265
|
-
config.add_tag("env/bool", klass: EnvTag) do |tag|
|
|
266
|
-
EnvTag.coerce_bool(tag.env_value)
|
|
267
|
-
end
|
|
268
|
-
config.add_tag("env/bool?", klass: EnvTag) do |tag|
|
|
269
|
-
EnvTag.coerce_bool(tag.env_value)
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
config.add_tag("path") do |tag|
|
|
273
|
-
Pathname.new(tag.args.join("/"))
|
|
274
|
-
end
|
|
275
|
-
config.add_tag("path/git_root", klass: PathRootTag[containing: ".git"])
|
|
276
|
-
config.add_tag("path/rails_root", klass: PathRootTag[containing: "config.ru"])
|
|
277
|
-
|
|
278
|
-
config.add_tag("uri") do |tag|
|
|
279
|
-
URI.join(*tag.args.join)
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
config.add_tag("str/format") do |tag|
|
|
283
|
-
case tag.args
|
|
284
|
-
when Hash
|
|
285
|
-
fmt = tag.args.delete(:fmt)
|
|
286
|
-
sprintf(fmt, tag.args)
|
|
287
|
-
else
|
|
288
|
-
sprintf(*tag.args)
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
private_class_method :add_default_tags!
|
|
294
|
-
|
|
295
|
-
def self.reset_configuration!
|
|
296
|
-
@configuration = nil
|
|
297
|
-
|
|
298
|
-
configure do |config|
|
|
299
|
-
config.config_dir = Pathname.pwd
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
add_default_tags!
|
|
303
|
-
end
|
|
304
|
-
reset_configuration!
|
|
305
|
-
|
|
306
|
-
def self.yaml_options
|
|
307
|
-
{
|
|
308
|
-
permitted_classes: [Symbol] + configuration.tags.values.map { _1[:klass] },
|
|
309
|
-
aliases: true
|
|
310
|
-
}
|
|
311
|
-
end
|
|
312
|
-
private_class_method :yaml_options
|
|
313
|
-
|
|
314
|
-
def self.load_config(file, root: nil, env: nil, resolve: true)
|
|
315
|
-
root ||= env
|
|
316
|
-
add_tags!
|
|
317
|
-
|
|
318
|
-
config_file = resolve_file(file)
|
|
319
|
-
|
|
320
|
-
if config_file.exist?
|
|
321
|
-
process_yaml(yaml_load_file(config_file, yaml_options), root:, config_file:, resolve:)
|
|
322
|
-
else
|
|
323
|
-
raise "Can't find file #{config_file}"
|
|
324
|
-
end
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
def self.resolve_file(file)
|
|
328
|
-
case file
|
|
32
|
+
def self.config_for(file, env: nil, root: nil, &block)
|
|
33
|
+
root ||= env&.to_s
|
|
34
|
+
path = case file
|
|
329
35
|
when Pathname then file
|
|
330
|
-
|
|
331
|
-
else
|
|
332
|
-
configuration.config_dir / "#{file}.yml"
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
private_class_method :resolve_file
|
|
336
|
-
|
|
337
|
-
def self.load(raw, root: nil, env: nil, resolve: true)
|
|
338
|
-
root ||= env
|
|
339
|
-
add_tags!
|
|
340
|
-
|
|
341
|
-
process_yaml(yaml_load(raw, yaml_options), root:, resolve:)
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
def self.process_yaml(yaml, root: nil, resolve: true, config_file: nil)
|
|
345
|
-
config_file ||= (Pathname.pwd / __FILE__)
|
|
346
|
-
|
|
347
|
-
unresolved = Util.deep_symbolize_keys(yaml).then do
|
|
348
|
-
root ? _1[root.to_sym] : _1
|
|
349
|
-
end
|
|
350
|
-
ctx = {tags: configuration.tags, yaml: unresolved, yaml_file: config_file}
|
|
351
|
-
init_tags!(collect_tags(unresolved), ctx:)
|
|
352
|
-
|
|
353
|
-
return unresolved unless resolve
|
|
354
|
-
|
|
355
|
-
# NOTE originally ctx was passed at this point. Maybe delete this.
|
|
356
|
-
deep_resolve(unresolved, **{})
|
|
357
|
-
end
|
|
358
|
-
private_class_method :process_yaml
|
|
359
|
-
|
|
360
|
-
def self.init_tags!(tags, ctx:)
|
|
361
|
-
tags.each do |tag|
|
|
362
|
-
options = ctx.dig(:tags, tag.tag_name, :options) || {}
|
|
363
|
-
tag.init(ctx:, options:)
|
|
364
|
-
end
|
|
365
|
-
end
|
|
366
|
-
private_class_method :init_tags!
|
|
367
|
-
|
|
368
|
-
def self.yaml_load_file(file, opts = {})
|
|
369
|
-
if Psych::VERSION < "4"
|
|
370
|
-
YAML.load_file(file)
|
|
371
|
-
else
|
|
372
|
-
YAML.load_file(file, **opts)
|
|
373
|
-
end
|
|
374
|
-
end
|
|
375
|
-
private_class_method :yaml_load_file
|
|
376
|
-
|
|
377
|
-
def self.yaml_load(file, opts = {})
|
|
378
|
-
if Psych::VERSION < "4"
|
|
379
|
-
YAML.load(file)
|
|
380
|
-
else
|
|
381
|
-
YAML.load(file, **opts)
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
private_class_method :yaml_load
|
|
385
|
-
|
|
386
|
-
def self.add_tags!
|
|
387
|
-
configuration.tags.each do |tag_name, tag|
|
|
388
|
-
YAML.add_tag("!#{tag_name}", tag[:klass])
|
|
389
|
-
end
|
|
390
|
-
end
|
|
391
|
-
private_class_method :add_tags!
|
|
392
|
-
|
|
393
|
-
def self.collect_tags(obj)
|
|
394
|
-
case obj
|
|
395
|
-
when Hash
|
|
396
|
-
obj.each_value.flat_map { collect_tags(_1) }.compact
|
|
397
|
-
when Nero::BaseTag
|
|
398
|
-
[obj] +
|
|
399
|
-
case obj.coder.type
|
|
400
|
-
when :seq
|
|
401
|
-
collect_tags(obj.coder.seq)
|
|
402
|
-
when :map
|
|
403
|
-
collect_tags(obj.coder.map)
|
|
404
|
-
else
|
|
405
|
-
[]
|
|
406
|
-
end
|
|
407
|
-
when Array
|
|
408
|
-
obj.flat_map { collect_tags(_1) }.compact
|
|
409
|
-
else
|
|
410
|
-
[]
|
|
36
|
+
else Pathname.new("config") / "#{file}.yml"
|
|
411
37
|
end
|
|
38
|
+
parse_file(path.expand_path, root: root, &block)
|
|
412
39
|
end
|
|
413
|
-
private_class_method :collect_tags
|
|
414
40
|
end
|
|
415
41
|
|
|
416
|
-
|
|
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
|
data/rakelib/yard.rake
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "yard"
|
|
2
|
+
|
|
3
|
+
YARD::Rake::YardocTask.new(:docs) do |t|
|
|
4
|
+
# Options defined in `.yardopts` are read first, then merged with
|
|
5
|
+
# options defined here.
|
|
6
|
+
#
|
|
7
|
+
# It's recommended to define options in `.yardopts` instead of here,
|
|
8
|
+
# as `.yardopts` can be read by external YARD tools, like the
|
|
9
|
+
# hot-reload YARD server `yard server --reload`.
|
|
10
|
+
|
|
11
|
+
# t.options += ['--title', "Something custom"]
|
|
12
|
+
end
|