oj 3.15.1 → 3.16.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/ext/oj/dump_compat.c +16 -16
- data/ext/oj/mimic_json.c +0 -2
- data/ext/oj/oj.c +31 -7
- data/ext/oj/oj.h +16 -24
- data/ext/oj/parse.c +5 -1
- data/ext/oj/rails.c +0 -1
- data/ext/oj/string_writer.c +0 -1
- data/lib/oj/json.rb +4 -1
- data/lib/oj/version.rb +1 -1
- data/test/json_gem/json_common_interface_test.rb +4 -2
- data/test/json_gem/json_generator_test.rb +1 -1
- data/test/test_compat.rb +19 -1
- data/test/test_strict.rb +10 -0
- data/test/test_various.rb +6 -0
- 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: 380409882636ecb1f8e3e54369f85dc11d7e167db741f5aec15bfadad2180601
|
4
|
+
data.tar.gz: 0c60d6f8a5b7d76904b84d378e41632f67c0e3fa2e61aeacfb680cadf15ef9dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1cbb6d75ab0c495c8d40983970e8fca54aa846eedb4e031368a98874f1db4ca68f160076ac59c230186316dc686923bad86196fa09bf585f501cf11fe814f9bc
|
7
|
+
data.tar.gz: 9d521e53c4e5311eda8abb0f89b12ae2dd3f8007abb02f0b5ff7728311f6d0b64221a493f8df66a7f223dd12c2c2ba6d47b309867f99e2fe206d440d028368a6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 3.16.1 - 2023-09-01
|
4
|
+
|
5
|
+
- Fixed exception type on number parsing. (thank you @jasonpenny)
|
6
|
+
|
7
|
+
## 3.16.0 - 2023-08-16
|
8
|
+
|
9
|
+
- Added the `float_format` option.
|
10
|
+
|
11
|
+
- Expanded the `max_nesting` option to allow integer values as well as
|
12
|
+
the previous boolean (true or nil).
|
13
|
+
|
14
|
+
- Skip nesting tests with Truffle Ruby in the json gem tests.
|
15
|
+
|
3
16
|
## 3.15.1 - 2023-07-30
|
4
17
|
|
5
18
|
- Add protection against some using `require 'oj/json`, an internal file.
|
data/ext/oj/dump_compat.c
CHANGED
@@ -851,13 +851,18 @@ static DumpFunc compat_funcs[] = {
|
|
851
851
|
};
|
852
852
|
|
853
853
|
static void set_state_depth(VALUE state, int depth) {
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
854
|
+
if (0 == rb_const_defined(rb_cObject, rb_intern("JSON"))) {
|
855
|
+
rb_require("oj/json");
|
856
|
+
}
|
857
|
+
{
|
858
|
+
VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
|
859
|
+
VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
|
860
|
+
VALUE generator = rb_const_get(ext, rb_intern("Generator"));
|
861
|
+
VALUE state_class = rb_const_get(generator, rb_intern("State"));
|
858
862
|
|
859
|
-
|
860
|
-
|
863
|
+
if (state_class == rb_obj_class(state)) {
|
864
|
+
rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
|
865
|
+
}
|
861
866
|
}
|
862
867
|
}
|
863
868
|
|
@@ -865,20 +870,15 @@ void oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
|
|
865
870
|
int type = rb_type(obj);
|
866
871
|
|
867
872
|
TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
|
873
|
+
// The max_nesting logic is that an empty Array or Hash is assumed to have
|
874
|
+
// content so the max_nesting should fail but a non-collection value is
|
875
|
+
// okay. That means a check for a collectable value is needed before
|
876
|
+
// raising.
|
868
877
|
if (out->opts->dump_opts.max_depth <= depth) {
|
869
|
-
|
870
|
-
// limit is the depth inclusive. If JSON.generate is called then a
|
871
|
-
// NestingError is expected and the limit is inclusive. Worse than
|
872
|
-
// that there are unit tests for both.
|
873
|
-
if (CALLER_DUMP == out->caller) {
|
878
|
+
if (RUBY_T_ARRAY == type || RUBY_T_HASH == type) {
|
874
879
|
if (0 < out->argc) {
|
875
880
|
set_state_depth(*out->argv, depth);
|
876
881
|
}
|
877
|
-
rb_raise(rb_eArgError, "Too deeply nested.");
|
878
|
-
} else if (out->opts->dump_opts.max_depth < depth) {
|
879
|
-
if (0 < out->argc) {
|
880
|
-
set_state_depth(*out->argv, depth - 1);
|
881
|
-
}
|
882
882
|
raise_json_err("Too deeply nested", "NestingError");
|
883
883
|
}
|
884
884
|
}
|
data/ext/oj/mimic_json.c
CHANGED
@@ -209,7 +209,6 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
|
|
209
209
|
|
210
210
|
oj_out_init(&out);
|
211
211
|
|
212
|
-
out.caller = CALLER_DUMP;
|
213
212
|
copts.escape_mode = JXEsc;
|
214
213
|
copts.mode = CompatMode;
|
215
214
|
|
@@ -368,7 +367,6 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
|
|
368
367
|
oj_out_init(&out);
|
369
368
|
|
370
369
|
out.omit_nil = copts->dump_opts.omit_nil;
|
371
|
-
out.caller = CALLER_GENERATE;
|
372
370
|
// For obj.to_json or generate nan is not allowed but if called from dump
|
373
371
|
// it is.
|
374
372
|
copts->dump_opts.nan_dump = RaiseNan;
|
data/ext/oj/oj.c
CHANGED
@@ -123,6 +123,7 @@ static VALUE escape_mode_sym;
|
|
123
123
|
static VALUE integer_range_sym;
|
124
124
|
static VALUE fast_sym;
|
125
125
|
static VALUE float_prec_sym;
|
126
|
+
static VALUE float_format_sym;
|
126
127
|
static VALUE float_sym;
|
127
128
|
static VALUE huge_sym;
|
128
129
|
static VALUE ignore_sym;
|
@@ -232,7 +233,7 @@ struct _options oj_default_options = {
|
|
232
233
|
NULL, // tail
|
233
234
|
{'\0'}, // err
|
234
235
|
},
|
235
|
-
NULL,
|
236
|
+
NULL,
|
236
237
|
};
|
237
238
|
|
238
239
|
/* Document-method: default_options()
|
@@ -267,6 +268,8 @@ struct _options oj_default_options = {
|
|
267
268
|
*seconds portion of time
|
268
269
|
* - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
|
269
270
|
*indicates use Ruby
|
271
|
+
* - *:float_format* [_String_] the C printf format string for printing floats. Default follows
|
272
|
+
* the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
|
270
273
|
* - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
|
271
274
|
* - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
|
272
275
|
* - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
|
@@ -378,6 +381,7 @@ static VALUE get_def_opts(VALUE self) {
|
|
378
381
|
oj_safe_sym,
|
379
382
|
(Yes == oj_default_options.safe) ? Qtrue : ((No == oj_default_options.safe) ? Qfalse : Qnil));
|
380
383
|
rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
|
384
|
+
rb_hash_aset(opts, float_format_sym, rb_str_new_cstr(oj_default_options.float_fmt));
|
381
385
|
rb_hash_aset(opts, cache_str_sym, INT2FIX(oj_default_options.cache_str));
|
382
386
|
rb_hash_aset(
|
383
387
|
opts,
|
@@ -519,6 +523,8 @@ static VALUE get_def_opts(VALUE self) {
|
|
519
523
|
*load.
|
520
524
|
* - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the
|
521
525
|
*seconds portion of time.
|
526
|
+
* - *:float_format* [_String_] the C printf format string for printing floats. Default follows
|
527
|
+
* the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
|
522
528
|
* - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
|
523
529
|
*indicates use Ruby.
|
524
530
|
* - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
|
@@ -617,7 +623,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
617
623
|
if (set_yesno_options(k, v, copts)) {
|
618
624
|
return ST_CONTINUE;
|
619
625
|
}
|
620
|
-
|
621
626
|
if (oj_indent_sym == k) {
|
622
627
|
switch (rb_type(v)) {
|
623
628
|
case T_NIL:
|
@@ -757,7 +762,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
757
762
|
if (Qnil == v) {
|
758
763
|
return ST_CONTINUE;
|
759
764
|
}
|
760
|
-
|
761
765
|
copts->compat_bigdec = (Qtrue == v);
|
762
766
|
} else if (oj_decimal_class_sym == k) {
|
763
767
|
if (rb_cFloat == v) {
|
@@ -949,6 +953,25 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
949
953
|
return ST_CONTINUE;
|
950
954
|
}
|
951
955
|
copts->sym_key = (Qtrue == v) ? Yes : No;
|
956
|
+
|
957
|
+
} else if (oj_max_nesting_sym == k) {
|
958
|
+
if (Qtrue == v) {
|
959
|
+
copts->dump_opts.max_depth = 100;
|
960
|
+
} else if (Qfalse == v || Qnil == v) {
|
961
|
+
copts->dump_opts.max_depth = MAX_DEPTH;
|
962
|
+
} else if (T_FIXNUM == rb_type(v)) {
|
963
|
+
copts->dump_opts.max_depth = NUM2INT(v);
|
964
|
+
if (0 >= copts->dump_opts.max_depth) {
|
965
|
+
copts->dump_opts.max_depth = MAX_DEPTH;
|
966
|
+
}
|
967
|
+
}
|
968
|
+
} else if (float_format_sym == k) {
|
969
|
+
rb_check_type(v, T_STRING);
|
970
|
+
if (6 < (int)RSTRING_LEN(v)) {
|
971
|
+
rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
|
972
|
+
}
|
973
|
+
strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
|
974
|
+
copts->float_fmt[RSTRING_LEN(v)] = '\0';
|
952
975
|
}
|
953
976
|
return ST_CONTINUE;
|
954
977
|
}
|
@@ -957,7 +980,6 @@ void oj_parse_options(VALUE ropts, Options copts) {
|
|
957
980
|
if (T_HASH != rb_type(ropts)) {
|
958
981
|
return;
|
959
982
|
}
|
960
|
-
|
961
983
|
rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
|
962
984
|
oj_parse_opt_match_string(&copts->str_rx, ropts);
|
963
985
|
|
@@ -1296,7 +1318,6 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
|
|
1296
1318
|
|
1297
1319
|
arg.out->omit_nil = copts.dump_opts.omit_nil;
|
1298
1320
|
arg.out->omit_null_byte = copts.dump_opts.omit_null_byte;
|
1299
|
-
arg.out->caller = CALLER_DUMP;
|
1300
1321
|
|
1301
1322
|
return rb_ensure(dump_body, (VALUE)&arg, dump_ensure, (VALUE)&arg);
|
1302
1323
|
}
|
@@ -1308,8 +1329,9 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
|
|
1308
1329
|
* will be called. The mode is set to :compat.
|
1309
1330
|
* - *obj* [_Object_] Object to serialize as an JSON document String
|
1310
1331
|
* - *options* [_Hash_]
|
1311
|
-
* - *:max_nesting* [_boolean_] It true nesting is limited to 100.
|
1312
|
-
*
|
1332
|
+
* - *:max_nesting* [_Fixnum_|_boolean_] It true nesting is limited to 100. If a Fixnum nesting
|
1333
|
+
* is set to the provided value. The option to detect circular references is available but is not
|
1334
|
+
* compatible with the json gem., default is false or unlimited.
|
1313
1335
|
* - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and Infinity will be
|
1314
1336
|
* used as appropriate, default is true.
|
1315
1337
|
* - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true
|
@@ -1929,6 +1951,8 @@ void Init_oj(void) {
|
|
1929
1951
|
rb_gc_register_address(&integer_range_sym);
|
1930
1952
|
fast_sym = ID2SYM(rb_intern("fast"));
|
1931
1953
|
rb_gc_register_address(&fast_sym);
|
1954
|
+
float_format_sym = ID2SYM(rb_intern("float_format"));
|
1955
|
+
rb_gc_register_address(&float_format_sym);
|
1932
1956
|
float_prec_sym = ID2SYM(rb_intern("float_precision"));
|
1933
1957
|
rb_gc_register_address(&float_prec_sym);
|
1934
1958
|
float_sym = ID2SYM(rb_intern("float"));
|
data/ext/oj/oj.h
CHANGED
@@ -103,13 +103,6 @@ typedef enum {
|
|
103
103
|
FILE_IO = 'f',
|
104
104
|
} StreamWriterType;
|
105
105
|
|
106
|
-
typedef enum {
|
107
|
-
CALLER_DUMP = 'd',
|
108
|
-
CALLER_TO_JSON = 't',
|
109
|
-
CALLER_GENERATE = 'g',
|
110
|
-
// Add the fast versions if necessary. Maybe unparse as well if needed.
|
111
|
-
} DumpCaller;
|
112
|
-
|
113
106
|
typedef struct _dumpOpts {
|
114
107
|
bool use;
|
115
108
|
char indent_str[16];
|
@@ -188,23 +181,22 @@ typedef struct _rOptTable {
|
|
188
181
|
} *ROptTable;
|
189
182
|
|
190
183
|
typedef struct _out {
|
191
|
-
char
|
192
|
-
char
|
193
|
-
char
|
194
|
-
char
|
195
|
-
Cache8
|
196
|
-
slot_t
|
197
|
-
int
|
198
|
-
int
|
199
|
-
Options
|
200
|
-
uint32_t
|
201
|
-
bool
|
202
|
-
bool
|
203
|
-
bool
|
204
|
-
int
|
205
|
-
VALUE
|
206
|
-
|
207
|
-
ROptTable ropts;
|
184
|
+
char stack_buffer[4096];
|
185
|
+
char *buf;
|
186
|
+
char *end;
|
187
|
+
char *cur;
|
188
|
+
Cache8 circ_cache;
|
189
|
+
slot_t circ_cnt;
|
190
|
+
int indent;
|
191
|
+
int depth; // used by dump_hash
|
192
|
+
Options opts;
|
193
|
+
uint32_t hash_cnt;
|
194
|
+
bool allocated;
|
195
|
+
bool omit_nil;
|
196
|
+
bool omit_null_byte;
|
197
|
+
int argc;
|
198
|
+
VALUE *argv;
|
199
|
+
ROptTable ropts;
|
208
200
|
} *Out;
|
209
201
|
|
210
202
|
typedef struct _strWriter {
|
data/ext/oj/parse.c
CHANGED
@@ -875,7 +875,11 @@ oj_num_as_value(NumInfo ni) {
|
|
875
875
|
double d = strtod(ni->str, &end);
|
876
876
|
|
877
877
|
if ((long)ni->len != (long)(end - ni->str)) {
|
878
|
-
|
878
|
+
if (Qnil == ni->pi->err_class) {
|
879
|
+
rb_raise(oj_parse_error_class, "Invalid float");
|
880
|
+
} else {
|
881
|
+
rb_raise(ni->pi->err_class, "Invalid float");
|
882
|
+
}
|
879
883
|
}
|
880
884
|
rnum = rb_float_new(d);
|
881
885
|
}
|
data/ext/oj/rails.c
CHANGED
data/ext/oj/string_writer.c
CHANGED
data/lib/oj/json.rb
CHANGED
@@ -2,7 +2,10 @@ require 'ostruct'
|
|
2
2
|
require 'oj/state'
|
3
3
|
|
4
4
|
if defined?(JSON::PRETTY_STATE_PROTOTYPE)
|
5
|
-
|
5
|
+
# There are enough people that try to use both the json gen and oj in mimic
|
6
|
+
# mode so don't display the warning.
|
7
|
+
|
8
|
+
# warn "WARNING: oj/json is a compatability shim used by Oj. Requiring the file explicitly is not recommended."
|
6
9
|
end
|
7
10
|
|
8
11
|
unless defined?(JSON::PRETTY_STATE_PROTOTYPE)
|
data/lib/oj/version.rb
CHANGED
@@ -128,8 +128,10 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
|
|
128
128
|
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
|
129
129
|
assert_equal too_deep, JSON.dump(eval(too_deep))
|
130
130
|
assert_kind_of String, Marshal.dump(eval(too_deep))
|
131
|
-
|
132
|
-
|
131
|
+
if RUBY_ENGINE != 'truffleruby'
|
132
|
+
assert_raise(ArgumentError) { JSON.dump(eval(too_deep), 100) }
|
133
|
+
assert_raise(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
|
134
|
+
end
|
133
135
|
assert_equal too_deep, JSON.dump(eval(too_deep), 101)
|
134
136
|
assert_kind_of String, Marshal.dump(eval(too_deep), 101)
|
135
137
|
output = StringIO.new
|
@@ -352,7 +352,7 @@ class JSONGeneratorTest < Test::Unit::TestCase
|
|
352
352
|
too_deep_ary = eval too_deep
|
353
353
|
assert_raise(JSON::NestingError) { JSON.generate too_deep_ary }
|
354
354
|
assert_raise(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 100 }
|
355
|
-
ok = JSON.generate too_deep_ary, :max_nesting =>
|
355
|
+
ok = JSON.generate too_deep_ary, :max_nesting => 102
|
356
356
|
assert_equal too_deep, ok
|
357
357
|
ok = JSON.generate too_deep_ary, :max_nesting => nil
|
358
358
|
assert_equal too_deep, ok
|
data/test/test_compat.rb
CHANGED
@@ -467,10 +467,28 @@ class CompatJuice < Minitest::Test
|
|
467
467
|
end
|
468
468
|
|
469
469
|
def test_arg_passing
|
470
|
-
json = Oj.to_json(Argy.new(), :max_nesting=> 40)
|
470
|
+
json = Oj.to_json(Argy.new(), :max_nesting => 40)
|
471
471
|
assert_equal(%|{"args":"[{:max_nesting=>40}]"}|, json)
|
472
472
|
end
|
473
473
|
|
474
|
+
def test_max_nesting
|
475
|
+
assert_raises() { Oj.to_json([[[[[]]]]], :max_nesting => 3) }
|
476
|
+
assert_raises() { Oj.dump([[[[[]]]]], :max_nesting => 3, :mode=>:compat) }
|
477
|
+
|
478
|
+
assert_raises() { Oj.to_json([[]], :max_nesting => 1) }
|
479
|
+
assert_equal('[[]]', Oj.to_json([[]], :max_nesting => 2))
|
480
|
+
|
481
|
+
assert_raises() { Oj.dump([[]], :max_nesting => 1, :mode=>:compat) }
|
482
|
+
assert_equal('[[]]', Oj.dump([[]], :max_nesting => 2, :mode=>:compat))
|
483
|
+
|
484
|
+
assert_raises() { Oj.to_json([[3]], :max_nesting => 1) }
|
485
|
+
assert_equal('[[3]]', Oj.to_json([[3]], :max_nesting => 2))
|
486
|
+
|
487
|
+
assert_raises() { Oj.dump([[3]], :max_nesting => 1, :mode=>:compat) }
|
488
|
+
assert_equal('[[3]]', Oj.dump([[3]], :max_nesting => 2, :mode=>:compat))
|
489
|
+
|
490
|
+
end
|
491
|
+
|
474
492
|
def test_bad_unicode
|
475
493
|
assert_raises() { Oj.to_json("\xE4xy") }
|
476
494
|
end
|
data/test/test_strict.rb
CHANGED
@@ -69,6 +69,16 @@ class StrictJuice < Minitest::Test
|
|
69
69
|
dump_and_load(-2.48e100 * 1.0e10, false)
|
70
70
|
end
|
71
71
|
|
72
|
+
def test_invalid_float
|
73
|
+
begin
|
74
|
+
Oj.load("64ecb72d29191067f91ff95b")
|
75
|
+
rescue Oj::ParseError => e
|
76
|
+
assert(e.message == "Invalid float")
|
77
|
+
return
|
78
|
+
end
|
79
|
+
assert(false, "*** expected an exception")
|
80
|
+
end
|
81
|
+
|
72
82
|
def test_nan_dump
|
73
83
|
assert_equal('null', Oj.dump(0/0.0, :nan => :null))
|
74
84
|
assert_equal('3.3e14159265358979323846', Oj.dump(0/0.0, :nan => :huge))
|
data/test/test_various.rb
CHANGED
@@ -105,6 +105,7 @@ class Juice < Minitest::Test
|
|
105
105
|
allow_gc: false,
|
106
106
|
quirks_mode: false,
|
107
107
|
allow_invalid_unicode: true,
|
108
|
+
float_format: '%0.13g',
|
108
109
|
float_precision: 13,
|
109
110
|
mode: :strict,
|
110
111
|
escape_mode: :ascii,
|
@@ -416,6 +417,11 @@ class Juice < Minitest::Test
|
|
416
417
|
}
|
417
418
|
end
|
418
419
|
|
420
|
+
def test_dump_float
|
421
|
+
json = Oj.dump(1.23e-2, :mode => :null, :float_format => '%0.4f')
|
422
|
+
assert_equal('0.0123', json)
|
423
|
+
end
|
424
|
+
|
419
425
|
# Class
|
420
426
|
def test_class_null
|
421
427
|
json = Oj.dump(Juice, :mode => :null)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.16.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|