oj 2.0.4 → 2.0.5

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.

data/README.md CHANGED
@@ -32,15 +32,14 @@ A fast JSON parser and Object marshaller as a Ruby gem.
32
32
 
33
33
  ## <a name="release">Release Notes</a>
34
34
 
35
- ### Release 2.0.4
35
+ ### Release 2.0.5
36
36
 
37
- - Fixed bug related to long class names.
37
+ - DateTimes are now output the same in compat mode for both 1.8.7 and 1.9.3 even though they are implemented differently in each Ruby.
38
38
 
39
- - Change the default for the auto_define option.
39
+ - Objects implemented as data structs can now change the encoding by implemented either to_json(), as_json(), or to_hash().
40
40
 
41
- - Added Oj.strict_load() method that sets the options to public safe options. This should be safe for loading JSON
42
- documents from a public unverified source. It does not eleviate to need for reasonable programming practices of
43
- course. See the section on the <a href="#proper_use">proper use of Oj</a> in a public exposure.
41
+ - Added an option to allow BigDecimals to be dumped as either a string or as a number. There was no agreement on which
42
+ was the best or correct so both are possible with the correct option setting.
44
43
 
45
44
  ## <a name="description">Description</a>
46
45
 
@@ -93,7 +93,7 @@ static void dump_ruby_time(VALUE obj, Out out);
93
93
  static void dump_xml_time(VALUE obj, Out out);
94
94
  static void dump_data_strict(VALUE obj, Out out);
95
95
  static void dump_data_null(VALUE obj, Out out);
96
- static void dump_data_comp(VALUE obj, Out out);
96
+ static void dump_data_comp(VALUE obj, int depth, Out out);
97
97
  static void dump_data_obj(VALUE obj, int depth, Out out);
98
98
  static void dump_obj_comp(VALUE obj, int depth, Out out);
99
99
  static void dump_obj_obj(VALUE obj, int depth, Out out);
@@ -1116,21 +1116,51 @@ dump_data_null(VALUE obj, Out out) {
1116
1116
  }
1117
1117
 
1118
1118
  static void
