oj 3.10.15 → 3.11.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80fcb3a59fb873da369d49ad3962960b159c0de18580de24da0cbc65935e7275
4
- data.tar.gz: ddcd3f0b95a5a6cd9658c86fff9063bf6bbbc034091a894e8c3c8dcbd4a82e3a
3
+ metadata.gz: 698d68ef4c5a05318046700568e3d19c9d5e40644950c76cf02d31490a4bd738
4
+ data.tar.gz: 1cff670f6587f0080108980d339f9c8cff3097bb5e7cf67a2c583e8745385b7b
5
5
  SHA512:
6
- metadata.gz: a59ab0a3872261dde24fb06612cbc90fbe6d8b06800d5304ba82ce789c0cdcff88c22477b268a8e1161595f97d8b09c0dcbc6b7b25ea7a9373590734618952c0
7
- data.tar.gz: 7a547ff4e19e3ca256762fa5c75598e1aa5cb5bd1a15986a5258b9bdea24be6d4f77d34bfc0d142a061960d30d4b69238bb38b21ebd50095086a99e378eb37e5
6
+ metadata.gz: 3cfb98a97ead48a89f95af28dc364633199afb5cbe269ba07b90a26eabe77d9e59c1051731e07ff3821233bdf10d54896c2e9821786dfee2140beaac9dc8c0d2
7
+ data.tar.gz: 79173558b0cc6e4cb9852c32e7949d88396ffbb8f34cc7781f6144ade5c7ecf5cf8b24a374056ee50e8367d96ba79510cf8ba21a19acd8e52a61c8c731b44921
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # [![{}j](http://www.ohler.com/dev/images/oj_comet_64.svg)](http://www.ohler.com/oj) gem
2
2
 
