oj 3.9.0 → 3.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/ext/oj/compat.c +5 -5
- data/ext/oj/custom.c +7 -3
- data/ext/oj/dump.c +9 -12
- data/ext/oj/dump_compat.c +8 -10
- data/ext/oj/dump_object.c +18 -11
- data/ext/oj/dump_strict.c +6 -5
- data/ext/oj/extconf.rb +5 -0
- data/ext/oj/mimic_json.c +15 -3
- data/ext/oj/object.c +6 -2
- data/ext/oj/oj.c +47 -28
- data/ext/oj/oj.h +4 -2
- data/ext/oj/parse.c +22 -3
- data/ext/oj/parse.h +1 -0
- data/ext/oj/rails.c +38 -4
- data/ext/oj/sparse.c +5 -0
- data/ext/oj/util.c +5 -5
- data/ext/oj/wab.c +9 -9
- data/lib/oj/version.rb +1 -1
- data/pages/Rails.md +59 -21
- data/test/activesupport5/abstract_unit.rb +45 -0
- data/test/activesupport5/decoding_test.rb +68 -60
- data/test/activesupport5/encoding_test.rb +111 -96
- data/test/activesupport5/encoding_test_cases.rb +33 -25
- data/test/activesupport5/test_helper.rb +43 -21
- data/test/activesupport5/time_zone_test_helpers.rb +18 -3
- data/test/activesupport6/abstract_unit.rb +44 -0
- data/test/activesupport6/decoding_test.rb +133 -0
- data/test/activesupport6/encoding_test.rb +507 -0
- data/test/activesupport6/encoding_test_cases.rb +98 -0
- data/test/activesupport6/test_common.rb +17 -0
- data/test/activesupport6/test_helper.rb +163 -0
- data/test/activesupport6/time_zone_test_helpers.rb +39 -0
- data/test/bar.rb +8 -11
- data/test/baz.rb +16 -0
- data/test/foo.rb +39 -8
- data/test/test_compat.rb +0 -7
- data/test/test_custom.rb +25 -6
- data/test/test_integer_range.rb +1 -2
- data/test/test_object.rb +12 -3
- data/test/test_rails.rb +26 -0
- data/test/test_strict.rb +24 -1
- data/test/test_various.rb +41 -62
- data/test/tests.rb +1 -0
- metadata +23 -3
data/ext/oj/oj.h
CHANGED
@@ -149,8 +149,10 @@ typedef struct _options {
|
|
149
149
|
char allow_nan; // YEsyNo for parsing only
|
150
150
|
char trace; // YesNo
|
151
151
|
char safe; // YesNo
|
152
|
-
|
153
|
-
|
152
|
+
char sec_prec_set; // boolean (0 or 1)
|
153
|
+
char ignore_under; // YesNo - ignore attrs starting with _ if true in object and custom modes
|
154
|
+
int64_t int_range_min; // dump numbers below as string
|
155
|
+
int64_t int_range_max; // dump numbers above as string
|
154
156
|
const char *create_id; // 0 or string
|
155
157
|
size_t create_id_len; // length of create_id
|
156
158
|
int sec_prec; // second precision when dumping time
|
data/ext/oj/parse.c
CHANGED
@@ -400,6 +400,10 @@ read_num(ParseInfo pi) {
|
|
400
400
|
pi->cur++;
|
401
401
|
ni.neg = 1;
|
402
402
|
} else if ('+' == *pi->cur) {
|
403
|
+
if (StrictMode == pi->options.mode) {
|
404
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
405
|
+
return;
|
406
|
+
}
|
403
407
|
pi->cur++;
|
404
408
|
}
|
405
409
|
if ('I' == *pi->cur) {
|
@@ -446,8 +450,13 @@ read_num(ParseInfo pi) {
|
|
446
450
|
if ('.' == *pi->cur) {
|
447
451
|
pi->cur++;
|
448
452
|
// A trailing . is not a valid decimal but if encountered allow it
|
449
|
-
// except when mimicing the JSON gem.
|
450
|
-
if (CompatMode == pi->options.mode) {
|
453
|
+
// except when mimicing the JSON gem or in strict mode.
|
454
|
+
if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
|
455
|
+
int pos = (int)(pi->cur - ni.str);
|
456
|
+
if (1 == pos || (2 == pos && ni.neg)) {
|
457
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
458
|
+
return;
|
459
|
+
}
|
451
460
|
if (*pi->cur < '0' || '9' < *pi->cur) {
|
452
461
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
453
462
|
return;
|
@@ -731,7 +740,7 @@ oj_parse2(ParseInfo pi) {
|
|
731
740
|
}
|
732
741
|
|
733
742
|
static VALUE
|
734
|
-
rescue_big_decimal(VALUE str) {
|
743
|
+
rescue_big_decimal(VALUE str, VALUE ignore) {
|
735
744
|
rb_raise(oj_parse_error_class, "Invalid value for BigDecimal()");
|
736
745
|
return Qnil;
|
737
746
|
}
|
@@ -1032,6 +1041,12 @@ CLEANUP:
|
|
1032
1041
|
if (pi->str_rx.head != oj_default_options.str_rx.head) {
|
1033
1042
|
oj_rxclass_cleanup(&pi->str_rx);
|
1034
1043
|
}
|
1044
|
+
// TBD if validate only then (Qundef == result ??)
|
1045
|
+
// if pi->err or 0 != line
|
1046
|
+
// rb_get_errinfo();??
|
1047
|
+
// rb_set_errinfo(Qnil);
|
1048
|
+
// return nil or error
|
1049
|
+
|
1035
1050
|
if (err_has(&pi->err)) {
|
1036
1051
|
rb_set_errinfo(Qnil);
|
1037
1052
|
if (Qnil != pi->err_class) {
|
@@ -1049,6 +1064,10 @@ CLEANUP:
|
|
1049
1064
|
msg = rb_str_append(msg, oj_encode(rb_str_new2(pi->json)));
|
1050
1065
|
}
|
1051
1066
|
args[0] = msg;
|
1067
|
+
if (pi->err.clas == oj_parse_error_class) {
|
1068
|
+
// The error was an Oj::ParseError so change to a JSON::ParserError.
|
1069
|
+
pi->err.clas = oj_json_parser_error_class;
|
1070
|
+
}
|
1052
1071
|
rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
|
1053
1072
|
} else {
|
1054
1073
|
oj_err_raise(&pi->err);
|
data/ext/oj/parse.h
CHANGED
@@ -80,6 +80,7 @@ extern VALUE oj_num_as_value(NumInfo ni);
|
|
80
80
|
extern void oj_set_strict_callbacks(ParseInfo pi);
|
81
81
|
extern void oj_set_object_callbacks(ParseInfo pi);
|
82
82
|
extern void oj_set_compat_callbacks(ParseInfo pi);
|
83
|
+
extern void oj_set_custom_callbacks(ParseInfo pi);
|
83
84
|
extern void oj_set_wab_callbacks(ParseInfo pi);
|
84
85
|
|
85
86
|
extern void oj_sparse2(ParseInfo pi);
|
data/ext/oj/rails.c
CHANGED
@@ -87,7 +87,8 @@ copy_opts(ROptTable src, ROptTable dest) {
|
|
87
87
|
}
|
88
88
|
|
89
89
|
static int
|
90
|
-
dump_attr_cb(ID key, VALUE value,
|
90
|
+
dump_attr_cb(ID key, VALUE value, VALUE ov) {
|
91
|
+
Out out = (Out)ov;
|
91
92
|
int depth = out->depth;
|
92
93
|
size_t size = depth * out->indent + 1;
|
93
94
|
const char *attr = rb_id2name(key);
|
@@ -214,6 +215,8 @@ dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
|
|
214
215
|
|
215
216
|
if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
|
216
217
|
oj_dump_nil(Qnil, depth, out, false);
|
218
|
+
} else if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) {
|
219
|
+
oj_dump_cstr(str, (int)RSTRING_LEN(rstr), 0, 0, out);
|
217
220
|
} else if (Yes == out->opts->bigdec_as_num) {
|
218
221
|
oj_dump_raw(str, (int)RSTRING_LEN(rstr), out);
|
219
222
|
} else {
|
@@ -1009,9 +1012,11 @@ rails_encode(int argc, VALUE *argv, VALUE self) {
|
|
1009
1012
|
}
|
1010
1013
|
}
|
1011
1014
|
|
1015
|
+
// TBD provide a get function as well
|
1012
1016
|
static VALUE
|
1013
1017
|
rails_use_standard_json_time_format(VALUE self, VALUE state) {
|
1014
1018
|
if (Qtrue == state || Qfalse == state) {
|
1019
|
+
// no change needed
|
1015
1020
|
} else if (Qnil == state) {
|
1016
1021
|
state = Qfalse;
|
1017
1022
|
} else {
|
@@ -1023,6 +1028,11 @@ rails_use_standard_json_time_format(VALUE self, VALUE state) {
|
|
1023
1028
|
return state;
|
1024
1029
|
}
|
1025
1030
|
|
1031
|
+
static VALUE
|
1032
|
+
rails_use_standard_json_time_format_get(VALUE self) {
|
1033
|
+
return xml_time ? Qtrue : Qfalse;
|
1034
|
+
}
|
1035
|
+
|
1026
1036
|
static VALUE
|
1027
1037
|
rails_escape_html_entities_in_json(VALUE self, VALUE state) {
|
1028
1038
|
rb_iv_set(self, "@escape_html_entities_in_json", state);
|
@@ -1031,10 +1041,16 @@ rails_escape_html_entities_in_json(VALUE self, VALUE state) {
|
|
1031
1041
|
return state;
|
1032
1042
|
}
|
1033
1043
|
|
1044
|
+
static VALUE
|
1045
|
+
rails_escape_html_entities_in_json_get(VALUE self) {
|
1046
|
+
return escape_html ? Qtrue : Qfalse;
|
1047
|
+
}
|
1048
|
+
|
1034
1049
|
static VALUE
|
1035
1050
|
rails_time_precision(VALUE self, VALUE prec) {
|
1036
1051
|
rb_iv_set(self, "@time_precision", prec);
|
1037
1052
|
oj_default_options.sec_prec = NUM2INT(prec);
|
1053
|
+
oj_default_options.sec_prec_set = true;
|
1038
1054
|
|
1039
1055
|
return prec;
|
1040
1056
|
}
|
@@ -1053,7 +1069,12 @@ rails_set_encoder(VALUE self) {
|
|
1053
1069
|
VALUE encoding;
|
1054
1070
|
VALUE pv;
|
1055
1071
|
VALUE verbose;
|
1072
|
+
VALUE enc = resolve_classpath("ActiveSupport::JSON::Encoding");
|
1056
1073
|
|
1074
|
+
if (Qnil != enc) {
|
1075
|
+
escape_html = Qtrue == rb_iv_get(self, "@escape_html_entities_in_json");
|
1076
|
+
xml_time = Qtrue == rb_iv_get(enc, "@use_standard_json_time_format");
|
1077
|
+
}
|
1057
1078
|
if (rb_const_defined_at(rb_cObject, rb_intern("ActiveSupport"))) {
|
1058
1079
|
active = rb_const_get_at(rb_cObject, rb_intern("ActiveSupport"));
|
1059
1080
|
} else {
|
@@ -1070,14 +1091,19 @@ rails_set_encoder(VALUE self) {
|
|
1070
1091
|
rb_gv_set("$VERBOSE", Qfalse);
|
1071
1092
|
rb_undef_method(encoding, "use_standard_json_time_format=");
|
1072
1093
|
rb_define_module_function(encoding, "use_standard_json_time_format=", rails_use_standard_json_time_format, 1);
|
1094
|
+
rb_undef_method(encoding, "use_standard_json_time_format");
|
1095
|
+
rb_define_module_function(encoding, "use_standard_json_time_format", rails_use_standard_json_time_format_get, 0);
|
1073
1096
|
|
1074
1097
|
pv = rb_iv_get(encoding, "@escape_html_entities_in_json");
|
1075
1098
|
escape_html = Qtrue == pv;
|
1076
1099
|
rb_undef_method(encoding, "escape_html_entities_in_json=");
|
1077
1100
|
rb_define_module_function(encoding, "escape_html_entities_in_json=", rails_escape_html_entities_in_json, 1);
|
1101
|
+
rb_undef_method(encoding, "escape_html_entities_in_json");
|
1102
|
+
rb_define_module_function(encoding, "escape_html_entities_in_json", rails_escape_html_entities_in_json_get, 0);
|
1078
1103
|
|
1079
1104
|
pv = rb_iv_get(encoding, "@time_precision");
|
1080
1105
|
oj_default_options.sec_prec = NUM2INT(pv);
|
1106
|
+
oj_default_options.sec_prec_set = true;
|
1081
1107
|
rb_undef_method(encoding, "time_precision=");
|
1082
1108
|
rb_define_module_function(encoding, "time_precision=", rails_time_precision, 1);
|
1083
1109
|
rb_gv_set("$VERBOSE", verbose);
|
@@ -1285,7 +1311,8 @@ dump_array(VALUE a, int depth, Out out, bool as_ok) {
|
|
1285
1311
|
}
|
1286
1312
|
|
1287
1313
|
static int
|
1288
|
-
hash_cb(VALUE key, VALUE value,
|
1314
|
+
hash_cb(VALUE key, VALUE value, VALUE ov) {
|
1315
|
+
Out out = (Out)ov;
|
1289
1316
|
int depth = out->depth;
|
1290
1317
|
long size;
|
1291
1318
|
int rtype = rb_type(key);
|
@@ -1398,14 +1425,17 @@ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1398
1425
|
|
1399
1426
|
static void
|
1400
1427
|
dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
|
1428
|
+
VALUE clas;
|
1429
|
+
|
1401
1430
|
if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
|
1402
1431
|
out->argc = 0;
|
1403
1432
|
return;
|
1404
1433
|
}
|
1434
|
+
clas = rb_obj_class(obj);
|
1405
1435
|
if (as_ok) {
|
1406
1436
|
ROpt ro;
|
1407
1437
|
|
1408
|
-
if (NULL != (ro = oj_rails_get_opt(out->ropts,
|
1438
|
+
if (NULL != (ro = oj_rails_get_opt(out->ropts, clas)) && ro->on) {
|
1409
1439
|
ro->dump(obj, depth, out, as_ok);
|
1410
1440
|
} else if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
|
1411
1441
|
oj_dump_raw_json(obj, depth, out);
|
@@ -1413,6 +1443,8 @@ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1413
1443
|
dump_as_json(obj, depth, out, true);
|
1414
1444
|
} else if (rb_respond_to(obj, oj_to_hash_id)) {
|
1415
1445
|
dump_to_hash(obj, depth, out);
|
1446
|
+
} else if (oj_bigdecimal_class == clas) {
|
1447
|
+
dump_bigdecimal(obj, depth, out, false);
|
1416
1448
|
} else {
|
1417
1449
|
oj_dump_obj_to_s(obj, out);
|
1418
1450
|
}
|
@@ -1421,6 +1453,8 @@ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1421
1453
|
} else if (rb_respond_to(obj, oj_to_hash_id)) {
|
1422
1454
|
// Always attempt to_hash.
|
1423
1455
|
dump_to_hash(obj, depth, out);
|
1456
|
+
} else if (oj_bigdecimal_class == clas) {
|
1457
|
+
dump_bigdecimal(obj, depth, out, false);
|
1424
1458
|
} else {
|
1425
1459
|
oj_dump_obj_to_s(obj, out);
|
1426
1460
|
}
|
@@ -1439,7 +1473,7 @@ static DumpFunc rails_funcs[] = {
|
|
1439
1473
|
dump_hash, // RUBY_T_HASH = 0x08,
|
1440
1474
|
dump_obj, // RUBY_T_STRUCT = 0x09,
|
1441
1475
|
oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
|
1442
|
-
|
1476
|
+
dump_as_string, // RUBY_T_FILE = 0x0b,
|
1443
1477
|
dump_obj, // RUBY_T_DATA = 0x0c,
|
1444
1478
|
NULL, // RUBY_T_MATCH = 0x0d,
|
1445
1479
|
// Rails raises a stack error on Complex and Rational. It also corrupts
|
data/ext/oj/sparse.c
CHANGED
@@ -773,6 +773,7 @@ oj_sparse2(ParseInfo pi) {
|
|
773
773
|
first = 0;
|
774
774
|
}
|
775
775
|
start = pi->rd.pos;
|
776
|
+
// TBD break if option set to allow that
|
776
777
|
}
|
777
778
|
}
|
778
779
|
}
|
@@ -898,6 +899,10 @@ CLEANUP:
|
|
898
899
|
// idea.
|
899
900
|
VALUE args[] = { oj_encode(rb_str_new2(pi->err.msg)) };
|
900
901
|
|
902
|
+
if (pi->err.clas == oj_parse_error_class) {
|
903
|
+
// The error was an Oj::ParseError so change to a JSON::ParserError.
|
904
|
+
pi->err.clas = oj_json_parser_error_class;
|
905
|
+
}
|
901
906
|
rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
|
902
907
|
} else {
|
903
908
|
oj_err_raise(&pi->err);
|
data/ext/oj/util.c
CHANGED
@@ -110,7 +110,7 @@ sec_as_time(int64_t secs, TimeInfo ti) {
|
|
110
110
|
}
|
111
111
|
}
|
112
112
|
}
|
113
|
-
ti->year = (qc - shift) * 400 + c * 100 + qy * 4 + y;
|
113
|
+
ti->year = (int)((qc - (int64_t)shift) * 400 + c * 100 + qy * 4 + y);
|
114
114
|
if (leap) {
|
115
115
|
ms = eom_leap_secs;
|
116
116
|
} else {
|
@@ -125,12 +125,12 @@ sec_as_time(int64_t secs, TimeInfo ti) {
|
|
125
125
|
break;
|
126
126
|
}
|
127
127
|
}
|
128
|
-
ti->day = secs / 86400LL;
|
128
|
+
ti->day = (int)(secs / 86400LL);
|
129
129
|
secs = secs - (int64_t)ti->day * 86400LL;
|
130
130
|
ti->day++;
|
131
|
-
ti->hour = secs / 3600LL;
|
131
|
+
ti->hour = (int)(secs / 3600LL);
|
132
132
|
secs = secs - (int64_t)ti->hour * 3600LL;
|
133
|
-
ti->min = secs / 60LL;
|
133
|
+
ti->min = (int)(secs / 60LL);
|
134
134
|
secs = secs - (int64_t)ti->min * 60LL;
|
135
|
-
ti->sec = secs;
|
135
|
+
ti->sec = (int)secs;
|
136
136
|
}
|
data/ext/oj/wab.c
CHANGED
@@ -148,11 +148,12 @@ dump_array(VALUE a, int depth, Out out, bool as_ok) {
|
|
148
148
|
}
|
149
149
|
|
150
150
|
static int
|
151
|
-
hash_cb(VALUE key, VALUE value,
|
151
|
+
hash_cb(VALUE key, VALUE value, VALUE ov) {
|
152
|
+
Out out = (Out)ov;
|
152
153
|
int depth = out->depth;
|
153
154
|
long size;
|
154
155
|
int rtype = rb_type(key);
|
155
|
-
|
156
|
+
|
156
157
|
if (rtype != T_SYMBOL) {
|
157
158
|
rb_raise(rb_eTypeError, "In :wab mode all Hash keys must be Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));
|
158
159
|
}
|
@@ -270,7 +271,7 @@ static DumpFunc wab_funcs[] = {
|
|
270
271
|
void
|
271
272
|
oj_dump_wab_val(VALUE obj, int depth, Out out) {
|
272
273
|
int type = rb_type(obj);
|
273
|
-
|
274
|
+
|
274
275
|
if (Yes == out->opts->trace) {
|
275
276
|
oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
|
276
277
|
}
|
@@ -324,7 +325,7 @@ add_value(ParseInfo pi, VALUE val) {
|
|
324
325
|
static bool
|
325
326
|
uuid_check(const char *str, int len) {
|
326
327
|
int i;
|
327
|
-
|
328
|
+
|
328
329
|
for (i = 0; i < 8; i++, str++) {
|
329
330
|
if ('x' != hex_chars[*(uint8_t*)str]) {
|
330
331
|
return false;
|
@@ -380,7 +381,7 @@ time_parse(const char *s, int len) {
|
|
380
381
|
long nsecs = 0;
|
381
382
|
int i;
|
382
383
|
time_t secs;
|
383
|
-
|
384
|
+
|
384
385
|
memset(&tm, 0, sizeof(tm));
|
385
386
|
if ('-' == *s) {
|
386
387
|
s++;
|
@@ -444,7 +445,7 @@ protect_uri(VALUE rstr) {
|
|
444
445
|
static VALUE
|
445
446
|
cstr_to_rstr(const char *str, size_t len) {
|
446
447
|
volatile VALUE v = Qnil;
|
447
|
-
|
448
|
+
|
448
449
|
if (30 == len && '-' == str[4] && '-' == str[7] && 'T' == str[10] && ':' == str[13] && ':' == str[16] && '.' == str[19] && 'Z' == str[29]) {
|
449
450
|
if (Qnil != (v = time_parse(str, (int)len))) {
|
450
451
|
return v;
|
@@ -521,7 +522,7 @@ hash_set_cstr(ParseInfo pi, Val parent, const char *str, size_t len, const char
|
|
521
522
|
static void
|
522
523
|
hash_set_num(ParseInfo pi, Val parent, NumInfo ni) {
|
523
524
|
volatile VALUE rval = Qnil;
|
524
|
-
|
525
|
+
|
525
526
|
if (ni->infinity || ni->nan) {
|
526
527
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
527
528
|
}
|
@@ -551,7 +552,7 @@ start_array(ParseInfo pi) {
|
|
551
552
|
static void
|
552
553
|
array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
553
554
|
volatile VALUE rval = cstr_to_rstr(str, len);
|
554
|
-
|
555
|
+
|
555
556
|
rb_ary_push(stack_peek(&pi->stack)->val, rval);
|
556
557
|
if (Yes == pi->options.trace) {
|
557
558
|
oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, rval);
|
@@ -628,4 +629,3 @@ oj_wab_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
|
|
628
629
|
|
629
630
|
return oj_pi_parse(argc, argv, &pi, json, len, true);
|
630
631
|
}
|
631
|
-
|
data/lib/oj/version.rb
CHANGED
data/pages/Rails.md
CHANGED
@@ -26,44 +26,44 @@ directly. If Rails mode is also desired then use the `Oj.default_options` to
|
|
26
26
|
change the default mode.
|
27
27
|
|
28
28
|
Some of the Oj options are supported as arguments to the encoder if called
|
29
|
-
from Oj::Rails.encode() but when using the Oj::Rails::Encoder class the
|
30
|
-
encode() method does not support optional arguments as required by the
|
29
|
+
from `Oj::Rails.encode()` but when using the `Oj::Rails::Encoder` class the
|
30
|
+
`encode()` method does not support optional arguments as required by the
|
31
31
|
ActiveSupport compliance guidelines. The general approach Rails takes for
|
32
32
|
configuring encoding options is to either set global values or to create a new
|
33
33
|
instance of the Encoder class and provide options in the initializer.
|
34
34
|
|
35
35
|
The globals that ActiveSupport uses for encoding are:
|
36
36
|
|
37
|
-
* ActiveSupport::JSON::Encoding.use_standard_json_time_format
|
38
|
-
* ActiveSupport::JSON::Encoding.escape_html_entities_in_json
|
39
|
-
* ActiveSupport::JSON::Encoding.time_precision
|
40
|
-
* ActiveSupport::JSON::Encoding.json_encoder
|
37
|
+
* `ActiveSupport::JSON::Encoding.use_standard_json_time_format`
|
38
|
+
* `ActiveSupport::JSON::Encoding.escape_html_entities_in_json`
|
39
|
+
* `ActiveSupport::JSON::Encoding.time_precision`
|
40
|
+
* `ActiveSupport::JSON::Encoding.json_encoder`
|
41
41
|
|
42
42
|
Those globals are aliased to also be accessed from the ActiveSupport module
|
43
|
-
directly so ActiveSupport::JSON::Encoding.time_precision can also be accessed
|
44
|
-
from ActiveSupport.time_precision
|
45
|
-
Rails after the Oj::Rails.set_encode() method is called. That also sets the
|
46
|
-
ActiveSupport.json_encoder to the Oj::Rails::Encoder class.
|
43
|
+
directly so `ActiveSupport::JSON::Encoding.time_precision` can also be accessed
|
44
|
+
from `ActiveSupport.time_precision`. Oj makes use of these globals in mimicing
|
45
|
+
Rails after the `Oj::Rails.set_encode()` method is called. That also sets the
|
46
|
+
`ActiveSupport.json_encoder` to the `Oj::Rails::Encoder` class.
|
47
47
|
|
48
|
-
Options passed into a call to to_json() are passed to the as_json()
|
48
|
+
Options passed into a call to `to_json()` are passed to the `as_json()`
|
49
49
|
methods. These are mostly ignored by Oj and simply passed on without
|
50
50
|
modifications as per the guidelines. The exception to this are the options
|
51
|
-
specific to Oj such as the
|
51
|
+
specific to Oj such as the `:circular` option which it used to detect circular
|
52
52
|
references while encoding.
|
53
53
|
|
54
54
|
By default Oj acts like the ActiveSupport encoder and honors any changes in
|
55
|
-
the as_json() methods. There are some optimized Oj encoders for some
|
56
|
-
classes. When the optimized encoder it toggled the as_json() methods will not
|
55
|
+
the `as_json()` methods. There are some optimized Oj encoders for some
|
56
|
+
classes. When the optimized encoder it toggled the `as_json()` methods will not
|
57
57
|
be called for that class but instead the optimized version will be called. The
|
58
58
|
optimized version is the same as the ActiveSupport default encoding for a
|
59
|
-
given class. The optimized versions are toggled with the optimize() and
|
60
|
-
deoptimize() methods. There is a default optimized version for every class
|
59
|
+
given class. The optimized versions are toggled with the `optimize()` and
|
60
|
+
`deoptimize()` methods. There is a default optimized version for every class
|
61
61
|
that takes the visible attributes and encodes them but that may not be the
|
62
62
|
same as what Rails uses. Trial and error is the best approach for classes not
|
63
63
|
listed here.
|
64
64
|
|
65
65
|
The classes that can be put in optimized mode and are optimized when
|
66
|
-
Oj::Rails.optimize is called with no arguments are:
|
66
|
+
`Oj::Rails.optimize` is called with no arguments are:
|
67
67
|
|
68
68
|
* Array
|
69
69
|
* BigDecimal
|
@@ -77,8 +77,46 @@ Oj::Rails.optimize is called with no arguments are:
|
|
77
77
|
* any class inheriting from ActiveRecord::Base
|
78
78
|
* any other class where all attributes should be dumped
|
79
79
|
|
80
|
-
The ActiveSupport decoder is the JSON.parse() method. Calling the
|
81
|
-
Oj::Rails.set_decoder() method replaces that method with the Oj equivalent.
|
80
|
+
The ActiveSupport decoder is the `JSON.parse()` method. Calling the
|
81
|
+
`Oj::Rails.set_decoder()` method replaces that method with the Oj equivalent.
|
82
|
+
|
83
|
+
### Usage in Rails 3
|
84
|
+
|
85
|
+
To support Rails 3 you can create a new module mixin to prepend to controllers:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'oj'
|
89
|
+
|
90
|
+
module OjJsonEncoder
|
91
|
+
def render(options = nil, extra_options = {}, &block)
|
92
|
+
if options && options[:json]
|
93
|
+
obj = options.delete(:json)
|
94
|
+
options[:text] = Oj.dump(obj, :mode => :rails)
|
95
|
+
options[:content_type] = 'application/json'
|
96
|
+
end
|
97
|
+
super
|
98
|
+
end
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
Usage:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
class MyController < ApplicationController
|
106
|
+
prepend OjJsonEncoder
|
107
|
+
def index
|
108
|
+
render :json => { :hello => 'world' }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
### Older Ruby Version Support (Pre 2.3.0)
|
114
|
+
|
115
|
+
If you are using an older version of Ruby, you can pin `oj` to an earlier version in your Gemfile:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
gem 'oj', '3.7.12'
|
119
|
+
```
|
82
120
|
|
83
121
|
### Notes:
|
84
122
|
|
@@ -87,8 +125,8 @@ Oj::Rails.set_decoder() method replaces that method with the Oj equivalent.
|
|
87
125
|
significant digits which can be either 16 or 17 depending on the value.
|
88
126
|
|
89
127
|
2. Optimized Hashs do not collapse keys that become the same in the output. As
|
90
|
-
an example, a non-String object that has a to_s() method will become the
|
91
|
-
return value of the to_s() method in the output without checking to see if
|
128
|
+
an example, a non-String object that has a `to_s()` method will become the
|
129
|
+
return value of the `to_s()` method in the output without checking to see if
|
92
130
|
that has already been used. This could occur is a mix of String and Symbols
|
93
131
|
are used as keys or if a other non-String objects such as Numerics are mixed
|
94
132
|
with numbers as Strings.
|