oj 2.10.4 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of oj might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30d74e96259d1e3ef85ebc6c587f0f0b1f2d155a
4
- data.tar.gz: 0471ab90473ecbd13760d4942edf3461fdf9403d
3
+ metadata.gz: b4acaa9204b28018f0acc3ba755aa319759807b0
4
+ data.tar.gz: 50e58cf7d4ac2265a74f234a6d8d9bff6b45b00d
5
5
  SHA512:
6
- metadata.gz: 04e8cae819f240f2449e19d5673c040f84e1bfb6ea6820126a862f0b1e930437542400c7af5ec4cc0472cd17ed1b5904565c8dd20eee8f69762b05407fb6e6ef
7
- data.tar.gz: 7666bec8a2682042e317dc4d8ca36200a06800e59a667f71ce1b1eb34eb855481f863d0f867111290d655a5c41ae68d93e626afcadcb95dc29a736708c1f8d20
6
+ metadata.gz: b81caf19b41d35592c690f9e1c8411681a9ff0236eed9db198fdcc93e6a3b5463f5ef437849944b99808742f3ce91339d748a38bb687083e22f71bc1a658f788
7
+ data.tar.gz: ba0df930d57755e5bbe0524ed88ff43542ef4a0177454fd139aa38cfcdf1801e63f73358e8f94d4e69ef6a55f972b3f162714c97212da734557afcec2080e914
data/README.md CHANGED
@@ -26,23 +26,30 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
26
26
 