3
- [![Build Status](https://img.shields.io/travis/ohler55/oj/master.svg?logo=travis)](http://travis-ci.org/ohler55/oj?branch=master) [![AppVeyor](https://img.shields.io/appveyor/ci/ohler55/oj/master.svg?logo=appveyor)](https://ci.appveyor.com/project/ohler55/oj) ![Gem](https://img.shields.io/gem/v/oj.svg) ![Gem](https://img.shields.io/gem/dt/oj.svg) [![SemVer compatibility](https://api.dependabot.com/badges/compatibility_score?dependency-name=oj&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=oj&package-manager=bundler&version-scheme=semver) [![TideLift](https://tidelift.com/badges/github/ohler55/oj)](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme)
3
+ [![Build Status](https://img.shields.io/travis/ohler55/oj/master.svg?logo=travis)](http://travis-ci.org/ohler55/oj?branch=master) ![Gem](https://img.shields.io/gem/v/oj.svg) ![Gem](https://img.shields.io/gem/dt/oj.svg) [![SemVer compatibility](https://api.dependabot.com/badges/compatibility_score?dependency-name=oj&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=oj&package-manager=bundler&version-scheme=semver) [![TideLift](https://tidelift.com/badges/github/ohler55/oj)](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme)
4
4
 
5
5
  A *fast* JSON parser and Object marshaller as a Ruby gem.
6
6
 
@@ -174,15 +174,20 @@ hixss_friendly_size(const uint8_t *str, size_t len) {
174
174
  return size - len * (size_t)'0' + check;
175
175
  }
176
176
 
177
- inline static size_t
177
+ inline static long
178
178
  rails_xss_friendly_size(const uint8_t *str, size_t len) {
179
- size_t size = 0;
179
+ long size = 0;
180
180
  size_t i = len;
181
+ uint8_t hi = 0;
181
182
 
182
183
  for (; 0 < i; str++, i--) {
183
184
  size += rails_xss_friendly_chars[*str];
185
+ hi |= *str & 0x80;
184
186
  }
185
- return size - len * (size_t)'0';
187
+ if (0 == hi) {
188
+ return size - len * (size_t)'0';
189
+ }
190
+ return -(size - len * (size_t)'0');
186
191
  }
187
192
 
188
193
  inline static size_t
@@ -249,7 +254,6 @@ dump_hex(uint8_t c, Out out) {
249
254
 
250
255
  static void
251
256
  raise_invalid_unicode(const char *str, int len, int pos) {
252
- char buf[len + 1];
253
257
  char c;
254
258
  char code[32];
255
259
  char *cp = code;
@@ -268,8 +272,7 @@ raise_invalid_unicode(const char *str, int len, int pos) {
268
272
  cp--;
269
273
  *cp++ = ']';
270
274
  *cp = '\0';
271
- strncpy(buf, str, len);
272
- rb_raise(oj_json_generator_error_class, "Invalid Unicode %s at %d in '%s'", code, pos, buf);
275
+ rb_raise(oj_json_generator_error_class, "Invalid Unicode %s at %d", code, pos);
273
276
  }
274
277
 
275
278
  static const char*
@@ -767,6 +770,7 @@ oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
767
770
  size_t size;
768
771
  char *cmap;
769
772
  const char *orig = str;
773
+ bool has_hi = false;
770
774
 
771
775
  switch (out->opts->escape_mode) {
772
776
  case NLEsc:
@@ -785,10 +789,19 @@ oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
785
789
  cmap = hixss_friendly_chars;
786
790
  size = hixss_friendly_size((uint8_t*)str, cnt);
787
791
  break;
788
- case RailsXEsc:
792
+ case RailsXEsc: {
793
+ long sz;
794
+
789
795
  cmap = rails_xss_friendly_chars;
790
- size = rails_xss_friendly_size((uint8_t*)str, cnt);
796
+ sz = rails_xss_friendly_size((uint8_t*)str, cnt);
797
+ if (sz < 0) {
798
+ has_hi = true;
799
+ size = (size_t)-sz;
800
+ } else {
801
+ size = (size_t)sz;
802
+ }
791
803
  break;
804
+ }
792
805
  case RailsEsc:
793
806
  cmap = rails_friendly_chars;
794
807
  size = rails_friendly_size((uint8_t*)str, cnt);
@@ -812,7 +825,7 @@ oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
812
825
  str++;
813
826
  is_sym = 0; // just to make sure
814
827
  }
815
- if (cnt == size) {
828
+ if (cnt == size && !has_hi) {
816
829
  if (is_sym) {
817
830
  *out->cur++ = ':';
818
831
  }
@@ -364,7 +364,6 @@ mimic_generate_core(int argc, VALUE *argv, Options copts) {
364
364
  struct _out out;
365
365
  VALUE rstr;
366
366
 
367
- // TBD
368
367
  memset(buf, 0, sizeof(buf));
369
368
 
370
369
  out.buf = buf;
@@ -510,7 +509,6 @@ mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
510
509
  pi.options.create_ok = No;
511
510
  pi.options.allow_nan = (bang ? Yes : No);
512
511
  pi.options.nilnil = No;
513
- //pi.options.bigdec_load = FloatDec;
514
512
  pi.options.bigdec_load = RubyDec;
515
513
  pi.options.mode = CompatMode;
516
514
  pi.max_depth = 100;
@@ -561,6 +559,9 @@ mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
561
559
  pi.options.array_class = v;
562
560
  }
563
561
  }
562
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_decimal_class_sym)) {
563
+ pi.options.compat_bigdec = (oj_bigdecimal_class == rb_hash_lookup(ropts, oj_decimal_class_sym));
564
+ }
564
565
  v = rb_hash_lookup(ropts, oj_max_nesting_sym);
565
566
  if (Qtrue == v) {
566
567
  pi.max_depth = 100;
@@ -685,6 +686,7 @@ static struct _options mimic_object_to_json_options = {
685
686
  RubyTime, // time_format
686
687
  No, // bigdec_as_num
687
688
  RubyDec, // bigdec_load
689
+ false, // compat_bigdec
688
690
  No, // to_hash
689
691
  No, // to_json
690
692
  No, // as_json
@@ -839,6 +841,7 @@ oj_mimic_json_methods(VALUE json) {
839
841
  }
840
842
  // Pull in the JSON::State mimic file.
841
843
  state_class = rb_const_get_at(generator, rb_intern("State"));
844
+ rb_gc_register_mark_object(state_class);
842
845
 
843
846
  symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
844
847
  }
@@ -88,6 +88,7 @@ VALUE oj_slash_string;
88
88
  VALUE oj_allow_nan_sym;
89
89
  VALUE oj_array_class_sym;
90
90
  VALUE oj_create_additions_sym;
91
+ VALUE oj_decimal_class_sym;
91
92
  VALUE oj_hash_class_sym;
92
93
  VALUE oj_indent_sym;
93
94
  VALUE oj_object_class_sym;
@@ -106,6 +107,7 @@ static VALUE bigdecimal_load_sym;
106
107
  static VALUE bigdecimal_sym;
107
108
  static VALUE circular_sym;
108
109
  static VALUE class_cache_sym;
110
+ static VALUE compat_bigdecimal_sym;
109
111
  static VALUE compat_sym;
110
112
  static VALUE create_id_sym;
111
113
  static VALUE custom_sym;
@@ -167,6 +169,7 @@ struct _options oj_default_options = {
167
169
  UnixTime, // time_format
168
170
  NotSet, // bigdec_as_num
169
171
  AutoDec, // bigdec_load
172
+ false, // compat_bigdec
170
173
  No, // to_hash
171
174
  No, // to_json
172
175
  No, // as_json
@@ -229,6 +232,7 @@ struct _options oj_default_options = {
229
232
  * - *:time_format* [_:unix_|_:unix_zone_|_:xmlschema_|_:ruby_] time format when dumping
230
233
  * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String
231
234
  * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_:fast_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits. :float should be the same as ruby. :fast may require rounding but is must faster.
235
+ * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as a Float when in compat or rails mode.
232
236
  * - *:create_id* [_String_|_nil_] create id for json compatible object encoding, default is 'json_class'
233
237
  * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on load.
234
238
  * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the seconds portion of time
@@ -333,6 +337,7 @@ get_def_opts(VALUE self) {
333
337
  case AutoDec:
334
338
  default: rb_hash_aset(opts, bigdecimal_load_sym, auto_sym); break;
335
339
  }
340
+ rb_hash_aset(opts, compat_bigdecimal_sym, oj_default_options.compat_bigdec ? Qtrue : Qfalse);
336
341
  rb_hash_aset(opts, create_id_sym, (NULL == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
337
342
  rb_hash_aset(opts, oj_space_sym, (0 == oj_default_options.dump_opts.after_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.after_sep));
338
343
  rb_hash_aset(opts, oj_space_before_sym, (0 == oj_default_options.dump_opts.before_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.before_sep));
@@ -378,6 +383,7 @@ get_def_opts(VALUE self) {
378
383
  * - *:escape* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] mode encodes all high-bit characters as escaped sequences if :ascii, :json is standand UTF-8 JSON encoding, :newline is the same as :json but newlines are not escaped, :unicode_xss allows unicode but escapes &, <, and >, and any \u20xx characters along with some others, and :xss_safe escapes &, <, and >, and some others.
379
384
  * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String.
380
385
  * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_nil_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
386
+ * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as a Float in compat mode.
381
387
  * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump mode to use for JSON :strict raises an exception when a non-supported Object is encountered. :compat attempts to extract variable values from an Object using to_json() or to_hash() then it walks the Object's variables if neither is found. The :object mode ignores to_hash() and to_json() methods and encodes variables using code internal to the Oj gem. The :null mode ignores non-supported Objects and replaces them with a null. The :custom mode honors all dump options. The :rails more mimics rails and Active behavior.
382
388
  * - *:time_format* [_:unix_|_:xmlschema_|_:ruby_] time format when dumping in :compat mode :unix decimal number denoting the number of seconds since 1/1/1970, :unix_zone decimal number denoting the number of seconds since 1/1/1970 plus the utc_offset in the exponent, :xmlschema date-time format taken from XML Schema as a String, :ruby Time.to_s formatted String.
383
389
  * - *:create_id* [_String_|_nil_] create id for json compatible object encoding
@@ -581,6 +587,21 @@ oj_parse_options(VALUE ropts, Options copts) {
581
587
  rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
582
588
  }
583
589
  }
590
+ if (Qnil != (v = rb_hash_lookup(ropts, compat_bigdecimal_sym))) {
591
+ copts->compat_bigdec = (Qtrue == v);
592
+ }
593
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_decimal_class_sym)) {
594
+ v = rb_hash_lookup(ropts, oj_decimal_class_sym);
595
+ if (rb_cFloat == v) {
596
+ copts->compat_bigdec = FloatDec;
597
+ } else if (oj_bigdecimal_class == v) {
598
+ copts->compat_bigdec = BigDec;
599
+ } else if (Qnil == v) {
600
+ copts->compat_bigdec = AutoDec;
601
+ } else {
602
+ rb_raise(rb_eArgError, ":decimal_class must be BigDecimal, Float, or nil.");
603
+ }
604
+ }
584
605
  if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, create_id_sym)) {
585
606
  v = rb_hash_lookup(ropts, create_id_sym);
586
607
  if (Qnil == v) {
@@ -1628,13 +1649,21 @@ Init_oj() {
1628
1649
  rb_require("oj/schandler");
1629
1650
 
1630
1651
  oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
1652
+ rb_gc_register_mark_object(oj_bag_class);
1631
1653
  oj_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1654
+ rb_gc_register_mark_object(oj_bigdecimal_class);
1632
1655
  oj_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
1656
+ rb_gc_register_mark_object(oj_date_class);
1633
1657
  oj_datetime_class = rb_const_get(rb_cObject, rb_intern("DateTime"));
1658
+ rb_gc_register_mark_object(oj_datetime_class);
1634
1659
  oj_enumerable_class = rb_const_get(rb_cObject, rb_intern("Enumerable"));
1660
+ rb_gc_register_mark_object(oj_enumerable_class);
1635
1661
  oj_parse_error_class = rb_const_get_at(Oj, rb_intern("ParseError"));
1662
+ rb_gc_register_mark_object(oj_parse_error_class);
1636
1663
  oj_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
1664
+ rb_gc_register_mark_object(oj_stringio_class);
1637
1665
  oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
1666
+ rb_gc_register_mark_object(oj_struct_class);
1638
1667
  oj_json_parser_error_class = rb_eEncodingError; // replaced if mimic is called
1639
1668
  oj_json_generator_error_class = rb_eEncodingError; // replaced if mimic is called
1640
1669
 
@@ -1649,6 +1678,7 @@ Init_oj() {
1649
1678
  bigdecimal_sym = ID2SYM(rb_intern("bigdecimal")); rb_gc_register_address(&bigdecimal_sym);
1650
1679
  circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
1651
1680
  class_cache_sym = ID2SYM(rb_intern("class_cache")); rb_gc_register_address(&class_cache_sym);
1681
+ compat_bigdecimal_sym = ID2SYM(rb_intern("compat_bigdecimal"));rb_gc_register_address(&compat_bigdecimal_sym);
1652
1682
  compat_sym = ID2SYM(rb_intern("compat")); rb_gc_register_address(&compat_sym);
1653
1683
  create_id_sym = ID2SYM(rb_intern("create_id")); rb_gc_register_address(&create_id_sym);
1654
1684
  custom_sym = ID2SYM(rb_intern("custom")); rb_gc_register_address(&custom_sym);
@@ -1674,6 +1704,7 @@ Init_oj() {
1674
1704
  oj_array_nl_sym = ID2SYM(rb_intern("array_nl")); rb_gc_register_address(&oj_array_nl_sym);
1675
1705
  oj_ascii_only_sym = ID2SYM(rb_intern("ascii_only")); rb_gc_register_address(&oj_ascii_only_sym);
1676
1706
  oj_create_additions_sym = ID2SYM(rb_intern("create_additions"));rb_gc_register_address(&oj_create_additions_sym);
1707
+ oj_decimal_class_sym = ID2SYM(rb_intern("decimal_class")); rb_gc_register_address(&oj_decimal_class_sym);
1677
1708
  oj_hash_class_sym = ID2SYM(rb_intern("hash_class")); rb_gc_register_address(&oj_hash_class_sym);
1678
1709
  oj_indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&oj_indent_sym);
1679
1710
  oj_max_nesting_sym = ID2SYM(rb_intern("max_nesting")); rb_gc_register_address(&oj_max_nesting_sym);
@@ -135,6 +135,7 @@ typedef struct _options {
135
135
  char time_format; // TimeFormat
136
136
  char bigdec_as_num; // YesNo
137
137
  char bigdec_load; // BigLoad
138
+ char compat_bigdec; // boolean (0 or 1)
138
139
  char to_hash; // YesNo
139
140
  char to_json; // YesNo
140
141
  char as_json; // YesNo
@@ -310,6 +311,7 @@ extern VALUE oj_array_class_sym;
310
311
  extern VALUE oj_array_nl_sym;
311
312
  extern VALUE oj_ascii_only_sym;
312
313
  extern VALUE oj_create_additions_sym;
314
+ extern VALUE oj_decimal_class_sym;
313
315
  extern VALUE oj_hash_class_sym;
314
316
  extern VALUE oj_indent_sym;
315
317
  extern VALUE oj_max_nesting_sym;
@@ -212,18 +212,6 @@ read_escaped_str(ParseInfo pi, const char *start) {
212
212
  case '"': buf_append(&buf, '"'); break;
213
213
  case '/': buf_append(&buf, '/'); break;
214
214
  case '\\': buf_append(&buf, '\\'); break;
215
- case '\'':
216
- // The json gem claims this is not an error despite the
217
- // ECMA-404 indicating it is not valid.
218
- if (CompatMode == pi->options.mode) {
219
- buf_append(&buf, '\'');
220
- } else {
221
- pi->cur = s;
222
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
223
- buf_cleanup(&buf);
224
- return;
225
- }
226
- break;
227
215
  case 'u':
228
216
  s++;
229
217
  if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
@@ -263,6 +251,12 @@ read_escaped_str(ParseInfo pi, const char *start) {
263
251
  }
264
252
  break;
265
253
  default:
254
+ // The json gem claims this is not an error despite the
255
+ // ECMA-404 indicating it is not valid.
256
+ if (CompatMode == pi->options.mode) {
257
+ buf_append(&buf, *s);
258
+ break;
259
+ }
266
260
  pi->cur = s;
267
261
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
268
262
  buf_cleanup(&buf);
@@ -391,8 +385,13 @@ read_num(ParseInfo pi) {
391
385
  ni.nan = 0;
392
386
  ni.neg = 0;
393
387
  ni.has_exp = 0;
394
- ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
395
- ni.bigdec_load = pi->options.bigdec_load;
388
+ if (CompatMode == pi->options.mode) {
389
+ ni.no_big = !pi->options.compat_bigdec;
390
+ ni.bigdec_load = pi->options.compat_bigdec;
391
+ } else {
392
+ ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
393
+ ni.bigdec_load = pi->options.bigdec_load;
394
+ }
396
395
 
397
396
  if ('-' == *pi->cur) {
398
397
  pi->cur++;
@@ -517,7 +516,11 @@ read_num(ParseInfo pi) {
517
516
  ni.nan = 1;
518
517
  }
519
518
  }
520
- if (BigDec == pi->options.bigdec_load) {
519
+ if (CompatMode == pi->options.mode) {
520
+ if (pi->options.compat_bigdec) {
521
+ ni.big = 1;
522
+ }
523
+ } else if (BigDec == pi->options.bigdec_load) {
521
524
  ni.big = 1;
522
525
  }
523
526
  if (0 == parent) {
@@ -225,17 +225,6 @@ read_escaped_str(ParseInfo pi) {
225
225
  case '"': buf_append(&buf, '"'); break;
226
226
  case '/': buf_append(&buf, '/'); break;
227
227
  case '\\': buf_append(&buf, '\\'); break;
228
- case '\'':
229
- // The json gem claims this is not an error despite the
230
- // ECMA-404 indicating it is not valid.
231
- if (CompatMode == pi->options.mode) {
232
- buf_append(&buf, '\'');
233
- } else {
234
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
235
- buf_cleanup(&buf);
236
- return;
237
- }
238
- break;
239
228
  case 'u':
240
229
  if (0 == (code = read_hex(pi)) && err_has(&pi->err)) {
241
230
  buf_cleanup(&buf);
@@ -273,6 +262,12 @@ read_escaped_str(ParseInfo pi) {
273
262
  }
274
263
  break;
275
264
  default:
265
+ // The json gem claims this is not an error despite the
266
+ // ECMA-404 indicating it is not valid.
267
+ if (CompatMode == pi->options.mode) {
268
+ buf_append(&buf, c);
269
+ break;
270
+ }
276
271
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
277
272
  buf_cleanup(&buf);
278
273
  return;
@@ -405,8 +400,13 @@ read_num(ParseInfo pi) {
405
400
  ni.nan = 0;
406
401
  ni.neg = 0;
407
402
  ni.has_exp = 0;
408
- ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
409
- ni.bigdec_load = pi->options.bigdec_load;
403
+ if (CompatMode == pi->options.mode) {
404
+ ni.no_big = !pi->options.compat_bigdec;
405
+ ni.bigdec_load = pi->options.compat_bigdec;
406
+ } else {
407
+ ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
408
+ ni.bigdec_load = pi->options.bigdec_load;
409
+ }
410
410
 
411
411
  c = reader_get(&pi->rd);
412
412
  if ('-' == c) {
@@ -523,7 +523,11 @@ read_num(ParseInfo pi) {
523
523
  ni.nan = 1;
524
524
  }
525
525
  }
526
- if (BigDec == pi->options.bigdec_load) {
526
+ if (CompatMode == pi->options.mode) {
527
+ if (pi->options.compat_bigdec) {
528
+ ni.big = 1;
529
+ }
530
+ } else if (BigDec == pi->options.bigdec_load) {
527
531
  ni.big = 1;
528
532
  }
529
533
  add_num_value(pi, &ni);
@@ -546,15 +550,24 @@ read_nan(ParseInfo pi) {
546
550
  ni.infinity = 0;
547
551
  ni.nan = 1;
548
552
  ni.neg = 0;
549
- ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
550
- ni.bigdec_load = pi->options.bigdec_load;
553
+ if (CompatMode == pi->options.mode) {
554
+ ni.no_big = !pi->options.compat_bigdec;
555
+ ni.bigdec_load = pi->options.compat_bigdec;
556
+ } else {
557
+ ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
558
+ ni.bigdec_load = pi->options.bigdec_load;
559
+ }
551
560
 
552
561
  if ('a' != reader_get(&pi->rd) ||
553
562
  ('N' != (c = reader_get(&pi->rd)) && 'n' != c)) {
554
563
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
555
564
  return;
556
565
  }
557
- if (BigDec == pi->options.bigdec_load) {
566
+ if (CompatMode == pi->options.mode) {
567
+ if (pi->options.compat_bigdec) {
568
+ ni.big = 1;
569
+ }
570
+ } else if (BigDec == pi->options.bigdec_load) {
558
571
  ni.big = 1;
559
572
  }
560
573
  add_num_value(pi, &ni);
@@ -744,8 +757,15 @@ oj_sparse2(ParseInfo pi) {
744
757
  ni.infinity = 0;
745
758
  ni.nan = 1;
746
759
  ni.neg = 0;
747
- ni.no_big = (FloatDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load);
748
- ni.bigdec_load = pi->options.bigdec_load;
760
+ if (CompatMode == pi->options.mode) {
761
+ ni.no_big = !pi->options.compat_bigdec;
762
+ ni.bigdec_load = pi->options.compat_bigdec;
763
+ } else {
764
+ ni.no_big = (FloatDec == pi->options.bigdec_load ||
765
+ FastDec == pi->options.bigdec_load ||
766
+ RubyDec == pi->options.bigdec_load);
767
+ ni.bigdec_load = pi->options.bigdec_load;
768
+ }
749
769
  add_num_value(pi, &ni);
750
770
  } else {
751
771
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid token");
@@ -8,6 +8,50 @@ end
8
8
 
9
9
  module Oj
10
10
 
11
+ ##
12
+ # Custom mode can be used to emulate the compat mode with some minor
13
+ # differences. These are the options that setup the custom mode to be like
14
+ # the compat mode.
15
+ CUSTOM_MIMIC_JSON_OPTIONS = {
16
+ allow_gc: true,
17
+ allow_invalid_unicode: false,
18
+ allow_nan: false,
19
+ array_class: nil,
20
+ array_nl: nil,
21
+ auto_define: false,
22
+ bigdecimal_as_decimal: false,
23
+ bigdecimal_load: :auto,
24
+ circular: false,
25
+ class_cache: false,
26
+ create_additions: false,
27
+ create_id: "json_class",
28
+ empty_string: false,
29
+ escape_mode: :unicode_xss,
30
+ float_precision: 0,
31
+ hash_class: nil,
32
+ ignore: nil,
33
+ ignore_under: false,
34
+ indent: 0,
35
+ integer_range: nil,
36
+ mode: :custom,
37
+ nan: :raise,
38
+ nilnil: false,
39
+ object_nl: nil,
40
+ omit_nil: false,
41
+ quirks_mode: true,
42
+ safe: false,
43
+ second_precision: 3,
44
+ space: nil,
45
+ space_before: nil,
46
+ symbol_keys: false,
47
+ time_format: :ruby,
48
+ trace: false,
49
+ use_as_json: false,
50
+ use_raw_json: false,
51
+ use_to_hash: false,
52
+ use_to_json: true,
53
+ }
54
+
11
55
  # A bit hack-ish but does the trick. The JSON.dump_default_options is a Hash
12
56
  # but in mimic we use a C struct to store defaults. This class creates a view
13
57
  # onto that struct.
@@ -38,7 +82,7 @@ module Oj
38
82
 
39
83
  jfile = File.join(d, 'json.rb')
40
84
  $LOADED_FEATURES << jfile unless $LOADED_FEATURES.include?(jfile) if File.exist?(jfile)
41
-
85
+
42
86
  Dir.glob(File.join(d, 'json', '**', '*.rb')).each do |file|
43
87
  # allow json/add/xxx to be loaded. User can override with Oj.add_to_json(xxx).
44
88
  $LOADED_FEATURES << file unless $LOADED_FEATURES.include?(file) unless file.include?('add')
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '3.10.15'
4
+ VERSION = '3.11.1'
5
5
  end
@@ -96,6 +96,7 @@ information.
96
96
  | :auto_define | Boolean | | | | | x | x | |
97
97
  | :bigdecimal_as_decimal | Boolean | | | | 3 | x | x | |
98
98
  | :bigdecimal_load | Boolean | | | | | | x | |
99
+ | :compat_bigdecimal | Boolean | | | x | | | x | |
99
100
  | :circular | Boolean | x | x | x | x | x | x | |
100
101
  | :class_cache | Boolean | | | | | x | x | |
101
102
  | :create_additions | Boolean | | | x | x | | x | |
@@ -66,6 +66,18 @@ Determines how to load decimals.
66
66
 
67
67
  - `:auto` the most precise for the number of digits is used.
68
68
 
69
+ This can also be set with `:decimal_class` when used as a load or
70
+ parse option to match the JSON gem. In that case either `Float`,
71
+ `BigDecimal`, or `nil` can be provided.
72
+
73
+ ### :compat_bigdecimal [Boolean]
74
+
75
+ Determines how to load decimals when in `:compat` mode.
76
+
77
+ - `true` convert all decimal numbers to BigDecimal.
78
+
79
+ - `false` convert all decimal numbers to Float.
80
+
69
81
  ### :circular [Boolean]
70
82
 
71
83
  Detect circular references while dumping. In :compat mode raise a
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+ #
1
3
  # Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
2
4
  # required. That can be set in the RUBYOPT environment variable.
3
5
  # export RUBYOPT=-w
@@ -16,6 +18,14 @@ require 'bigdecimal'
16
18
  require 'pp'
17
19
  require 'oj'
18
20
 
21
+
22
+ if defined?(GC.verify_compaction_references) == 'method'
23
+ # This method was added in Ruby 3.0.0. Calling it this way asks the GC to
24
+ # move objects around, helping to find object movement bugs.
25
+ GC.verify_compaction_references(double_heap: true, toward: :empty)
26
+ end
27
+
28
+
19
29
  $ruby = RUBY_DESCRIPTION.split(' ')[0]
20
30
  $ruby = 'ree' if 'ruby' == $ruby && RUBY_DESCRIPTION.include?('Ruby Enterprise Edition')
21
31
 
@@ -136,6 +136,10 @@ EOT
136
136
 
137
137
  def test_pretty_state
138
138
  state = JSON::PRETTY_STATE_PROTOTYPE.dup
139
+ # In come cases in Ruby 3.0 an :escape_slash is included in the state. It
140
+ # seems to occur on travis but not locally.
141
+ actual = state.to_h
142
+ actual.delete(:escape_slash)
139
143
  assert_equal({
140
144
  :allow_nan => false,
141
145
  :array_nl => "\n",
@@ -147,11 +151,15 @@ EOT
147
151
  :object_nl => "\n",
148
152
  :space => " ",
149
153
  :space_before => "",
150
- }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
154
+ }.sort_by { |n,| n.to_s }, actual.sort_by { |n,| n.to_s })
151
155
  end
152
156
 
153
157
  def test_safe_state
154
158
  state = JSON::SAFE_STATE_PROTOTYPE.dup
159
+ # In come cases in Ruby 3.0 an :escape_slash is included in the state. It
160
+ # seems to occur on travis but not locally.
161
+ actual = state.to_h
162
+ actual.delete(:escape_slash)
155
163
  assert_equal({
156
164
  :allow_nan => false,
157
165
  :array_nl => "",
@@ -163,11 +171,15 @@ EOT
163
171
  :object_nl => "",
164
172
  :space => "",
165
173
  :space_before => "",
166
- }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
174
+ }.sort_by { |n,| n.to_s }, actual.sort_by { |n,| n.to_s })
167
175
  end
168
176
 
169
177
  def test_fast_state
170
178
  state = JSON::FAST_STATE_PROTOTYPE.dup
179
+ # In come cases in Ruby 3.0 an :escape_slash is included in the state. It
180
+ # seems to occur on travis but not locally.
181
+ actual = state.to_h
182
+ actual.delete(:escape_slash)
171
183
  assert_equal({
172
184
  :allow_nan => false,
173
185
  :array_nl => "",
@@ -179,7 +191,7 @@ EOT
179
191
  :object_nl => "",
180
192
  :space => "",
181
193
  :space_before => "",
182
- }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
194
+ }.sort_by { |n,| n.to_s }, actual.sort_by { |n,| n.to_s })
183
195
  end
184
196
 
185
197
  def test_allow_nan
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  $: << File.dirname(__FILE__)
2
4
  $oj_dir = File.dirname(File.dirname(File.expand_path(File.dirname(__FILE__))))
3
5
  %w(lib ext).each do |dir|
@@ -12,6 +14,12 @@ if ENV['REAL_JSON_GEM']
12
14
  else
13
15
  require 'oj'
14
16
  Oj.mimic_JSON
17
+
18
+ if defined?(GC.verify_compaction_references) == 'method'
19
+ # This method was added in Ruby 3.0.0. Calling it this way asks the GC to
20
+ # move objects around, helping to find object movement bugs.
21
+ GC.verify_compaction_references(double_heap: true, toward: :empty)
22
+ end
15
23
  end
16
24
 
17
25
  NaN = JSON::NaN if defined?(JSON::NaN)
@@ -178,7 +178,7 @@ class CompatJuice < Minitest::Test
178
178
  assert_equal('"abc"', json)
179
179
  end
180
180
 
181
- def test_time
181
+ def test_time_xml_schema
182
182
  t = Time.xmlschema("2012-01-05T23:58:07.123456000+09:00")
183
183
  #t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
184
184
  json = Oj.dump(t, :mode => :compat)
@@ -236,6 +236,12 @@ class CompatJuice < Minitest::Test
236
236
  assert_equal({"a\nb" => true, "c\td" => false}, obj)
237
237
  end
238
238
 
239
+ def test_invalid_escapes_handled
240
+ json = '{"subtext":"\"404er\” \w \k \3 \a"}'
241
+ obj = Oj.compat_load(json)
242
+ assert_equal({"subtext" => "\"404er” w k 3 a"}, obj)
243
+ end
244
+
239
245
  def test_hash_escaping
240
246
  json = Oj.to_json({'<>' => '<>'}, mode: :compat)
241
247
  assert_equal(json, '{"<>":"<>"}')
@@ -271,12 +277,19 @@ class CompatJuice < Minitest::Test
271
277
  # BigDecimal
272
278
  def test_bigdecimal
273
279
  # BigDecimals are dumped as strings and can not be restored to the
274
- # original value.
280
+ # original value without using an undocumented feature of the JSON gem.
275
281
  json = Oj.dump(BigDecimal('3.14159265358979323846'))
276
282
  # 2.4.0 changes the exponent to lowercase
277
283
  assert_equal('"0.314159265358979323846e1"', json.downcase)
278
284
  end
279
285
 
286
+ def test_decimal_class
287
+ big = BigDecimal('3.14159265358979323846')
288
+ # :decimal_class is the undocumented feature.
289
+ json = Oj.load('3.14159265358979323846', mode: :compat, decimal_class: BigDecimal)
290
+ assert_equal(big, json)
291
+ end
292
+
280
293
  def test_infinity
281
294
  assert_raises(Oj::ParseError) { Oj.load('Infinity', :mode => :strict) }
282
295
  x = Oj.load('Infinity', :mode => :compat)
@@ -284,7 +297,7 @@ class CompatJuice < Minitest::Test
284
297
  end
285
298
 
286
299
  # Time
287
- def test_time
300
+ def test_time_from_time_object
288
301
  t = Time.new(2015, 1, 5, 21, 37, 7.123456, -8 * 3600)
289
302
  expect = '"' + t.to_s + '"'
290
303
  json = Oj.dump(t)
@@ -23,4 +23,13 @@ class RailsJuice < Minitest::Test
23
23
  assert_equal('0.123e3', json.downcase)
24
24
  end
25
25
 
26
+ def test_invalid_encoding
27
+ assert_raises(EncodingError) {
28
+ Oj.dump("\"\xf3j", mode: :rails)
29
+ }
30
+ assert_raises(EncodingError) {
31
+ Oj.dump("\xf3j", mode: :rails)
32
+ }
33
+ end
34
+
26
35
  end
@@ -120,6 +120,7 @@ class Juice < Minitest::Test
120
120
  escape_mode: :ascii,
121
121
  time_format: :unix_zone,
122
122
  bigdecimal_load: :float,
123
+ compat_bigdecimal: true,
123
124
  create_id: 'classy',
124
125
  create_additions: true,
125
126
  space: 'z',
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.10.15
4
+ version: 3.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-16 00:00:00.000000000 Z
11
+ date: 2021-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -79,16 +79,16 @@ extensions:
79
79
  - ext/oj/extconf.rb
80
80
  extra_rdoc_files:
81
81
  - README.md
82
- - pages/Rails.md
83
- - pages/JsonGem.md
84
- - pages/Encoding.md
85
- - pages/WAB.md
86
- - pages/Custom.md
87
82
  - pages/Advanced.md
88
- - pages/Options.md
89
83
  - pages/Compatibility.md
84
+ - pages/Custom.md
85
+ - pages/Encoding.md
86
+ - pages/JsonGem.md
90
87
  - pages/Modes.md
88
+ - pages/Options.md
89
+ - pages/Rails.md
91
90
  - pages/Security.md
91
+ - pages/WAB.md
92
92
  files:
93
93
  - LICENSE
94
94
  - README.md
@@ -265,7 +265,7 @@ metadata:
265
265
  homepage_uri: http://www.ohler.com/oj/
266
266
  source_code_uri: https://github.com/ohler55/oj
267
267
  wiki_uri: https://github.com/ohler55/oj/wiki
268
- post_install_message:
268
+ post_install_message:
269
269
  rdoc_options:
270
270
  - "--title"
271
271
  - Oj
@@ -284,98 +284,98 @@ required_rubygems_version: !ruby/object:Gem::Requirement
284
284
  - !ruby/object:Gem::Version
285
285
  version: '0'
286
286
  requirements: []
287
- rubygems_version: 3.1.2
288
- signing_key:
287
+ rubygems_version: 3.2.3
288
+ signing_key:
289
289
  specification_version: 4
290
290
  summary: A fast JSON parser and serializer.
291
291
  test_files:
292
+ - test/_test_active.rb
293
+ - test/_test_active_mimic.rb
294
+ - test/_test_mimic_rails.rb
295
+ - test/activerecord/result_test.rb
296
+ - test/activesupport4/decoding_test.rb
297
+ - test/activesupport4/encoding_test.rb
298
+ - test/activesupport4/test_helper.rb
299
+ - test/activesupport5/abstract_unit.rb
300
+ - test/activesupport5/decoding_test.rb
301
+ - test/activesupport5/encoding_test.rb
302
+ - test/activesupport5/encoding_test_cases.rb
303
+ - test/activesupport5/test_helper.rb
304
+ - test/activesupport5/time_zone_test_helpers.rb
305
+ - test/activesupport6/abstract_unit.rb
306
+ - test/activesupport6/decoding_test.rb
307
+ - test/activesupport6/encoding_test.rb
308
+ - test/activesupport6/encoding_test_cases.rb
309
+ - test/activesupport6/test_common.rb
310
+ - test/activesupport6/test_helper.rb
311
+ - test/activesupport6/time_zone_test_helpers.rb
312
+ - test/bar.rb
313
+ - test/baz.rb
314
+ - test/files.rb
292
315
  - test/foo.rb
293
- - test/prec.rb
294
- - test/test_integer_range.rb
295
- - test/test_strict.rb
296
- - test/perf_strict.rb
297
- - test/tests.rb
298
- - test/perf_saj.rb
299
- - test/test_compat.rb
300
316
  - test/helper.rb
301
- - test/perf_file.rb
302
- - test/test_scp.rb
303
- - test/perf_compat.rb
304
- - test/json_gem/json_common_interface_test.rb
317
+ - test/isolated/shared.rb
318
+ - test/isolated/test_mimic_after.rb
319
+ - test/isolated/test_mimic_alone.rb
320
+ - test/isolated/test_mimic_as_json.rb
321
+ - test/isolated/test_mimic_before.rb
322
+ - test/isolated/test_mimic_define.rb
323
+ - test/isolated/test_mimic_rails_after.rb
324
+ - test/isolated/test_mimic_rails_before.rb
325
+ - test/isolated/test_mimic_redefine.rb
305
326
  - test/json_gem/json_addition_test.rb
306
- - test/json_gem/json_fixtures_test.rb
327
+ - test/json_gem/json_common_interface_test.rb
328
+ - test/json_gem/json_encoding_test.rb
307
329
  - test/json_gem/json_ext_parser_test.rb
308
- - test/json_gem/json_string_matching_test.rb
309
- - test/json_gem/json_generic_object_test.rb
330
+ - test/json_gem/json_fixtures_test.rb
310
331
  - test/json_gem/json_generator_test.rb
311
- - test/json_gem/test_helper.rb
312
- - test/json_gem/json_encoding_test.rb
332
+ - test/json_gem/json_generic_object_test.rb
313
333
  - test/json_gem/json_parser_test.rb
314
- - test/perf_wab.rb
315
- - test/test_file.rb
316
- - test/test_object.rb
317
- - test/sample.rb
334
+ - test/json_gem/json_string_matching_test.rb
335
+ - test/json_gem/test_helper.rb
336
+ - test/perf.rb
337
+ - test/perf_compat.rb
338
+ - test/perf_fast.rb
339
+ - test/perf_file.rb
318
340
  - test/perf_object.rb
319
- - test/test_hash.rb
320
- - test/test_custom.rb
321
- - test/bar.rb
322
- - test/activesupport4/encoding_test.rb
323
- - test/activesupport4/test_helper.rb
324
- - test/activesupport4/decoding_test.rb
325
- - test/sample_json.rb
326
- - test/activesupport5/encoding_test_cases.rb
327
- - test/activesupport5/encoding_test.rb
328
- - test/activesupport5/abstract_unit.rb
329
- - test/activesupport5/time_zone_test_helpers.rb
330
- - test/activesupport5/test_helper.rb
331
- - test/activesupport5/decoding_test.rb
332
- - test/test_saj.rb
341
+ - test/perf_saj.rb
333
342
  - test/perf_scp.rb
334
- - test/test_wab.rb
335
- - test/test_null.rb
336
- - test/_test_active.rb
337
- - test/_test_mimic_rails.rb
338
- - test/test_fast.rb
339
- - test/perf_fast.rb
343
+ - test/perf_simple.rb
344
+ - test/perf_strict.rb
345
+ - test/perf_wab.rb
346
+ - test/prec.rb
340
347
  - test/sample/change.rb
341
- - test/sample/text.rb
348
+ - test/sample/dir.rb
342
349
  - test/sample/doc.rb
343
- - test/sample/shape.rb
344
- - test/sample/layer.rb
345
- - test/sample/group.rb
346
350
  - test/sample/file.rb
347
- - test/sample/rect.rb
351
+ - test/sample/group.rb
348
352
  - test/sample/hasprops.rb
353
+ - test/sample/layer.rb
349
354
  - test/sample/line.rb
350
- - test/sample/dir.rb
351
355
  - test/sample/oval.rb
352
- - test/tests_mimic.rb
353
- - test/perf_simple.rb
354
- - test/zoo.rb
355
- - test/activerecord/result_test.rb
356
- - test/_test_active_mimic.rb
357
- - test/baz.rb
358
- - test/tests_mimic_addition.rb
359
- - test/test_writer.rb
360
- - test/test_rails.rb
361
- - test/perf.rb
362
- - test/isolated/test_mimic_define.rb
363
- - test/isolated/test_mimic_after.rb
364
- - test/isolated/test_mimic_rails_after.rb
365
- - test/isolated/test_mimic_before.rb
366
- - test/isolated/test_mimic_rails_before.rb
367
- - test/isolated/test_mimic_redefine.rb
368
- - test/isolated/shared.rb
369
- - test/isolated/test_mimic_alone.rb
370
- - test/isolated/test_mimic_as_json.rb
356
+ - test/sample/rect.rb
357
+ - test/sample/shape.rb
358
+ - test/sample/text.rb
359
+ - test/sample.rb
360
+ - test/sample_json.rb
361
+ - test/test_compat.rb
362
+ - test/test_custom.rb
371
363
  - test/test_debian.rb
364
+ - test/test_fast.rb
365
+ - test/test_file.rb
372
366
  - test/test_gc.rb
373
- - test/files.rb
367
+ - test/test_hash.rb
368
+ - test/test_integer_range.rb
369
+ - test/test_null.rb
370
+ - test/test_object.rb
371
+ - test/test_rails.rb
372
+ - test/test_saj.rb
373
+ - test/test_scp.rb
374
+ - test/test_strict.rb
374
375
  - test/test_various.rb
375
- - test/activesupport6/encoding_test_cases.rb
376
- - test/activesupport6/encoding_test.rb
377
- - test/activesupport6/abstract_unit.rb
378
- - test/activesupport6/time_zone_test_helpers.rb
379
- - test/activesupport6/test_helper.rb
380
- - test/activesupport6/test_common.rb
381
- - test/activesupport6/decoding_test.rb
376
+ - test/test_wab.rb
377
+ - test/test_writer.rb
378
+ - test/tests.rb
379
+ - test/tests_mimic.rb
380
+ - test/tests_mimic_addition.rb
381
+ - test/zoo.rb