nero 0.2.2 → 0.4.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/Appraisals +7 -0
- data/CHANGELOG.md +53 -0
- data/README.md +59 -27
- data/gemfiles/psych_3.gemfile +10 -0
- data/gemfiles/psych_4.gemfile +10 -0
- data/lib/nero/version.rb +1 -1
- data/lib/nero.rb +162 -67
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9822fcc078e4b2c0e9f049da7cf6f37a98a5b9cf607434057f266a393154296e
|
4
|
+
data.tar.gz: b71a5be94f469b564e3a80c80a0545246afbc053f619201f8a0c6c0923a776a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 863f2c0fbeb21286a0d0f51219e07351a93489688eb7c49be5c1c720a3e414d8e43129e2c0e4d23c6e44213e8547a03c28df128210026414e9e3e209bb7ef472
|
7
|
+
data.tar.gz: c431e49a4d5d422b554a891973c0e24b7c1810dd922492d56b39ca6157dc70e3110114d7474d0d2ed3b5076f2bea63d8493b2cd9db64c3354ea051022f8887cb
|
data/Appraisals
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,57 @@
|
|
1
1
|
## [Unreleased]
|
2
|
+
...
|
3
|
+
|
4
|
+
## [0.4.0] - 2025-02-15
|
5
|
+
|
6
|
+
- Add `!ref`-tag:
|
7
|
+
```ruby
|
8
|
+
Nero.load(<<~YAML)
|
9
|
+
min_threads: !env [MIN_THREADS, !ref [max_threads]]
|
10
|
+
max_threads: 5
|
11
|
+
end
|
12
|
+
# => {min_threads: 5, max_threads: 5}
|
13
|
+
```
|
14
|
+
- Support Psych v3
|
15
|
+
...so it can used with Rails v6
|
16
|
+
|
17
|
+
## [0.3.0] - 2025-02-02
|
18
|
+
|
19
|
+
- Add configuration
|
20
|
+
For custom tags:
|
21
|
+
```ruby
|
22
|
+
Nero.configure do |nero|
|
23
|
+
nero.add_tag("duration") do |coder|
|
24
|
+
num, duration = coder.seq
|
25
|
+
mult = case duration
|
26
|
+
when /^seconds?/ then 1
|
27
|
+
when /^minutes?$/ then 60
|
28
|
+
when /^hours?$/ then 60 *60
|
29
|
+
when /^days?$/ then 24 * 60 * 60
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Unknown duration #{coder.seq.inspect}"
|
32
|
+
end
|
33
|
+
num * mult
|
34
|
+
end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
...and config_dir:
|
38
|
+
```ruby
|
39
|
+
Nero.configure {|nero| nero.config_dir = Rails.root / "config" }
|
40
|
+
```
|
41
|
+
- Allow for a `Rails.application.config_for` like experience
|
42
|
+
```ruby
|
43
|
+
Nero.configure {|nero| nero.config_dir = Rails.root / "config" }
|
44
|
+
|
45
|
+
Nero.load_config(:stripe, root: Rails.env)
|
46
|
+
# Returns content of Rails.root / "config/stripe.yml"
|
47
|
+
```
|
48
|
+
- Add `Nero.load` like `YAML.load`
|
49
|
+
```ruby
|
50
|
+
Nero.load(<<~YAML)
|
51
|
+
cache_ttl: !duration [1, day]
|
52
|
+
end
|
53
|
+
# => {cache_ttl: 86400}
|
54
|
+
```
|
2
55
|
|
3
56
|
## [0.1.0] - 2025-01-24
|
4
57
|
|
data/README.md
CHANGED
@@ -2,37 +2,33 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/nero)
|
4
4
|
|
5
|
-
Nero is a RubyGem that offers
|
5
|
+
Nero is a RubyGem that offers declarative YAML-tags to simplify config files, e.g. for requiring and coercion of env-vars.
|
6
|
+
Additionally, it allows you to create your own.
|
6
7
|
|
7
|
-
|
8
|
+
**Sample:**
|
8
9
|
|
9
10
|
```yaml
|
10
11
|
development:
|
11
|
-
# env-var with
|
12
|
-
secret: <%= ENV.fetch("SECRET", "dummy") %>
|
13
|
-
# NOTE *any* value provided is taken as `true`
|
14
|
-
debug?: <%= !!ENV["DEBUG"] %>
|
15
|
-
production:
|
16
|
-
# NOTE we can't fail-fast on ENV-var absence (i.e. use `ENV.fetch`),
|
17
|
-
# as it would require the env-var for development as well
|
18
|
-
secret: <%= ENV["SECRET"] %>
|
19
|
-
max_threads: <%= ENV.fetch("MAX_THREADS", 5).to_i %>
|
20
|
-
```
|
21
|
-
|
22
|
-
...turn it into this:
|
23
|
-
```yaml
|
24
|
-
development:
|
25
|
-
# env-var with a fallback
|
12
|
+
# env-var with default value
|
26
13
|
secret: !env [SECRET, "dummy"]
|
27
|
-
#
|
14
|
+
# optional env-var with coercion
|
28
15
|
debug?: !env/bool? DEBUG
|
29
16
|
production:
|
30
|
-
#
|
17
|
+
# required env-var (only when getting the production-root)
|
31
18
|
secret: !env SECRET
|
32
|
-
#
|
19
|
+
# int coercion
|
33
20
|
max_threads: !env/integer [MAX_THREADS, 5]
|
21
|
+
# something custom
|
22
|
+
cache_ttl: !duration [2, hours]
|
34
23
|
```
|
35
24
|
|
25
|
+
## Highlights
|
26
|
+
|
27
|
+
* 💎 declarative YAML-tags for e.g. requiring and coercing env-vars
|
28
|
+
* 🛠️ add custom tags
|
29
|
+
* 🛤️ `Rails.application.config_for` stand-in
|
30
|
+
* ♻️ Zeitwerk-only dependency
|
31
|
+
|
36
32
|
## Installation
|
37
33
|
|
38
34
|
Install the gem and add to the application's Gemfile by executing:
|
@@ -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
|
|
@@ -132,17 +128,42 @@ The following tags are provided:
|
|
132
128
|
- smtps://%s:%s@smtp.gmail.com
|
133
129
|
- !env SMTP_USER
|
134
130
|
- !env SMTP_PASS
|
135
|
-
|
131
|
+
|
132
|
+
# pass it a map (including a key 'fmt') to use references
|
136
133
|
smtp_url: !str/format
|
137
134
|
fmt: smtps://%<user>s:%<pass>s@smtp.gmail.com
|
138
135
|
user: !env SMTP_USER
|
139
136
|
pass: !env SMTP_PASS
|
140
137
|
```
|
138
|
+
- `!ref`
|
139
|
+
Include values from elsewhere:
|
140
|
+
```yaml
|
141
|
+
# simple
|
142
|
+
min_threads: !env [MIN_THREADS, !ref [max_threads]]
|
143
|
+
max_threads: 5
|
144
|
+
|
145
|
+
# oauth_callback -refs-> base.url -refs-> base.host
|
146
|
+
base:
|
147
|
+
host: !env [HOST]
|
148
|
+
url: !str/format ['https://%s', !ref[base, host]]
|
149
|
+
oauth_callback: !str/format
|
150
|
+
- '%s/oauth/callback'
|
151
|
+
- !ref[base, url]
|
152
|
+
|
153
|
+
# refs are resolved within the tree of the selected root.
|
154
|
+
# The following config won't work when doing `Nero.load_config(:app, root: :prod)`
|
155
|
+
dev:
|
156
|
+
max_threads: 5
|
157
|
+
prod:
|
158
|
+
max_threads: !env[MAX_THREADS, !ref[dev, max_threads]]
|
159
|
+
```
|
160
|
+
NOTE future version should raise properly over ref-ing a non-existing path.
|
161
|
+
|
141
162
|
|
142
|
-
|
163
|
+
Add one yourself:
|
143
164
|
```ruby
|
144
|
-
Nero.configure do
|
145
|
-
add_tag("foo") do |coder|
|
165
|
+
Nero.configure do |nero|
|
166
|
+
nero.add_tag("foo") do |coder|
|
146
167
|
# coder.type is one of :scalar, :seq or :map
|
147
168
|
# e.g. respective YAML:
|
148
169
|
# ---
|
@@ -156,7 +177,18 @@ Nero.configure do
|
|
156
177
|
#
|
157
178
|
# Find the value in the respective attribute, e.g. `coder.scalar`:
|
158
179
|
coder.scalar.upcase
|
180
|
+
|
181
|
+
# NOTE when needing just one argument, supporting both scalar and seq allows for chaining:
|
182
|
+
# a: !my/inc 4 # scalar suffices
|
183
|
+
# ...but when chaining, a seq is required:
|
184
|
+
# a: !my/inc [!my/square 2]
|
159
185
|
end
|
186
|
+
|
187
|
+
# Other configuration options:
|
188
|
+
#
|
189
|
+
# `config_dir` (default: Pathname.pwd) - path used for expanding non-Pathnames passed to `load_config`, e.g.
|
190
|
+
# `Nero.load_config(:app)` loads file `Pathname.pwd / "app.yml"`.
|
191
|
+
nero.config_dir = Rails.root / "config"
|
160
192
|
end
|
161
193
|
```
|
162
194
|
|
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, ctx)
|
45
49
|
end
|
46
50
|
|
47
51
|
def resolve_nested!(ctx)
|
@@ -54,106 +58,197 @@ 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
|
+
# helpers for configuration
|
79
|
+
# module TagHelpers
|
80
|
+
# def to_boolean(s)
|
81
|
+
# end
|
82
|
+
# end
|
79
83
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
def self.add_default_tags!
|
85
|
+
# extend TagHelpers
|
86
|
+
|
87
|
+
configure do |config|
|
88
|
+
config.add_tag("ref") do |coder, ctx|
|
89
|
+
# validate: non-empty coder.seq, only strs, path must exists in ctx[:config]
|
90
|
+
|
91
|
+
path = coder.seq.map(&:to_sym)
|
92
|
+
deep_resolve(ctx[:config].dig(*path), **ctx)
|
87
93
|
end
|
88
|
-
end
|
89
94
|
|
90
|
-
|
91
|
-
|
95
|
+
config.add_tag("env/integer") do |coder|
|
96
|
+
Integer(env_fetch(*(coder.scalar || coder.seq), all_optional: "999"))
|
97
|
+
end
|
92
98
|
|
93
|
-
|
94
|
-
|
95
|
-
|
99
|
+
config.add_tag("env/integer?") do |coder|
|
100
|
+
Integer(ENV[coder.scalar]) if ENV[coder.scalar]
|
101
|
+
end
|
96
102
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
103
|
+
config.add_tag("env/bool") do |coder|
|
104
|
+
re_true = /y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON/
|
105
|
+
re_false = /n|N|no|No|NO|false|False|FALSE|off|Off|OFF/
|
106
|
+
|
107
|
+
coerce = ->(s) do
|
108
|
+
case s
|
109
|
+
when TrueClass, FalseClass then s
|
110
|
+
when re_true then true
|
111
|
+
when re_false then false
|
112
|
+
else
|
113
|
+
raise "bool value should be one of y(es)/n(o), on/off, true/false (got #{s.inspect})"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
coerce[env_fetch(*(coder.scalar || coder.seq), all_optional: "false")]
|
104
118
|
end
|
105
|
-
end
|
106
119
|
|
107
|
-
|
108
|
-
|
120
|
+
config.add_tag("env/bool?") do |coder|
|
121
|
+
re_true = /y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON/
|
122
|
+
re_false = /n|N|no|No|NO|false|False|FALSE|off|Off|OFF/
|
123
|
+
|
124
|
+
coerce = ->(s) do
|
125
|
+
case s
|
126
|
+
when TrueClass, FalseClass then s
|
127
|
+
when re_true then true
|
128
|
+
when re_false then false
|
129
|
+
else
|
130
|
+
raise "bool value should be one of y(es)/n(o), on/off, true/false (got #{s.inspect})"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
ENV[coder.scalar] ? coerce[ENV[coder.scalar]] : false
|
135
|
+
end
|
136
|
+
|
137
|
+
config.add_tag("env") do |coder|
|
138
|
+
env_fetch(*(coder.scalar || coder.seq))
|
139
|
+
end
|
140
|
+
|
141
|
+
config.add_tag("env?") do |coder|
|
142
|
+
fetch_args = coder.scalar ? [coder.scalar, nil] : coder.seq
|
143
|
+
ENV.fetch(*fetch_args)
|
144
|
+
end
|
145
|
+
|
146
|
+
config.add_tag("path") do |coder|
|
147
|
+
Pathname.new(coder.scalar || coder.seq.join("/"))
|
148
|
+
end
|
149
|
+
|
150
|
+
config.add_tag("uri") do |coder|
|
151
|
+
URI(coder.scalar || coder.seq.join)
|
152
|
+
end
|
109
153
|
|
110
|
-
|
111
|
-
|
154
|
+
config.add_tag("str/format") do |coder|
|
155
|
+
case coder.type
|
156
|
+
when :seq
|
157
|
+
sprintf(*coder.seq)
|
158
|
+
when :map
|
159
|
+
m = Util.deep_symbolize_keys(coder.map)
|
160
|
+
fmt = m.delete(:fmt)
|
161
|
+
sprintf(fmt, m)
|
162
|
+
else
|
163
|
+
coder.scalar
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
112
167
|
end
|
168
|
+
private_class_method :add_default_tags!
|
169
|
+
|
170
|
+
def self.reset_configuration!
|
171
|
+
@configuration = nil
|
172
|
+
|
173
|
+
configure do |config|
|
174
|
+
config.config_dir = Pathname.pwd
|
175
|
+
end
|
113
176
|
|
114
|
-
|
115
|
-
fetch_args = coder.scalar ? [coder.scalar, nil] : coder.seq
|
116
|
-
ENV.fetch(*fetch_args)
|
177
|
+
add_default_tags!
|
117
178
|
end
|
179
|
+
reset_configuration!
|
180
|
+
|
181
|
+
def self.env_fetch(k, fallback = nil, all_optional: "dummy")
|
182
|
+
fallback ||= all_optional if ENV["NERO_ENV_ALL_OPTIONAL"]
|
118
183
|
|
119
|
-
|
120
|
-
Pathname.new(coder.scalar || coder.seq.join("/"))
|
184
|
+
fallback.nil? ? ENV.fetch(k) : ENV.fetch(k, fallback)
|
121
185
|
end
|
186
|
+
private_class_method :env_fetch
|
122
187
|
|
123
|
-
|
124
|
-
|
188
|
+
@yaml_options = {
|
189
|
+
permitted_classes: [Symbol, TagResolver],
|
190
|
+
aliases: true
|
191
|
+
}
|
192
|
+
|
193
|
+
def self.load_config(file, root: nil, env: nil)
|
194
|
+
root ||= env
|
195
|
+
add_tags!
|
196
|
+
|
197
|
+
file = resolve_file(file)
|
198
|
+
|
199
|
+
if file.exist?
|
200
|
+
process_yaml(yaml_load_file(file, @yaml_options), root:)
|
201
|
+
else
|
202
|
+
raise "Can't find file #{file}"
|
203
|
+
end
|
125
204
|
end
|
126
205
|
|
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)
|
206
|
+
def self.resolve_file(file)
|
207
|
+
case file
|
208
|
+
when Pathname then file
|
209
|
+
# TODO expand full path
|
135
210
|
else
|
136
|
-
|
211
|
+
configuration.config_dir / "#{file}.yml"
|
137
212
|
end
|
138
213
|
end
|
214
|
+
private_class_method :resolve_file
|
139
215
|
|
140
|
-
def self.
|
216
|
+
def self.load(raw, root: nil, env: nil)
|
217
|
+
root ||= env
|
141
218
|
add_tags!
|
142
219
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
220
|
+
process_yaml(yaml_load(raw, @yaml_options), root:)
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.process_yaml(yaml, root: nil)
|
224
|
+
unresolved = Util.deep_symbolize_keys(yaml).then do
|
225
|
+
root ? _1[root.to_sym] : _1
|
226
|
+
end
|
148
227
|
|
149
|
-
|
228
|
+
deep_resolve(unresolved, tags: configuration.tags, config: unresolved)
|
229
|
+
end
|
230
|
+
private_class_method :process_yaml
|
231
|
+
|
232
|
+
def self.yaml_load_file(file, opts = {})
|
233
|
+
if Psych::VERSION < "4"
|
234
|
+
YAML.load_file(file)
|
150
235
|
else
|
151
|
-
|
236
|
+
YAML.load_file(file, **opts)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
private_class_method :yaml_load_file
|
240
|
+
|
241
|
+
def self.yaml_load(file, opts = {})
|
242
|
+
if Psych::VERSION < "4"
|
243
|
+
YAML.load(file)
|
244
|
+
else
|
245
|
+
YAML.load(file, **opts)
|
152
246
|
end
|
153
247
|
end
|
248
|
+
private_class_method :yaml_load
|
154
249
|
|
155
250
|
def self.add_tags!
|
156
|
-
|
251
|
+
configuration.tags.keys.each do
|
157
252
|
YAML.add_tag(_1, TagResolver)
|
158
253
|
end
|
159
254
|
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.4.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-15 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: zeitwerk
|
@@ -23,6 +23,20 @@ dependencies:
|
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: appraisal
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
26
40
|
description: |
|
27
41
|
Some convenient YAML-tags...
|
28
42
|
- to get environment values: env, env?, env/integer, env/integer?, env/bool, env/bool?.
|
@@ -39,10 +53,13 @@ files:
|
|
39
53
|
- ".envrc"
|
40
54
|
- ".rspec"
|
41
55
|
- ".standard.yml"
|
56
|
+
- Appraisals
|
42
57
|
- CHANGELOG.md
|
43
58
|
- LICENSE.txt
|
44
59
|
- README.md
|
45
60
|
- Rakefile
|
61
|
+
- gemfiles/psych_3.gemfile
|
62
|
+
- gemfiles/psych_4.gemfile
|
46
63
|
- lib/nero.rb
|
47
64
|
- lib/nero/util.rb
|
48
65
|
- lib/nero/version.rb
|