nero 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/README.md +18 -16
- data/lib/nero/version.rb +1 -1
- data/lib/nero.rb +128 -68
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3da331b2512fc63cf1a9258991a33dda876e8b0aed2445bca34ed207f8cfc1fb
|
4
|
+
data.tar.gz: 79c6afff69d21841b247de206f7b3f6482bf74496ad91c4986528fd0620e6551
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e80cb4098d709df1231e7ee1cfc077be097517709ffabee489cdce8bc9da30aac7e6f89157823c147e691042945c9da806f337f1ca64ab90dc376d390f46d0b9
|
7
|
+
data.tar.gz: 7637f0ae5983c3902a04092c2d1cf3ed9aacb0ae036f73d7c1067f7677a8c175ffe4c4826c972cb129c47cfe37517115e0bd51f81c1f6b4f132a8946b4311a3e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,44 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.0] - 2025-02-02
|
4
|
+
|
5
|
+
- Add configuration
|
6
|
+
For custom tags:
|
7
|
+
```ruby
|
8
|
+
Nero.configure do |nero|
|
9
|
+
nero.add_tag("duration") do |coder|
|
10
|
+
num, duration = coder.seq
|
11
|
+
mult = case duration
|
12
|
+
when /^seconds?/ then 1
|
13
|
+
when /^minutes?$/ then 60
|
14
|
+
when /^hours?$/ then 60 *60
|
15
|
+
when /^days?$/ then 24 * 60 * 60
|
16
|
+
else
|
17
|
+
raise ArgumentError, "Unknown duration #{coder.seq.inspect}"
|
18
|
+
end
|
19
|
+
num * mult
|
20
|
+
end
|
21
|
+
end
|
22
|
+
```
|
23
|
+
...and config_dir:
|
24
|
+
```ruby
|
25
|
+
Nero.configure {|nero| nero.config_dir = Rails.root / "config" }
|
26
|
+
```
|
27
|
+
- Allow for a `Rails.application.config_for` like experience
|
28
|
+
```ruby
|
29
|
+
Nero.configure {|nero| nero.config_dir = Rails.root / "config" }
|
30
|
+
|
31
|
+
Nero.load_config(:stripe, root: Rails.env)
|
32
|
+
# Returns content of Rails.root / "config/stripe.yml"
|
33
|
+
```
|
34
|
+
- Add `Nero.load` like `YAML.load`
|
35
|
+
```ruby
|
36
|
+
Nero.load(<<~YAML)
|
37
|
+
cache_ttl: !duration [1, day]
|
38
|
+
end
|
39
|
+
# => {cache_ttl: 86400}
|
40
|
+
```
|
41
|
+
|
3
42
|
## [0.1.0] - 2025-01-24
|
4
43
|
|
5
44
|
- Initial release
|
data/README.md
CHANGED
@@ -4,32 +4,28 @@
|
|
4
4
|
|
5
5
|
Nero is a RubyGem that offers predefined tags and allows you to effortlessly create custom ones for YAML configuration files.
|
6
6
|
|
7
|
-
E.g. instead of having the following settings file in your
|
7
|
+
E.g. instead of having the following settings file in your Rails project:
|
8
8
|
|
9
9
|
```yaml
|
10
10
|
development:
|
11
|
-
# env-var with a fallback
|
12
11
|
secret: <%= ENV.fetch("SECRET", "dummy") %>
|
13
|
-
#
|
14
|
-
debug?: <%=
|
12
|
+
# custom logic how to get a boolean...
|
13
|
+
debug?: <%= ENV["DEBUG"] == "true" %>
|
15
14
|
production:
|
16
|
-
#
|
17
|
-
# as it would require the env-var for development as well
|
15
|
+
# any ENV.fetch in this section would hamper local development...
|
18
16
|
secret: <%= ENV["SECRET"] %>
|
17
|
+
# custom coercion logic
|
19
18
|
max_threads: <%= ENV.fetch("MAX_THREADS", 5).to_i %>
|
20
19
|
```
|
21
20
|
|
22
21
|
...turn it into this:
|
23
22
|
```yaml
|
24
23
|
development:
|
25
|
-
# env-var with a fallback
|
26
24
|
secret: !env [SECRET, "dummy"]
|
27
|
-
# Though the default is false, explicitly providing "false"/"off"/"n"/"no" is also possible.
|
28
25
|
debug?: !env/bool? DEBUG
|
29
26
|
production:
|
30
|
-
#
|
27
|
+
# required _only_ when loading production
|
31
28
|
secret: !env SECRET
|
32
|
-
# always an integer
|
33
29
|
max_threads: !env/integer [MAX_THREADS, 5]
|
34
30
|
```
|
35
31
|
|
@@ -52,7 +48,7 @@ Given the following config:
|
|
52
48
|
development:
|
53
49
|
# env-var with a fallback
|
54
50
|
secret: !env [SECRET, "dummy"]
|
55
|
-
# Though the default is false, explicitly providing "false"/"off"/"n"/"no"
|
51
|
+
# Though the default is false, explicitly providing "false"/"off"/"n"/"no" also works.
|
56
52
|
debug?: !env/bool? DEBUG
|
57
53
|
production:
|
58
54
|
# fail-fast on absence of SECRET
|
@@ -65,7 +61,7 @@ Loading this config:
|
|
65
61
|
|
66
62
|
```ruby
|
67
63
|
# Loading development
|
68
|
-
Nero.load_config(
|
64
|
+
Nero.load_config("config/settings", root: :development)
|
69
65
|
# ...and no ENV-vars were provided
|
70
66
|
#=> {secret: "dummy", debug?: false}
|
71
67
|
|
@@ -73,7 +69,7 @@ Nero.load_config(Pathname.pwd / "config/settings.yml", root: :development)
|
|
73
69
|
#=> {secret: "dummy", debug?: true}
|
74
70
|
|
75
71
|
# Loading production
|
76
|
-
Nero.load_config(
|
72
|
+
Nero.load_config("config/settings", root: :production)
|
77
73
|
# ...and no ENV-vars were provided
|
78
74
|
# raises error: key not found: "SECRET" (KeyError)
|
79
75
|
|
@@ -139,10 +135,10 @@ The following tags are provided:
|
|
139
135
|
pass: !env SMTP_PASS
|
140
136
|
```
|
141
137
|
|
142
|
-
|
138
|
+
Add one yourself:
|
143
139
|
```ruby
|
144
|
-
Nero.configure do
|
145
|
-
add_tag("foo") do |coder|
|
140
|
+
Nero.configure do |nero|
|
141
|
+
nero.add_tag("foo") do |coder|
|
146
142
|
# coder.type is one of :scalar, :seq or :map
|
147
143
|
# e.g. respective YAML:
|
148
144
|
# ---
|
@@ -157,6 +153,12 @@ Nero.configure do
|
|
157
153
|
# Find the value in the respective attribute, e.g. `coder.scalar`:
|
158
154
|
coder.scalar.upcase
|
159
155
|
end
|
156
|
+
|
157
|
+
# Other configuration options:
|
158
|
+
#
|
159
|
+
# `config_dir` (default: Pathname.pwd) - path used for expanding non-Pathnames passed to `load_config`, e.g.
|
160
|
+
# `Nero.load_config(:app)` loads file `Pathname.pwd / "app.yml"`.
|
161
|
+
nero.config_dir = Rails.root / "config"
|
160
162
|
end
|
161
163
|
```
|
162
164
|
|
data/lib/nero/version.rb
CHANGED
data/lib/nero.rb
CHANGED
@@ -5,6 +5,7 @@ loader = Zeitwerk::Loader.for_gem
|
|
5
5
|
loader.setup
|
6
6
|
|
7
7
|
require "uri" # why needed?
|
8
|
+
require "yaml"
|
8
9
|
|
9
10
|
# TODO fail on unknown tag
|
10
11
|
# TODO show missing env's at once
|
@@ -30,7 +31,10 @@ module Nero
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
extend Resolvable
|
33
|
-
private_class_method
|
34
|
+
private_class_method \
|
35
|
+
:deep_resolve,
|
36
|
+
:gen_resolve_tryer,
|
37
|
+
:try_resolve
|
34
38
|
|
35
39
|
class TagResolver
|
36
40
|
include Resolvable
|
@@ -41,7 +45,7 @@ module Nero
|
|
41
45
|
|
42
46
|
def resolve(ctx)
|
43
47
|
resolve_nested!(ctx)
|
44
|
-
ctx[:
|
48
|
+
ctx[:tags][@coder.tag].call(@coder)
|
45
49
|
end
|
46
50
|
|
47
51
|
def resolve_nested!(ctx)
|
@@ -54,106 +58,162 @@ module Nero
|
|
54
58
|
end
|
55
59
|
end
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
def self.env_fetch(k, fallback = nil, all_optional: "dummy")
|
62
|
-
fallback ||= all_optional if ENV["NERO_ENV_ALL_OPTIONAL"]
|
61
|
+
class Configuration
|
62
|
+
attr_reader :tags
|
63
|
+
attr_accessor :config_dir
|
63
64
|
|
64
|
-
|
65
|
+
def add_tag(name, &block)
|
66
|
+
(@tags ||= {})["!#{name}"] = block
|
67
|
+
end
|
65
68
|
end
|
66
|
-
private_class_method :env_fetch
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
+
def self.configuration
|
71
|
+
@configuration ||= Configuration.new
|
70
72
|
end
|
71
73
|
|
72
|
-
|
73
|
-
|
74
|
+
def self.configure
|
75
|
+
yield configuration if block_given?
|
74
76
|
end
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
def self.add_default_tags!
|
79
|
+
configure do |config|
|
80
|
+
config.add_tag("env/integer") do |coder|
|
81
|
+
Integer(env_fetch(*(coder.scalar || coder.seq), all_optional: "999"))
|
82
|
+
end
|
79
83
|
|
80
|
-
|
81
|
-
|
82
|
-
when TrueClass, FalseClass then s
|
83
|
-
when re_true then true
|
84
|
-
when re_false then false
|
85
|
-
else
|
86
|
-
raise "bool value should be one of y(es)/n(o), on/off, true/false (got #{s.inspect})"
|
84
|
+
config.add_tag("env/integer?") do |coder|
|
85
|
+
Integer(ENV[coder.scalar]) if ENV[coder.scalar]
|
87
86
|
end
|
88
|
-
end
|
89
87
|
|
90
|
-
|
91
|
-
|
88
|
+
config.add_tag("env/bool") do |coder|
|
89
|
+
re_true = /y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON/
|
90
|
+
re_false = /n|N|no|No|NO|false|False|FALSE|off|Off|OFF/
|
91
|
+
|
92
|
+
coerce = ->(s) do
|
93
|
+
case s
|
94
|
+
when TrueClass, FalseClass then s
|
95
|
+
when re_true then true
|
96
|
+
when re_false then false
|
97
|
+
else
|
98
|
+
raise "bool value should be one of y(es)/n(o), on/off, true/false (got #{s.inspect})"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
coerce[env_fetch(*(coder.scalar || coder.seq), all_optional: "false")]
|
103
|
+
end
|
92
104
|
|
93
|
-
|
94
|
-
|
95
|
-
|
105
|
+
config.add_tag("env/bool?") do |coder|
|
106
|
+
re_true = /y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON/
|
107
|
+
re_false = /n|N|no|No|NO|false|False|FALSE|off|Off|OFF/
|
108
|
+
|
109
|
+
coerce = ->(s) do
|
110
|
+
case s
|
111
|
+
when TrueClass, FalseClass then s
|
112
|
+
when re_true then true
|
113
|
+
when re_false then false
|
114
|
+
else
|
115
|
+
raise "bool value should be one of y(es)/n(o), on/off, true/false (got #{s.inspect})"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
ENV[coder.scalar] ? coerce[ENV[coder.scalar]] : false
|
120
|
+
end
|
96
121
|
|
97
|
-
|
98
|
-
|
99
|
-
when TrueClass, FalseClass then s
|
100
|
-
when re_true then true
|
101
|
-
when re_false then false
|
102
|
-
else
|
103
|
-
raise "bool value should be one of y(es)/n(o), on/off, true/false (got #{s.inspect})"
|
122
|
+
config.add_tag("env") do |coder|
|
123
|
+
env_fetch(*(coder.scalar || coder.seq))
|
104
124
|
end
|
105
|
-
end
|
106
125
|
|
107
|
-
|
108
|
-
|
126
|
+
config.add_tag("env?") do |coder|
|
127
|
+
fetch_args = coder.scalar ? [coder.scalar, nil] : coder.seq
|
128
|
+
ENV.fetch(*fetch_args)
|
129
|
+
end
|
130
|
+
|
131
|
+
config.add_tag("path") do |coder|
|
132
|
+
Pathname.new(coder.scalar || coder.seq.join("/"))
|
133
|
+
end
|
134
|
+
|
135
|
+
config.add_tag("uri") do |coder|
|
136
|
+
URI(coder.scalar || coder.seq.join)
|
137
|
+
end
|
109
138
|
|
110
|
-
|
111
|
-
|
139
|
+
config.add_tag("str/format") do |coder|
|
140
|
+
case coder.type
|
141
|
+
when :seq
|
142
|
+
sprintf(*coder.seq)
|
143
|
+
when :map
|
144
|
+
m = Util.deep_symbolize_keys(coder.map)
|
145
|
+
fmt = m.delete(:fmt)
|
146
|
+
sprintf(fmt, m)
|
147
|
+
else
|
148
|
+
coder.scalar
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
112
152
|
end
|
153
|
+
private_class_method :add_default_tags!
|
154
|
+
|
155
|
+
def self.reset_configuration!
|
156
|
+
@configuration = nil
|
113
157
|
|
114
|
-
|
115
|
-
|
116
|
-
|
158
|
+
configure do |config|
|
159
|
+
config.config_dir = Pathname.pwd
|
160
|
+
end
|
161
|
+
|
162
|
+
add_default_tags!
|
117
163
|
end
|
164
|
+
reset_configuration!
|
165
|
+
|
166
|
+
def self.env_fetch(k, fallback = nil, all_optional: "dummy")
|
167
|
+
fallback ||= all_optional if ENV["NERO_ENV_ALL_OPTIONAL"]
|
118
168
|
|
119
|
-
|
120
|
-
Pathname.new(coder.scalar || coder.seq.join("/"))
|
169
|
+
fallback.nil? ? ENV.fetch(k) : ENV.fetch(k, fallback)
|
121
170
|
end
|
171
|
+
private_class_method :env_fetch
|
172
|
+
|
173
|
+
@yaml_options = {
|
174
|
+
permitted_classes: [Symbol, TagResolver],
|
175
|
+
aliases: true
|
176
|
+
}
|
177
|
+
|
178
|
+
def self.load_config(file, root: nil)
|
179
|
+
add_tags!
|
180
|
+
|
181
|
+
file = resolve_file(file)
|
122
182
|
|
123
|
-
|
124
|
-
|
183
|
+
if file.exist?
|
184
|
+
process_yaml(YAML.load_file(file, **@yaml_options), root:)
|
185
|
+
else
|
186
|
+
raise "Can't find file #{file}"
|
187
|
+
end
|
125
188
|
end
|
126
189
|
|
127
|
-
|
128
|
-
case
|
129
|
-
when
|
130
|
-
|
131
|
-
when :map
|
132
|
-
m = Util.deep_symbolize_keys(coder.map)
|
133
|
-
fmt = m.delete(:fmt)
|
134
|
-
sprintf(fmt, m)
|
190
|
+
def self.resolve_file(file)
|
191
|
+
case file
|
192
|
+
when Pathname then file
|
193
|
+
# TODO expand full path
|
135
194
|
else
|
136
|
-
|
195
|
+
configuration.config_dir / "#{file}.yml"
|
137
196
|
end
|
138
197
|
end
|
198
|
+
private_class_method :resolve_file
|
139
199
|
|
140
|
-
def self.
|
200
|
+
def self.load(raw, root: nil)
|
141
201
|
add_tags!
|
142
202
|
|
143
|
-
|
144
|
-
|
145
|
-
permitted_classes: [Symbol, TagResolver], aliases: true)).then do
|
146
|
-
root ? _1[root.to_sym] : _1
|
147
|
-
end
|
203
|
+
process_yaml(YAML.load(raw, **@yaml_options), root:)
|
204
|
+
end
|
148
205
|
|
149
|
-
|
150
|
-
|
151
|
-
|
206
|
+
def self.process_yaml(yaml, root: nil)
|
207
|
+
unresolved = Util.deep_symbolize_keys(yaml).then do
|
208
|
+
root ? _1[root.to_sym] : _1
|
152
209
|
end
|
210
|
+
|
211
|
+
deep_resolve(unresolved, tags: configuration.tags)
|
153
212
|
end
|
213
|
+
private_class_method :process_yaml
|
154
214
|
|
155
215
|
def self.add_tags!
|
156
|
-
|
216
|
+
configuration.tags.keys.each do
|
157
217
|
YAML.add_tag(_1, TagResolver)
|
158
218
|
end
|
159
219
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gert Goet
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-02-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: zeitwerk
|