27
27
  [![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj)
28
28
 
29
- ### Current Release 2.10.4
29
+ ### Current Release 2.11.0
30
30
 
31
- - Fixed Range encoding in compat mode to not use the object mode encoding.
31
+ - Restricted strict dump to not dump NaN nor Infinity but instead raise an exception.
32
32
 
33
- - Fixed serialization problem with timestamps.
33
+ - Changed compat mode so that the :bigdecimal_as_decimal option over-rides the
34
+ to_json method if the option is true. The default for mimic_JSON is to leave
35
+ the option off.
34
36
 
35
- - Fixed compat parser to accept NaN and Infinity.
36
-
37
- ### Release 2.10.3
37
+ - Added support for Module encoding in compat mode.
38
+
39
+ - Added ActiveSupportHelper so that require 'active_support_helper' will added
40
+ a helper for serializing ActiveSupport::TimeWithZone.
38
41
 
39
- - Using the xmlschema option with :object mode now saves time as a string and
40
- preserves the timezone.
42
+ - Added float_precision option to control the number of digits used for floats
43
+ when dumping.
44
+
45
+ ### Release 2.10.4
41
46
 
42
- - Rational recursive loop caused by active support fixed.
47
+ - Fixed Range encoding in compat mode to not use the object mode encoding.
43
48
 
44
- - Time in mimic_JSON mode are now the ruby string representation of a date.
49
+ - Fixed serialization problem with timestamps.
45
50
 
51
+ - Fixed compat parser to accept NaN and Infinity.
52
+
46
53
  [Older release notes](http://www.ohler.com/dev/oj_misc/release_notes.html).
47
54
 
48
55
  ## Description
@@ -102,7 +109,7 @@ gem 'oj_mimic_json'
102
109
 
103
110
  ## Proper Use
104
111
 
105
- Two settings in Oj are useful for parsing but do expose a vunerability if used from an untrusted source. Symbolized
112
+ Two settings in Oj are useful for parsing but do expose a vulnerability if used from an untrusted source. Symbolized
106
113
  keys can cause memory to be filled since Ruby does not garbage collect Symbols. The same is true for auto
107
114
  defining classes; memory will also be exhausted if too many classes are automatically defined. Auto defining is a useful
108
115
  feature during development and from trusted sources but it allows too many classes to be created in the object load
@@ -457,18 +457,36 @@ dump_float(VALUE obj, Out out) {
457
457
  *b++ = '\0';
458
458
  cnt = 3;
459
459
  } else if (OJ_INFINITY == d) {
460
+ if (StrictMode == out->opts->mode) {
461
+ raise_strict(obj);
462
+ }
460
463
  strcpy(buf, "Infinity");
461
464
  cnt = 8;
462
465
  } else if (-OJ_INFINITY == d) {
466
+ if (StrictMode == out->opts->mode) {
467
+ raise_strict(obj);
468
+ }
463
469
  strcpy(buf, "-Infinity");
464
470
  cnt = 9;
465
471
  } else if (isnan(d)) {
472
+ if (StrictMode == out->opts->mode) {
473
+ raise_strict(obj);
474
+ }
466
475
  strcpy(buf, "NaN");
467
476
  cnt = 3;
468
477
  } else if (d == (double)(long long int)d) {
469
- cnt = sprintf(buf, "%.1f", d); // used sprintf due to bug in snprintf
478
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
479
+ } else if (0 == out->opts->float_prec) {
480
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
481
+
482
+ cnt = RSTRING_LEN(rstr);
483
+ if (sizeof(buf) <= cnt) {
484
+ cnt = sizeof(buf) - 1;
485
+ }
486
+ strncpy(buf, rb_string_value_ptr((VALUE*)&rstr), cnt);
487
+ buf[cnt] = '\0';
470
488
  } else {
471
- cnt = sprintf(buf, "%0.16g", d); // used sprintf due to bug in snprintf
489
+ cnt = snprintf(buf, sizeof(buf), out->opts->float_fmt, d);
472
490
  }
473
491
  if (out->end - out->cur <= (long)cnt) {
474
492
  grow(out, cnt);
@@ -1169,6 +1187,8 @@ dump_data_null(VALUE obj, Out out) {
1169
1187
 
1170
1188
  static void
1171
1189
  dump_data_comp(VALUE obj, int depth, Out out) {
1190
+ VALUE clas = rb_obj_class(obj);
1191
+
1172
1192
  if (rb_respond_to(obj, oj_to_hash_id)) {
1173
1193
  volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
1174
1194
 
@@ -1176,6 +1196,11 @@ dump_data_comp(VALUE obj, int depth, Out out) {
1176
1196
  rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1177
1197
  }
1178
1198
  dump_hash(h, Qundef, depth, out->opts->mode, out);
1199
+
1200
+ } else if (Yes == out->opts->bigdec_as_num && oj_bigdecimal_class == clas) {
1201
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1202
+
1203
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1179
1204
  } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_as_json_id)) {
1180
1205
  volatile VALUE aj = rb_funcall(obj, oj_as_json_id, 0);
1181
1206
 
@@ -1204,8 +1229,6 @@ dump_data_comp(VALUE obj, int depth, Out out) {
1204
1229
  out->cur += len;
1205
1230
  *out->cur = '\0';
1206
1231
  } else {
1207
- VALUE clas = rb_obj_class(obj);
1208
-
1209
1232
  if (rb_cTime == clas) {
1210
1233
  switch (out->opts->time_format) {
1211
1234
  case RubyTime: dump_ruby_time(obj, out); break;
@@ -1216,11 +1239,7 @@ dump_data_comp(VALUE obj, int depth, Out out) {
1216
1239
  } else if (oj_bigdecimal_class == clas) {
1217
1240
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1218
1241
 
1219
- if (Yes == out->opts->bigdec_as_num) {
1220
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1221
- } else {
1222
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1223
- }
1242
+ dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1224
1243
  } else {
1225
1244
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1226
1245
 
@@ -1722,13 +1741,41 @@ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
1722
1741
  }
1723
1742
  size = d2 * out->indent + 1;
1724
1743
  for (idp = odd->attrs; 0 != *idp; idp++) {
1744
+ size_t nlen;
1745
+
1725
1746
  if (out->end - out->cur <= (long)size) {
1726
1747
  grow(out, size);
1727
1748
  }
1728
1749
  name = rb_id2name(*idp);
1729
- v = rb_funcall(obj, *idp, 0);
1750
+ nlen = strlen(name);
1751
+ if (0 == strchr(name, '.')) {
1752
+ v = rb_funcall(obj, *idp, 0);
1753
+ } else {
1754
+ char nbuf[256];
1755
+ char *n2 = nbuf;
1756
+ char *n;
1757
+ char *end;
1758
+ ID i;
1759
+
1760
+ if (sizeof(nbuf) <= nlen) {
1761
+ n2 = strdup(name);
1762
+ }
1763
+ n = n2;
1764
+ v = obj;
1765
+ while (0 != (end = strchr(n, '.'))) {
1766
+ *end = '\0';
1767
+ i = rb_intern(n);
1768
+ v = rb_funcall(v, i, 0);
1769
+ n = end + 1;
1770
+ }
1771
+ i = rb_intern(n);
1772
+ v = rb_funcall(v, i, 0);
1773
+ if (nbuf != n2) {
1774
+ free(n2);
1775
+ }
1776
+ }
1730
1777
  fill_indent(out, d2);
1731
- dump_cstr(name, strlen(name), 0, 0, out);
1778
+ dump_cstr(name, nlen, 0, 0, out);
1732
1779
  *out->cur++ = ':';
1733
1780
  dump_val(v, d2, out);
1734
1781
  if (out->end - out->cur <= 2) {
@@ -1759,6 +1806,7 @@ dump_val(VALUE obj, int depth, Out out) {
1759
1806
  case T_FALSE: dump_false(out); break;
1760
1807
  case T_FIXNUM: dump_fixnum(obj, out); break;
1761
1808
  case T_FLOAT: dump_float(obj, out); break;
1809
+ case T_MODULE:
1762
1810
  case T_CLASS:
1763
1811
  switch (out->opts->mode) {
1764
1812
  case StrictMode: raise_strict(obj); break;
@@ -34,7 +34,7 @@
34
34
 
35
35
  static struct _Odd _odds[4]; // bump up if new initial Odd classes are added
36
36
  static struct _Odd *odds = _odds;
37
- static int odd_cnt = 0;
37
+ static long odd_cnt = 0;
38
38
 
39
39
  static void
40
40
  set_class(Odd odd, const char *classname) {
@@ -114,6 +114,7 @@ static VALUE class_cache_sym;
114
114
  static VALUE compat_sym;
115
115
  static VALUE create_id_sym;
116
116
  static VALUE escape_mode_sym;
117
+ static VALUE float_prec_sym;
117
118
  static VALUE float_sym;
118
119
  static VALUE indent_sym;
119
120
  static VALUE json_sym;
@@ -168,12 +169,14 @@ struct _Options oj_default_options = {
168
169
  AutoDec, // bigdec_load
169
170
  Yes, // to_json
170
171
  No, // nilnil
172
+ Yes, // allow_gc
173
+ Yes, // quirks_mode
171
174
  json_class, // create_id
172
175
  10, // create_id_len
173
176
  9, // sec_prec
174
- Yes, // allow_gc
175
- Yes, // quirks_mode
176
177
  0, // dump_opts
178
+ 15, // float_prec
179
+ "%0.15g", // float_fmt
177
180
  };
178
181
 
179
182
  static VALUE define_mimic_json(int argc, VALUE *argv, VALUE self);
@@ -193,6 +196,7 @@ static VALUE define_mimic_json(int argc, VALUE *argv, VALUE self);
193
196
  * - bigdecimal_load: [:bigdecimal|:float|:auto] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
194
197
  * - create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
195
198
  * - second_precision: [Fixnum|nil] number of digits after the decimal when dumping the seconds portion of time
199
+ * - float_precision: [Fixnum|nil] number of digits of precision when dumping floats, 0 indicates use Ruby
196
200
  * - use_to_json: [true|false|nil] call to_json() methods on dump, default is false
197
201
  * - nilnil: [true|false|nil] if true a nil input to load will return nil and not raise an Exception
198
202
  * - allow_gc: [true|false|nil] allow or prohibit GC during parsing, default is true (allow)
@@ -214,6 +218,7 @@ get_def_opts(VALUE self) {
214
218
  rb_hash_aset(opts, nilnil_sym, (Yes == oj_default_options.nilnil) ? Qtrue : ((No == oj_default_options.nilnil) ? Qfalse : Qnil));
215
219
  rb_hash_aset(opts, allow_gc_sym, (Yes == oj_default_options.allow_gc) ? Qtrue : ((No == oj_default_options.allow_gc) ? Qfalse : Qnil));
216
220
  rb_hash_aset(opts, quirks_mode_sym, (Yes == oj_default_options.quirks_mode) ? Qtrue : ((No == oj_default_options.quirks_mode) ? Qfalse : Qnil));
221
+ rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
217
222
  switch (oj_default_options.mode) {
218
223
  case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
219
224
  case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
@@ -235,7 +240,7 @@ get_def_opts(VALUE self) {
235
240
  default: rb_hash_aset(opts, time_format_sym, unix_sym); break;
236
241
  }
237
242
  switch (oj_default_options.bigdec_load) {
238
- case BigDec: rb_hash_aset(opts, bigdecimal_load_sym, bigdecimal_sym); break;
243
+ case BigDec: rb_hash_aset(opts, bigdecimal_load_sym, bigdecimal_sym);break;
239
244
  case FloatDec: rb_hash_aset(opts, bigdecimal_load_sym, float_sym); break;
240
245
  case AutoDec:
241
246
  default: rb_hash_aset(opts, bigdecimal_load_sym, auto_sym); break;
@@ -274,6 +279,7 @@ get_def_opts(VALUE self) {
274
279
  * :ruby Time.to_s formatted String
275
280
  * @param [String|nil] :create_id create id for json compatible object encoding
276
281
  * @param [Fixnum|nil] :second_precision number of digits after the decimal when dumping the seconds portion of time
282
+ * @param [Fixnum|nil] :float_precision number of digits of precision when dumping floats, 0 indicates use Ruby
277
283
  * @param [true|false|nil] :use_to_json call to_json() methods on dump, default is false
278
284
  * @param [true|false|nil] :nilnil if true a nil input to load will return nil and not raise an Exception
279
285
  * @param [true|false|nil] :allow_gc allow or prohibit GC during parsing, default is true (allow)
@@ -303,6 +309,23 @@ set_def_opts(VALUE self, VALUE opts) {
303
309
  Check_Type(v, T_FIXNUM);
304
310
  oj_default_options.indent = FIX2INT(v);
305
311
  }
312
+ v = rb_hash_aref(opts, float_prec_sym);
313
+ if (Qnil != v) {
314
+ int n;
315
+
316
+ Check_Type(v, T_FIXNUM);
317
+ n = FIX2INT(v);
318
+ if (0 >= n) {
319
+ *oj_default_options.float_fmt = '\0';
320
+ oj_default_options.float_prec = 0;
321
+ } else {
322
+ if (20 < n) {
323
+ n = 20;
324
+ }
325
+ sprintf(oj_default_options.float_fmt, "%%0.%dg", n);
326
+ oj_default_options.float_prec = n;
327
+ }
328
+ }
306
329
  v = rb_hash_aref(opts, sec_prec_sym);
307
330
  if (Qnil != v) {
308
331
  int n;
@@ -440,6 +463,25 @@ oj_parse_options(VALUE ropts, Options copts) {
440
463
  }
441
464
  copts->indent = NUM2INT(v);
442
465
  }
466
+ if (Qnil != (v = rb_hash_lookup(ropts, float_prec_sym))) {
467
+ int n;
468
+
469
+ if (rb_cFixnum != rb_obj_class(v)) {
470
+ rb_raise(rb_eArgError, ":float_precision must be a Fixnum.");
471
+ }
472
+ Check_Type(v, T_FIXNUM);
473
+ n = FIX2INT(v);
474
+ if (0 >= n) {
475
+ *copts->float_fmt = '\0';
476
+ copts->float_prec = 0;
477
+ } else {
478
+ if (20 < n) {
479
+ n = 20;
480
+ }
481
+ sprintf(copts->float_fmt, "%%0.%dg", n);
482
+ copts->float_prec = n;
483
+ }
484
+ }
443
485
  if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
444
486
  int n;
445
487
 
@@ -890,7 +932,7 @@ to_stream(int argc, VALUE *argv, VALUE self) {
890
932
  * normal way. It is not intended as a hook for changing the output of all
891
933
  * classes as it is not optimized for large numbers of classes.
892
934
  *
893
- * @param [Class] clas Class to me made special
935
+ * @param [Class] clas Class to be made special
894
936
  * @param [Object] create_object object to call the create method on
895
937
  * @param [Symbol] create_method method on the clas that will create a new
896
938
  * instance of the clas when given all the member values in the
@@ -1717,16 +1759,18 @@ static struct _Options mimic_object_to_json_options = {
1717
1759
  CompatMode, // mode
1718
1760
  No, // class_cache
1719
1761
  RubyTime, // time_format
1720
- Yes, // bigdec_as_num
1762
+ No, // bigdec_as_num
1721
1763
  AutoDec, // bigdec_load
1722
1764
  No, // to_json
1723
1765
  Yes, // nilnil
1766
+ Yes, // allow_gc
1767
+ Yes, // quirks_mode
1724
1768
  json_class, // create_id
1725
1769
  10, // create_id_len
1726
1770
  9, // sec_prec
1727
- Yes, // allow_gc
1728
- Yes, // quirks_mode
1729
1771
  0, // dump_opts
1772
+ 15, // float_prec
1773
+ "%0.15g", // float_fmt
1730
1774
  };
1731
1775
 
1732
1776
  static VALUE
@@ -2019,6 +2063,7 @@ void Init_oj() {
2019
2063
  compat_sym = ID2SYM(rb_intern("compat")); rb_gc_register_address(&compat_sym);
2020
2064
  create_id_sym = ID2SYM(rb_intern("create_id")); rb_gc_register_address(&create_id_sym);
2021
2065
  escape_mode_sym = ID2SYM(rb_intern("escape_mode")); rb_gc_register_address(&escape_mode_sym);
2066
+ float_prec_sym = ID2SYM(rb_intern("float_precision"));rb_gc_register_address(&float_prec_sym);
2022
2067
  float_sym = ID2SYM(rb_intern("float")); rb_gc_register_address(&float_sym);
2023
2068
  indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&indent_sym);
2024
2069
  json_sym = ID2SYM(rb_intern("json")); rb_gc_register_address(&json_sym);
@@ -138,12 +138,14 @@ typedef struct _Options {
138
138
  char bigdec_load; // BigLoad
139
139
  char to_json; // YesNo
140
140
  char nilnil; // YesNo
141
+ char allow_gc; // allow GC during parse
142
+ char quirks_mode; // allow single JSON values instead of documents
141
143
  const char *create_id; // 0 or string
142
144
  size_t create_id_len; // length of create_id
143
145
  int sec_prec; // second precision when dumping time
144
- char allow_gc; // allow GC during parse
145
- char quirks_mode; // allow single JSON values instead of documents
146
146
  DumpOpts dump_opts;
147
+ char float_prec; // float precision, linked to float_fmt
148
+ char float_fmt[7]; // float format for dumping, if empty use Ruby
147
149
  } *Options;
148
150
 
149
151
  typedef struct _Out {
@@ -0,0 +1,17 @@
1
+
2
+ require 'active_support/time'
3
+
4
+ module Oj
5
+
6
+ #
7
+ class ActiveSupportHelper
8
+
9
+ def self.createTimeWithZone(utc, zone)
10
+ ActiveSupport::TimeWithZone.new(utc - utc.gmt_offset, ActiveSupport::TimeZone[zone])
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ Oj.register_odd(ActiveSupport::TimeWithZone, Oj::ActiveSupportHelper, :createTimeWithZone, :utc, 'time_zone.name')
17
+
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.10.4'
4
+ VERSION = '2.11.0'
5
5
  end
@@ -10,6 +10,11 @@ require 'rails/all'
10
10
  require 'active_model'
11
11
  require 'active_model_serializers'
12
12
  require 'active_support/json'
13
+ require 'active_support/time'
14
+
15
+ require 'oj/active_support_helper'
16
+
17
+ #Oj.mimic_JSON
13
18
 
14
19
  class Category
15
20
  include ActiveModel::Model
@@ -41,7 +46,7 @@ class MimicRails < Minitest::Test
41
46
  end
42
47
 
43
48
  def test_dump_string
44
- Oj.default_options= {:indent => 2} # JSON this will not change anything
49
+ Oj.default_options= {:indent => 2}
45
50
  json = ActiveSupport::JSON.encode([1, true, nil])
46
51
  assert_equal(%{[
47
52
  1,
@@ -52,7 +57,7 @@ class MimicRails < Minitest::Test
52
57
  end
53
58
 
54
59
  def test_dump_rational
55
- Oj.default_options= {:indent => 2} # JSON this will not change anything
60
+ Oj.default_options= {:indent => 2}
56
61
  json = ActiveSupport::JSON.encode([1, true, Rational(1)])
57
62
  assert_equal(%{[
58
63
  1,
@@ -63,7 +68,7 @@ class MimicRails < Minitest::Test
63
68
  end
64
69
 
65
70
  def test_dump_range
66
- Oj.default_options= {:indent => 2} # JSON this will not change anything
71
+ Oj.default_options= {:indent => 2}
67
72
  json = ActiveSupport::JSON.encode([1, true, '01'..'12'])
68
73
  assert_equal(%{[
69
74
  1,
@@ -79,9 +84,22 @@ class MimicRails < Minitest::Test
79
84
  serializer = CategorySerializer.new(category)
80
85
 
81
86
  json = serializer.to_json()
87
+ puts "*** serializer.to_json() #{serializer.to_json()}"
82
88
  json = serializer.as_json()
89
+ puts "*** serializer.as_json() #{serializer.as_json()}"
83
90
  json = JSON.dump(serializer)
91
+ puts "*** JSON.dump(serializer) #{JSON.dump(serializer)}"
84
92
  end
85
93
 
94
+ def test_dump_time
95
+ Oj.default_options= {:indent => 2}
96
+ now = ActiveSupport::TimeZone['America/Chicago'].parse("2014-11-01 13:20:47")
97
+ json = Oj.dump(now, mode: :object, time_format: :xmlschema)
98
+ #puts "*** json: #{json}"
99
+
100
+ oj_dump = Oj.load(json, mode: :object, time_format: :xmlschema)
101
+ #puts "Now: #{now}\n Oj: #{oj_dump}"
102
+ assert_equal("2014-11-01T13:20:47-05:00", oj_dump.xmlschema)
103
+ end
86
104
 
87
105
  end # MimicRails