1119
- dump_data_comp(VALUE obj, Out out) {
1120
- VALUE clas = rb_obj_class(obj);
1119
+ dump_data_comp(VALUE obj, int depth, Out out) {
1120
+ if (rb_respond_to(obj, oj_to_hash_id)) {
1121
+ VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
1122
+
1123
+ if (T_HASH != rb_type(h)) {
1124
+ rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1125
+ }
1126
+ dump_hash(h, depth, out->opts->mode, out);
1127
+ } else if (rb_respond_to(obj, oj_as_json_id)) {
1128
+ dump_val(rb_funcall(obj, oj_as_json_id, 0), depth, out);
1129
+ } else if (rb_respond_to(obj, oj_to_json_id)) {
1130
+ VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
1131
+ const char *s = StringValuePtr(rs);
1132
+ int len = (int)RSTRING_LEN(rs);
1121
1133
 
1122
- if (rb_cTime == clas) {
1123
- switch (out->opts->time_format) {
1124
- case RubyTime: dump_ruby_time(obj, out); break;
1125
- case XmlTime: dump_xml_time(obj, out); break;
1126
- case UnixTime:
1127
- default: dump_time(obj, out); break;
1134
+ if (out->end - out->cur <= len + 1) {
1135
+ grow(out, len);
1128
1136
  }
1137
+ memcpy(out->cur, s, len);
1138
+ out->cur += len;
1139
+ *out->cur = '\0';
1129
1140
  } else {
1130
- VALUE rstr;
1141
+ VALUE clas = rb_obj_class(obj);
1131
1142
 
1132
- rstr = rb_funcall(obj, oj_to_s_id, 0);
1133
- dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1143
+ if (rb_cTime == clas) {
1144
+ switch (out->opts->time_format) {
1145
+ case RubyTime: dump_ruby_time(obj, out); break;
1146
+ case XmlTime: dump_xml_time(obj, out); break;
1147
+ case UnixTime:
1148
+ default: dump_time(obj, out); break;
1149
+ }
1150
+ } else if (oj_bigdecimal_class == clas) {
1151
+ VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1152
+
1153
+ if (Yes == out->opts->bigdec_as_num) {
1154
+ dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1155
+ } else {
1156
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1157
+ }
1158
+ } else {
1159
+ VALUE rstr;
1160
+
1161
+ rstr = rb_funcall(obj, oj_to_s_id, 0);
1162
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1163
+ }
1134
1164
  }
1135
1165
  }
1136
1166
 
@@ -1157,8 +1187,12 @@ dump_data_obj(VALUE obj, int depth, Out out) {
1157
1187
  if (0 == odd) {
1158
1188
  if (oj_bigdecimal_class == clas) {
1159
1189
  VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1160
-
1161
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1190
+
1191
+ if (Yes == out->opts->bigdec_as_num) {
1192
+ dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1193
+ } else {
1194
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1195
+ }
1162
1196
  } else {
1163
1197
  dump_nil(out);
1164
1198
  }
@@ -1184,18 +1218,26 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
1184
1218
  const char *s = StringValuePtr(rs);
1185
1219
  int len = (int)RSTRING_LEN(rs);
1186
1220
 
1187
- if (out->end - out->cur <= len) {
1221
+ if (out->end - out->cur <= len + 1) {
1188
1222
  grow(out, len);
1189
1223
  }
1190
1224
  memcpy(out->cur, s, len);
1191
1225
  out->cur += len;
1226
+ *out->cur = '\0';
1192
1227
  } else {
1193
1228
  VALUE clas = rb_obj_class(obj);
1194
1229
 
1195
1230
  if (oj_bigdecimal_class == clas) {
1196
1231
  VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1197
1232
 
1198
- //dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1233
+ if (Yes == out->opts->bigdec_as_num) {
1234
+ dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1235
+ } else {
1236
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1237
+ }
1238
+ } else if (oj_datetime_class == clas || oj_date_class == clas) {
1239
+ VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1240
+
1199
1241
  dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1200
1242
  } else {
1201
1243
  Odd odd = oj_get_odd(clas);
@@ -1572,7 +1614,7 @@ dump_val(VALUE obj, int depth, Out out) {
1572
1614
  switch (out->opts->mode) {
1573
1615
  case StrictMode: dump_data_strict(obj, out); break;
1574
1616
  case NullMode: dump_data_null(obj, out); break;
1575
- case CompatMode: dump_data_comp(obj, out); break;
1617
+ case CompatMode: dump_data_comp(obj, depth, out);break;
1576
1618
  case ObjectMode:
1577
1619
  default: dump_data_obj(obj, depth, out); break;
1578
1620
  }
@@ -72,8 +72,10 @@ ID oj_utc_offset_id;
72
72
  ID oj_write_id;
73
73
 
74
74
  VALUE oj_bag_class;
75
- VALUE oj_parse_error_class;
76
75
  VALUE oj_bigdecimal_class;
76
+ VALUE oj_date_class;
77
+ VALUE oj_datetime_class;
78
+ VALUE oj_parse_error_class;
77
79
  VALUE oj_stringio_class;
78
80
  VALUE oj_struct_class;
79
81
  VALUE oj_time_class;
@@ -82,6 +84,7 @@ VALUE oj_slash_string;
82
84
 
83
85
  static VALUE ascii_only_sym;
84
86
  static VALUE auto_define_sym;
87
+ static VALUE bigdecimal_as_decimal_sym;
85
88
  static VALUE circular_sym;
86
89
  static VALUE compat_sym;
87
90
  static VALUE create_id_sym;
@@ -127,6 +130,7 @@ struct _Options oj_default_options = {
127
130
  No, // ascii_only
128
131
  ObjectMode, // mode
129
132
  UnixTime, // time_format
133
+ Yes, // bigdec_as_num
130
134
  json_class, // create_id
131
135
  65536, // max_stack
132
136
  9, // sec_prec
@@ -157,6 +161,7 @@ oj_get_odd(VALUE clas) {
157
161
  * - symbol_keys: [true|false|nil] use symbols instead of strings for hash keys
158
162
  * - mode: [:object|:strict|:compat|:null] load and dump modes to use for JSON
159
163
  * - time_format: [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
164
+ * - bigdecimal_as_decimal: [true|false|nil] dump BigDecimal as a decimal number or as a String
160
165
  * - create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
161
166
  * - max_stack: [Fixnum|nil] maximum json size to allocate on the stack, default is 65536
162
167
  * - second_precision: [Fixnum|nil] number of digits after the decimal when dumping the seconds portion of time
@@ -173,6 +178,7 @@ get_def_opts(VALUE self) {
173
178
  rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
174
179
  rb_hash_aset(opts, ascii_only_sym, (Yes == oj_default_options.ascii_only) ? Qtrue : ((No == oj_default_options.ascii_only) ? Qfalse : Qnil));
175
180
  rb_hash_aset(opts, symbol_keys_sym, (Yes == oj_default_options.sym_key) ? Qtrue : ((No == oj_default_options.sym_key) ? Qfalse : Qnil));
181
+ rb_hash_aset(opts, bigdecimal_as_decimal_sym, (Yes == oj_default_options.bigdec_as_num) ? Qtrue : ((No == oj_default_options.bigdec_as_num) ? Qfalse : Qnil));
176
182
  switch (oj_default_options.mode) {
177
183
  case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
178
184
  case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
@@ -200,6 +206,7 @@ get_def_opts(VALUE self) {
200
206
  * @param [true|false|nil] :auto_define automatically define classes if they do not exist
201
207
  * @param [true|false|nil] :symbol_keys convert hash keys to symbols
202
208
  * @param [true|false|nil] :ascii_only encode all high-bit characters as escaped sequences if true
209
+ * @param [true|false|nil] :bigdecimal_as_decimal dump BigDecimal as a decimal number or as a String
203
210
  * @param [:object|:strict|:compat|:null] load and dump mode to use for JSON
204
211
  * :strict raises an exception when a non-supported Object is
205
212
  * encountered. :compat attempts to extract variable values from an
@@ -224,6 +231,7 @@ set_def_opts(VALUE self, VALUE opts) {
224
231
  { auto_define_sym, &oj_default_options.auto_define },
225
232
  { symbol_keys_sym, &oj_default_options.sym_key },
226
233
  { ascii_only_sym, &oj_default_options.ascii_only },
234
+ { bigdecimal_as_decimal_sym, &oj_default_options.bigdec_as_num },
227
235
  { Qnil, 0 }
228
236
  };
229
237
  YesNoOpt o;
@@ -328,6 +336,7 @@ parse_options(VALUE ropts, Options copts) {
328
336
  { auto_define_sym, &copts->auto_define },
329
337
  { symbol_keys_sym, &copts->sym_key },
330
338
  { ascii_only_sym, &copts->ascii_only },
339
+ { bigdecimal_as_decimal_sym, &copts->bigdec_as_num },
331
340
  { Qnil, 0 }
332
341
  };
333
342
  YesNoOpt o;
@@ -1043,14 +1052,17 @@ void Init_oj() {
1043
1052
  oj_write_id = rb_intern("write");
1044
1053
 
1045
1054
  oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
1055
+ oj_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1056
+ oj_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
1057
+ oj_datetime_class = rb_const_get(rb_cObject, rb_intern("DateTime"));
1046
1058
  oj_parse_error_class = rb_const_get_at(Oj, rb_intern("ParseError"));
1059
+ oj_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
1047
1060
  oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
1048
1061
  oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
1049
- oj_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1050
- oj_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
1051
1062
 
1052
1063
  ascii_only_sym = ID2SYM(rb_intern("ascii_only")); rb_gc_register_address(&ascii_only_sym);
1053
1064
  auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
1065
+ bigdecimal_as_decimal_sym = ID2SYM(rb_intern("bigdecimal_as_decimal"));rb_gc_register_address(&bigdecimal_as_decimal_sym);
1054
1066
  circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
1055
1067
  compat_sym = ID2SYM(rb_intern("compat")); rb_gc_register_address(&compat_sym);
1056
1068
  create_id_sym = ID2SYM(rb_intern("create_id")); rb_gc_register_address(&create_id_sym);
@@ -1090,7 +1102,7 @@ void Init_oj() {
1090
1102
  // Date
1091
1103
  odd++;
1092
1104
  idp = odd->attrs;
1093
- odd->clas = rb_const_get(rb_cObject, rb_intern("Date"));
1105
+ odd->clas = oj_date_class;
1094
1106
  odd->create_obj = odd->clas;
1095
1107
  odd->create_op = oj_new_id;
1096
1108
  odd->attr_cnt = 4;
@@ -1102,7 +1114,7 @@ void Init_oj() {
1102
1114
  // DateTime
1103
1115
  odd++;
1104
1116
  idp = odd->attrs;
1105
- odd->clas = rb_const_get(rb_cObject, rb_intern("DateTime"));
1117
+ odd->clas = oj_datetime_class;
1106
1118
  odd->create_obj = odd->clas;
1107
1119
  odd->create_op = oj_new_id;
1108
1120
  odd->attr_cnt = 8;
@@ -108,6 +108,7 @@ typedef struct _Options {
108
108
  char ascii_only; // YesNo
109
109
  char mode; // Mode
110
110
  char time_format; // TimeFormat
111
+ char bigdec_as_num; // YesNo
111
112
  const char *create_id; // 0 or string
112
113
  size_t max_stack; // max size to allocate on the stack
113
114
  int sec_prec; // second precision when dumping time
@@ -166,6 +167,8 @@ extern rb_encoding *oj_utf8_encoding;
166
167
 
167
168
  extern VALUE oj_bag_class;
168
169
  extern VALUE oj_bigdecimal_class;
170
+ extern VALUE oj_date_class;
171
+ extern VALUE oj_datetime_class;
169
172
  extern VALUE oj_doc_class;
170
173
  extern VALUE oj_parse_error_class;
171
174
  extern VALUE oj_stringio_class;
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.0.4'
4
+ VERSION = '2.0.5'
5
5
  end
@@ -117,6 +117,7 @@ class Juice < ::Test::Unit::TestCase
117
117
  :ascii_only=>false,
118
118
  :mode=>:object,
119
119
  :time_format=>:unix,
120
+ :bigdecimal_as_decimal=>true,
120
121
  :max_stack=>65536,
121
122
  :create_id=>'json_class'}, opts)
122
123
  end
@@ -131,6 +132,7 @@ class Juice < ::Test::Unit::TestCase
131
132
  :ascii_only=>false,
132
133
  :mode=>:object,
133
134
  :time_format=>:unix,
135
+ :bigdecimal_as_decimal=>true,
134
136
  :max_stack=>65536,
135
137
  :create_id=>'json_class'}
136
138
  o2 = {
@@ -142,6 +144,7 @@ class Juice < ::Test::Unit::TestCase
142
144
  :ascii_only=>true,
143
145
  :mode=>:compat,
144
146
  :time_format=>:ruby,
147
+ :bigdecimal_as_decimal=>false,
145
148
  :max_stack=>4000,
146
149
  :create_id=>nil}
147
150
  o3 = { :indent => 4 }
@@ -732,14 +735,24 @@ class Juice < ::Test::Unit::TestCase
732
735
  end
733
736
  def test_bigdecimal_compat
734
737
  orig = BigDecimal.new('80.51')
735
- json = Oj.dump(orig, :mode => :compat)
738
+ json = Oj.dump(orig, :mode => :compat, :bigdecimal_as_decimal => false)
736
739
  bg = Oj.load(json, :mode => :compat)
737
740
  assert_equal(orig.to_s, bg)
738
741
  orig = BigDecimal.new('3.14159265358979323846')
739
- json = Oj.dump(orig, :mode => :compat)
742
+ json = Oj.dump(orig, :mode => :compat, :bigdecimal_as_decimal => false)
740
743
  bg = Oj.load(json, :mode => :compat)
741
744
  assert_equal(orig.to_s, bg)
742
745
  end
746
+ def test_bigdecimal_compat_to_json
747
+ orig = BigDecimal.new('80.51')
748
+ BigDecimal.send(:define_method, :to_json) do
749
+ %{"this is big"}
750
+ end
751
+ json = Oj.dump(orig, :mode => :compat)
752
+ bg = Oj.load(json, :mode => :compat)
753
+ assert_equal("this is big", bg)
754
+ BigDecimal.send(:remove_method, :to_json) # cleanup
755
+ end
743
756
  def test_bigdecimal_object
744
757
  mode = Oj.default_options[:mode]
745
758
  Oj.default_options = {:mode => :object}
@@ -800,13 +813,7 @@ class Juice < ::Test::Unit::TestCase
800
813
  x = Oj.load(json, :mode => :compat)
801
814
  # Some Rubies implement Date as data and some as a real Object. Either are
802
815
  # okay for the test.
803
- if x.is_a?(String)
804
- assert_equal(orig.to_s, x)
805
- else # better be a Hash
806
- assert_equal({ "year" => orig.year, "month" => orig.month, "day" => orig.day,
807
- "hour" => orig.hour, "min" => orig.min, "sec" => orig.sec,
808
- "offset" => orig.offset, "start" => orig.start}, x)
809
- end
816
+ assert_equal(orig.to_s, x)
810
817
  end
811
818
  def test_datetime_object
812
819
  dump_and_load(DateTime.new(2012, 6, 19), false)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4
4
+ version: 2.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-11 00:00:00.000000000 Z
12
+ date: 2013-02-16 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! 'The fastest JSON parser and object serializer. '
15
15
  email: peter@ohler.com