foobara-util 1.0.7 → 1.1.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 +9 -1
- data/lib/foobara/truncated_inspect.rb +1 -1
- data/lib/foobara/util/args.rb +18 -18
- data/lib/foobara/util/array.rb +1 -1
- data/lib/foobara/util/hash.rb +56 -7
- data/lib/foobara/util/meta.rb +1 -2
- data/lib/foobara/util/module.rb +4 -4
- data/lib/foobara/util/object_space.rb +13 -16
- data/lib/foobara/util/require.rb +2 -2
- data/lib/foobara/util/string.rb +48 -7
- data/lib/foobara/util.rb +6 -1
- 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: 773e6de381b5e09f00f4145f882cca6a35ff48c04905275ec102aad86bdaab8b
|
|
4
|
+
data.tar.gz: 16092889c533865713a4ea719c9027d00b6ff3eb78e195086ccd7c5d38939e18
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '06831ffb45e2c36640f50825c1ae4367b776154a87c770d0bb94ffc5eab2b879263ae3b73b421af5f118607a7151a7bb12991510ef37d4f4a6777b89661297a2'
|
|
7
|
+
data.tar.gz: b80cf394837039df4790b159f19039e6023e701c2c0705c87a355fbee9f6b0a52e35975eec53045867132a60f0162a0e6e5187ec3764fe94fe2e650b81f00995
|
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
## [1.0.9] - 2026-09-22
|
|
2
|
+
|
|
3
|
+
- Don't load "foobara/util/object_space" by default
|
|
4
|
+
|
|
5
|
+
## [1.0.8] - 2026-01-22
|
|
6
|
+
|
|
7
|
+
- Add .sort_by_keys/.sort_by_keys! for hashes
|
|
8
|
+
|
|
1
9
|
## [1.0.7] - 2025-12-19
|
|
2
10
|
|
|
3
11
|
- Fix bug where .all_symbolic_keys? would return true for a hash with a string key
|
|
4
|
-
- Handle situations where Hash subclass or non-Hash are passed to .symbolize_keys
|
|
12
|
+
- Handle situations where Hash subclass or non-Hash are passed to .symbolize_keys and .symbolize_keys!
|
|
5
13
|
|
|
6
14
|
## [1.0.6] - 2025-12-14
|
|
7
15
|
|
|
@@ -52,7 +52,7 @@ module Foobara
|
|
|
52
52
|
|
|
53
53
|
instance_vars = instance_vars.join(", ")
|
|
54
54
|
|
|
55
|
-
result = "#<#{self.class.name}:0x#{
|
|
55
|
+
result = "#<#{self.class.name}:0x#{__id__.to_s(16)} #{instance_vars}>"
|
|
56
56
|
|
|
57
57
|
if result.size > MAX_LENGTH
|
|
58
58
|
result = "#{result[0..(MAX_LENGTH - 5)]}...>"
|
data/lib/foobara/util/args.rb
CHANGED
|
@@ -4,15 +4,15 @@ module Foobara
|
|
|
4
4
|
|
|
5
5
|
def args_and_opts_to_opts(args, opts)
|
|
6
6
|
unless args.is_a?(::Array)
|
|
7
|
-
# :
|
|
7
|
+
# simplecov:disable
|
|
8
8
|
raise ArgumentError, "args must be an array of 0 or 1 hashes but received #{args}"
|
|
9
|
-
# :
|
|
9
|
+
# simplecov:enable
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
unless opts.is_a?(::Hash)
|
|
13
|
-
# :
|
|
13
|
+
# simplecov:disable
|
|
14
14
|
raise ArgumentError, "opts must be a hash not a #{opts.class}"
|
|
15
|
-
# :
|
|
15
|
+
# simplecov:enable
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
case args.size
|
|
@@ -23,9 +23,9 @@ module Foobara
|
|
|
23
23
|
|
|
24
24
|
if opts && !opts.empty?
|
|
25
25
|
unless arg.is_a?(::Hash)
|
|
26
|
-
# :
|
|
26
|
+
# simplecov:disable
|
|
27
27
|
raise ArgumentError, "opts must be a hash not a #{arg.class}"
|
|
28
|
-
# :
|
|
28
|
+
# simplecov:enable
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
arg.merge(opts)
|
|
@@ -33,9 +33,9 @@ module Foobara
|
|
|
33
33
|
arg
|
|
34
34
|
end
|
|
35
35
|
else
|
|
36
|
-
# :
|
|
36
|
+
# simplecov:disable
|
|
37
37
|
raise ArgumentError, "args must be an array of 0 or 1 hashes but received #{args}"
|
|
38
|
-
# :
|
|
38
|
+
# simplecov:enable
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -43,15 +43,15 @@ module Foobara
|
|
|
43
43
|
if arg && !arg.empty?
|
|
44
44
|
if opts && !opts.empty?
|
|
45
45
|
unless opts.is_a?(::Hash)
|
|
46
|
-
# :
|
|
46
|
+
# simplecov:disable
|
|
47
47
|
raise ArgumentError, "opts must be a hash not a #{opts}"
|
|
48
|
-
# :
|
|
48
|
+
# simplecov:enable
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
unless arg.is_a?(::Hash)
|
|
52
|
-
# :
|
|
52
|
+
# simplecov:disable
|
|
53
53
|
raise ArgumentError, "arg must be a hash if present when opts is present"
|
|
54
|
-
# :
|
|
54
|
+
# simplecov:enable
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
arg.merge(opts)
|
|
@@ -70,9 +70,9 @@ module Foobara
|
|
|
70
70
|
# argument with stuff merged into the argument if needed.
|
|
71
71
|
def args_and_opts_to_args(args, opts)
|
|
72
72
|
unless args.is_a?(::Array)
|
|
73
|
-
# :
|
|
73
|
+
# simplecov:disable
|
|
74
74
|
raise ArgumentError, "args must be an array of 0 or 1 hashes but received #{args}"
|
|
75
|
-
# :
|
|
75
|
+
# simplecov:enable
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
case args.size
|
|
@@ -84,9 +84,9 @@ module Foobara
|
|
|
84
84
|
arg = args.first
|
|
85
85
|
|
|
86
86
|
unless arg.is_a?(::Hash)
|
|
87
|
-
# :
|
|
87
|
+
# simplecov:disable
|
|
88
88
|
raise ArgumentError, "Expected #{arg.inspect} to be a Hash"
|
|
89
|
-
# :
|
|
89
|
+
# simplecov:enable
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
[arg_and_opts_to_arg(args.first, opts)]
|
|
@@ -96,9 +96,9 @@ module Foobara
|
|
|
96
96
|
|
|
97
97
|
[arg_and_opts_to_arg(args.first, opts)]
|
|
98
98
|
else
|
|
99
|
-
# :
|
|
99
|
+
# simplecov:disable
|
|
100
100
|
raise ArgumentError, "args must be an array of 0 or 1 hashes but received #{args}"
|
|
101
|
-
# :
|
|
101
|
+
# simplecov:enable
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
104
|
end
|
data/lib/foobara/util/array.rb
CHANGED
data/lib/foobara/util/hash.rb
CHANGED
|
@@ -2,18 +2,63 @@ module Foobara
|
|
|
2
2
|
module Util
|
|
3
3
|
module_function
|
|
4
4
|
|
|
5
|
+
def sort_by_keys(hash)
|
|
6
|
+
keys = hash.keys
|
|
7
|
+
|
|
8
|
+
keys.sort!
|
|
9
|
+
|
|
10
|
+
sorted_hash = {}
|
|
11
|
+
|
|
12
|
+
keys.each do |key|
|
|
13
|
+
sorted_hash[key] = hash[key]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
sorted_hash
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def sort_by_keys!(hash)
|
|
20
|
+
last_key = keys_to_move = nil
|
|
21
|
+
|
|
22
|
+
hash.each_key do |key|
|
|
23
|
+
if last_key
|
|
24
|
+
if key < last_key
|
|
25
|
+
if keys_to_move
|
|
26
|
+
keys_to_move << key
|
|
27
|
+
else
|
|
28
|
+
keys_to_move = [key]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
next
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
last_key = key
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
return hash unless keys_to_move
|
|
39
|
+
|
|
40
|
+
keys_to_move.sort!
|
|
41
|
+
|
|
42
|
+
keys_to_move.each do |key|
|
|
43
|
+
value = hash.delete(key)
|
|
44
|
+
hash[key] = value
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
hash
|
|
48
|
+
end
|
|
49
|
+
|
|
5
50
|
def symbolize_keys(hash)
|
|
6
51
|
unless all_symbolizable_keys?(hash)
|
|
7
|
-
# :
|
|
52
|
+
# simplecov:disable
|
|
8
53
|
raise "Cannot symbolize keys for #{hash} because they are not all symbolizable"
|
|
9
|
-
# :
|
|
54
|
+
# simplecov:enable
|
|
10
55
|
end
|
|
11
56
|
|
|
12
57
|
unless hash.instance_of?(::Hash)
|
|
13
58
|
unless hash.respond_to?(:to_h)
|
|
14
|
-
# :
|
|
59
|
+
# simplecov:disable
|
|
15
60
|
raise ArgumentError, "Could not turn #{hash} into an instance of Hash"
|
|
16
|
-
# :
|
|
61
|
+
# simplecov:enable
|
|
17
62
|
end
|
|
18
63
|
|
|
19
64
|
hash = hash.to_h
|
|
@@ -24,17 +69,21 @@ module Foobara
|
|
|
24
69
|
|
|
25
70
|
def symbolize_keys!(hash)
|
|
26
71
|
unless all_symbolizable_keys?(hash)
|
|
27
|
-
# :
|
|
72
|
+
# simplecov:disable
|
|
28
73
|
raise "Cannot symbolize keys for #{hash} because they are not all symbolizable"
|
|
29
|
-
# :
|
|
74
|
+
# simplecov:enable
|
|
30
75
|
end
|
|
31
76
|
|
|
32
77
|
hash.transform_keys!(&:to_sym)
|
|
33
78
|
|
|
34
79
|
unless hash.instance_of?(::Hash)
|
|
80
|
+
# simplecov:disable
|
|
35
81
|
unless all_symbolic_keys?(hash)
|
|
36
|
-
|
|
82
|
+
# This can happen with HashWithIndifferentAccess
|
|
83
|
+
raise ArgumentError,
|
|
84
|
+
"Cannot symbolize keys for #{hash} because its transform_keys! method does not behave as expected"
|
|
37
85
|
end
|
|
86
|
+
# simplecov:enable
|
|
38
87
|
end
|
|
39
88
|
|
|
40
89
|
hash
|
data/lib/foobara/util/meta.rb
CHANGED
|
@@ -39,12 +39,11 @@ module Foobara
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
unless already_exists
|
|
42
|
-
# rubocop:disable Security/Eval, Style/DocumentDynamicEvalDefinition
|
|
42
|
+
# rubocop:disable-next Security/Eval, Style/DocumentDynamicEvalDefinition
|
|
43
43
|
eval(<<~RUBY, binding, __FILE__, __LINE__ + 1)
|
|
44
44
|
#{which} ::#{name}#{inherit}
|
|
45
45
|
end
|
|
46
46
|
RUBY
|
|
47
|
-
# rubocop:enable Security/Eval, Style/DocumentDynamicEvalDefinition
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
klass = Object.const_get(name, false)
|
data/lib/foobara/util/module.rb
CHANGED
|
@@ -37,9 +37,9 @@ module Foobara
|
|
|
37
37
|
|
|
38
38
|
def constant_values(mod, is_a: nil, extends: nil, inherit: false)
|
|
39
39
|
if inherit && !mod.is_a?(Class)
|
|
40
|
-
# :
|
|
40
|
+
# simplecov:disable
|
|
41
41
|
raise "Cannot pass inherit: true for something that is not a Class"
|
|
42
|
-
# :
|
|
42
|
+
# simplecov:enable
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
if inherit
|
|
@@ -71,9 +71,9 @@ module Foobara
|
|
|
71
71
|
mod.const_get(name)
|
|
72
72
|
rescue NameError => e
|
|
73
73
|
if mod == Object || e.message !~ /uninitialized constant (.*::)?#{name}\z/
|
|
74
|
-
# :
|
|
74
|
+
# simplecov:disable
|
|
75
75
|
raise
|
|
76
|
-
# :
|
|
76
|
+
# simplecov:enable
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
mod = if mod.name&.include?("::")
|
|
@@ -19,7 +19,7 @@ module Foobara
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def referencing_paths(object)
|
|
22
|
-
paths = _referencing_paths(object.
|
|
22
|
+
paths = _referencing_paths(object.__id__, object_id_referenced_by_map)
|
|
23
23
|
paths.sort_by!(&:size)
|
|
24
24
|
paths.reverse!
|
|
25
25
|
paths
|
|
@@ -56,9 +56,9 @@ module Foobara
|
|
|
56
56
|
when :ivar
|
|
57
57
|
via
|
|
58
58
|
else
|
|
59
|
-
# :
|
|
59
|
+
# simplecov:disable
|
|
60
60
|
raise "Unexpected via_type #{part.via_type}"
|
|
61
|
-
# :
|
|
61
|
+
# simplecov:enable
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
class_part = if referenced_by.is_a?(Module)
|
|
@@ -67,12 +67,12 @@ module Foobara
|
|
|
67
67
|
if name
|
|
68
68
|
name
|
|
69
69
|
elsif referenced_by.is_a?(Class)
|
|
70
|
-
"AnonClass:#{referenced_by.
|
|
70
|
+
"AnonClass:#{referenced_by.__id__}"
|
|
71
71
|
else
|
|
72
|
-
"AnonModule:#{referenced_by.
|
|
72
|
+
"AnonModule:#{referenced_by.__id__}"
|
|
73
73
|
end
|
|
74
74
|
else
|
|
75
|
-
"<#{referenced_by.class}:#{referenced_by.
|
|
75
|
+
"<#{referenced_by.class}:#{referenced_by.__id__}>"
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
resolved_parts << "#{class_part}#{via}"
|
|
@@ -119,9 +119,7 @@ module Foobara
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
objects.each do |object|
|
|
122
|
-
|
|
123
|
-
references[object.object_id] = object_id_references(object)
|
|
124
|
-
# rubocop:enable Lint/HashCompareByIdentity
|
|
122
|
+
references[object.__id__] = object_id_references(object)
|
|
125
123
|
end
|
|
126
124
|
|
|
127
125
|
references
|
|
@@ -143,12 +141,12 @@ module Foobara
|
|
|
143
141
|
end
|
|
144
142
|
|
|
145
143
|
def object_id_references(object)
|
|
146
|
-
object_id = object.
|
|
144
|
+
object_id = object.__id__
|
|
147
145
|
|
|
148
146
|
references = object.instance_variables.map do |ivar|
|
|
149
147
|
ReferencePathPart.new(
|
|
150
148
|
object_id,
|
|
151
|
-
object.instance_variable_get(ivar).
|
|
149
|
+
object.instance_variable_get(ivar).__id__,
|
|
152
150
|
ivar,
|
|
153
151
|
:ivar
|
|
154
152
|
)
|
|
@@ -159,7 +157,7 @@ module Foobara
|
|
|
159
157
|
object.constants(false).each do |constant_name|
|
|
160
158
|
references << ReferencePathPart.new(
|
|
161
159
|
object_id,
|
|
162
|
-
object.const_get(constant_name).
|
|
160
|
+
object.const_get(constant_name).__id__,
|
|
163
161
|
constant_name,
|
|
164
162
|
:constant
|
|
165
163
|
)
|
|
@@ -169,11 +167,10 @@ module Foobara
|
|
|
169
167
|
nil
|
|
170
168
|
end
|
|
171
169
|
when ::Array, ::Set
|
|
172
|
-
|
|
173
170
|
object.each.with_index do |element, index|
|
|
174
171
|
references << ReferencePathPart.new(
|
|
175
172
|
object_id,
|
|
176
|
-
element.
|
|
173
|
+
element.__id__,
|
|
177
174
|
index,
|
|
178
175
|
:array_index
|
|
179
176
|
)
|
|
@@ -181,8 +178,8 @@ module Foobara
|
|
|
181
178
|
|
|
182
179
|
when ::Hash
|
|
183
180
|
object.each_pair do |k, v|
|
|
184
|
-
references << ReferencePathPart.new(object_id, k.
|
|
185
|
-
references << ReferencePathPart.new(object_id, v.
|
|
181
|
+
references << ReferencePathPart.new(object_id, k.__id__, k, :hash_key)
|
|
182
|
+
references << ReferencePathPart.new(object_id, v.__id__, k, :hash_value)
|
|
186
183
|
end
|
|
187
184
|
end
|
|
188
185
|
|
data/lib/foobara/util/require.rb
CHANGED
|
@@ -10,9 +10,9 @@ module Foobara
|
|
|
10
10
|
files = Dir[glob]
|
|
11
11
|
|
|
12
12
|
if files.empty?
|
|
13
|
-
# :
|
|
13
|
+
# simplecov:disable
|
|
14
14
|
raise "Didn't find anything to require for #{glob}"
|
|
15
|
-
# :
|
|
15
|
+
# simplecov:enable
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
files.sort_by { |file| [file.count("/"), file.length, file] }.reverse.each do |f|
|
data/lib/foobara/util/string.rb
CHANGED
|
@@ -7,7 +7,12 @@ module Foobara
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def camelize(string, upcase_first = false)
|
|
10
|
-
|
|
10
|
+
if string.nil?
|
|
11
|
+
# simplecov:disable
|
|
12
|
+
warn "Passing nil to .#{__callee__} is deprecated. Pass a string only."
|
|
13
|
+
return nil
|
|
14
|
+
# simplecov:enable
|
|
15
|
+
end
|
|
11
16
|
|
|
12
17
|
if string.is_a?(::Symbol)
|
|
13
18
|
string = string.to_s
|
|
@@ -33,7 +38,12 @@ module Foobara
|
|
|
33
38
|
IS_IDENTIFIER_CHARACTER = /\w/
|
|
34
39
|
|
|
35
40
|
def constantify(string)
|
|
36
|
-
|
|
41
|
+
if string.nil?
|
|
42
|
+
# simplecov:disable
|
|
43
|
+
warn "Passing nil to .#{__callee__} is deprecated. Pass a string only."
|
|
44
|
+
return nil
|
|
45
|
+
# simplecov:enable
|
|
46
|
+
end
|
|
37
47
|
|
|
38
48
|
if string.is_a?(::Symbol)
|
|
39
49
|
string = string.to_s
|
|
@@ -47,11 +57,23 @@ module Foobara
|
|
|
47
57
|
end
|
|
48
58
|
|
|
49
59
|
def constantify_sym(string)
|
|
50
|
-
|
|
60
|
+
if string.nil?
|
|
61
|
+
# simplecov:disable
|
|
62
|
+
warn "Passing nil to .#{__callee__} is deprecated. Pass a string only."
|
|
63
|
+
return nil
|
|
64
|
+
# simplecov:enable
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
constantify(string).to_sym
|
|
51
68
|
end
|
|
52
69
|
|
|
53
70
|
def underscore(string)
|
|
54
|
-
|
|
71
|
+
if string.nil?
|
|
72
|
+
# simplecov:disable
|
|
73
|
+
warn "Passing nil to .#{__callee__} is deprecated. Pass a string only."
|
|
74
|
+
return nil
|
|
75
|
+
# simplecov:enable
|
|
76
|
+
end
|
|
55
77
|
|
|
56
78
|
if string.is_a?(::Symbol)
|
|
57
79
|
string = string.to_s
|
|
@@ -86,11 +108,25 @@ module Foobara
|
|
|
86
108
|
end
|
|
87
109
|
|
|
88
110
|
def kebab_case(string)
|
|
89
|
-
|
|
111
|
+
if string.nil?
|
|
112
|
+
# simplecov:disable
|
|
113
|
+
warn "Passing nil to .#{__callee__} is deprecated. Pass a string only."
|
|
114
|
+
return nil
|
|
115
|
+
# simplecov:enable
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
underscore(string).gsub("_", "-")
|
|
90
119
|
end
|
|
91
120
|
|
|
92
121
|
def underscore_sym(string)
|
|
93
|
-
|
|
122
|
+
if string.nil?
|
|
123
|
+
# simplecov:disable
|
|
124
|
+
warn "Passing nil to .#{__callee__} is deprecated. Pass a string only."
|
|
125
|
+
return nil
|
|
126
|
+
# simplecov:enable
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
underscore(string).to_sym
|
|
94
130
|
end
|
|
95
131
|
|
|
96
132
|
def to_sentence(strings, connector = ", ", last_connector = ", and ")
|
|
@@ -110,7 +146,12 @@ module Foobara
|
|
|
110
146
|
end
|
|
111
147
|
|
|
112
148
|
def humanize(string)
|
|
113
|
-
|
|
149
|
+
if string.nil?
|
|
150
|
+
# simplecov:disable
|
|
151
|
+
warn "Passing nil to .#{__callee__} is deprecated. Pass a string only."
|
|
152
|
+
return nil
|
|
153
|
+
# simplecov:enable
|
|
154
|
+
end
|
|
114
155
|
|
|
115
156
|
if string.is_a?(::Symbol)
|
|
116
157
|
string = string.to_s
|
data/lib/foobara/util.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foobara-util
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Miles Georgi
|
|
@@ -57,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
57
57
|
- !ruby/object:Gem::Version
|
|
58
58
|
version: '0'
|
|
59
59
|
requirements: []
|
|
60
|
-
rubygems_version:
|
|
60
|
+
rubygems_version: 4.0.20
|
|
61
61
|
specification_version: 4
|
|
62
62
|
summary: Utility functions used across various Foobara projects
|
|
63
63
|
test_files: []
|