oj 2.18.5 → 3.0.0
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 +33 -226
- data/ext/oj/circarray.c +0 -25
- data/ext/oj/circarray.h +0 -25
- data/ext/oj/code.c +227 -0
- data/ext/oj/code.h +40 -0
- data/ext/oj/compat.c +126 -38
- data/ext/oj/custom.c +1097 -0
- data/ext/oj/dump.c +658 -2376
- data/ext/oj/dump.h +92 -0
- data/ext/oj/dump_compat.c +937 -0
- data/ext/oj/dump_leaf.c +254 -0
- data/ext/oj/dump_object.c +810 -0
- data/ext/oj/dump_rails.c +329 -0
- data/ext/oj/dump_strict.c +416 -0
- data/ext/oj/err.c +0 -25
- data/ext/oj/err.h +8 -2
- data/ext/oj/fast.c +24 -24
- data/ext/oj/mimic_json.c +817 -0
- data/ext/oj/mimic_rails.c +806 -0
- data/ext/oj/mimic_rails.h +17 -0
- data/ext/oj/object.c +18 -72
- data/ext/oj/odd.c +0 -25
- data/ext/oj/odd.h +2 -27
- data/ext/oj/oj.c +655 -1503
- data/ext/oj/oj.h +93 -40
- data/ext/oj/parse.c +99 -46
- data/ext/oj/parse.h +12 -26
- data/ext/oj/reader.c +1 -25
- data/ext/oj/reader.h +3 -25
- data/ext/oj/resolve.c +9 -11
- data/ext/oj/resolve.h +2 -2
- data/ext/oj/rxclass.c +133 -0
- data/ext/oj/rxclass.h +27 -0
- data/ext/oj/saj.c +4 -25
- data/ext/oj/scp.c +3 -25
- data/ext/oj/sparse.c +89 -13
- data/ext/oj/stream_writer.c +301 -0
- data/ext/oj/strict.c +4 -27
- data/ext/oj/string_writer.c +480 -0
- data/ext/oj/val_stack.h +6 -2
- data/lib/oj.rb +1 -23
- data/lib/oj/easy_hash.rb +12 -4
- data/lib/oj/json.rb +172 -0
- data/lib/oj/mimic.rb +123 -18
- data/lib/oj/state.rb +131 -0
- data/lib/oj/version.rb +1 -1
- data/pages/Advanced.md +22 -0
- data/pages/Compatibility.md +25 -0
- data/pages/Custom.md +23 -0
- data/pages/Encoding.md +65 -0
- data/pages/JsonGem.md +79 -0
- data/pages/Modes.md +140 -0
- data/pages/Options.md +250 -0
- data/pages/Rails.md +60 -0
- data/pages/Security.md +20 -0
- data/test/activesupport4/decoding_test.rb +105 -0
- data/test/activesupport4/encoding_test.rb +531 -0
- data/test/activesupport4/test_helper.rb +41 -0
- data/test/activesupport5/decoding_test.rb +125 -0
- data/test/activesupport5/encoding_test.rb +483 -0
- data/test/activesupport5/encoding_test_cases.rb +90 -0
- data/test/activesupport5/test_helper.rb +50 -0
- data/test/activesupport5/time_zone_test_helpers.rb +24 -0
- data/test/json_gem/json_addition_test.rb +216 -0
- data/test/json_gem/json_common_interface_test.rb +143 -0
- data/test/json_gem/json_encoding_test.rb +109 -0
- data/test/json_gem/json_ext_parser_test.rb +20 -0
- data/test/json_gem/json_fixtures_test.rb +35 -0
- data/test/json_gem/json_generator_test.rb +383 -0
- data/test/json_gem/json_generic_object_test.rb +90 -0
- data/test/json_gem/json_parser_test.rb +470 -0
- data/test/json_gem/json_string_matching_test.rb +42 -0
- data/test/json_gem/test_helper.rb +18 -0
- data/test/perf_compat.rb +30 -28
- data/test/perf_object.rb +1 -1
- data/test/perf_strict.rb +18 -1
- data/test/sample.rb +0 -1
- data/test/test_compat.rb +169 -93
- data/test/test_custom.rb +355 -0
- data/test/test_file.rb +0 -8
- data/test/test_null.rb +376 -0
- data/test/test_object.rb +268 -3
- data/test/test_scp.rb +22 -1
- data/test/test_strict.rb +160 -4
- data/test/test_various.rb +52 -620
- data/test/tests.rb +14 -0
- data/test/tests_mimic.rb +14 -0
- data/test/tests_mimic_addition.rb +7 -0
- metadata +89 -47
- data/test/activesupport_datetime_test.rb +0 -23
- data/test/bug.rb +0 -51
- data/test/bug2.rb +0 -10
- data/test/bug3.rb +0 -46
- data/test/bug_fast.rb +0 -32
- data/test/bug_load.rb +0 -24
- data/test/crash.rb +0 -111
- data/test/curl/curl_oj.rb +0 -46
- data/test/curl/get_oj.rb +0 -24
- data/test/curl/just_curl.rb +0 -31
- data/test/curl/just_oj.rb +0 -51
- data/test/example.rb +0 -11
- data/test/foo.rb +0 -24
- data/test/io.rb +0 -48
- data/test/isolated/test_mimic_rails_datetime.rb +0 -27
- data/test/mod.rb +0 -16
- data/test/rails.rb +0 -50
- data/test/russian.rb +0 -18
- data/test/struct.rb +0 -29
- data/test/test_serializer.rb +0 -59
- data/test/write_timebars.rb +0 -31
@@ -0,0 +1,806 @@
|
|
1
|
+
/* mimic_rails.c
|
2
|
+
* Copyright (c) 2017, Peter Ohler
|
3
|
+
* All rights reserved.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include "dump.h"
|
7
|
+
#include "mimic_rails.h"
|
8
|
+
#include "encode.h"
|
9
|
+
|
10
|
+
// TBD keep static array of strings and functions to help with rails optimization
|
11
|
+
typedef struct _Encoder {
|
12
|
+
struct _ROptTable ropts;
|
13
|
+
struct _Options opts;
|
14
|
+
VALUE arg;
|
15
|
+
} *Encoder;
|
16
|
+
|
17
|
+
extern VALUE Oj;
|
18
|
+
|
19
|
+
static struct _ROptTable ropts = { 0, 0, NULL };
|
20
|
+
|
21
|
+
static VALUE encoder_class = Qnil;
|
22
|
+
static bool escape_html = true;
|
23
|
+
static bool xml_time = true;
|
24
|
+
|
25
|
+
static ROpt create_opt(ROptTable rot, VALUE clas);
|
26
|
+
|
27
|
+
ROpt
|
28
|
+
oj_rails_get_opt(ROptTable rot, VALUE clas) {
|
29
|
+
if (NULL == rot) {
|
30
|
+
rot = &ropts;
|
31
|
+
}
|
32
|
+
if (0 < rot->len) {
|
33
|
+
int lo = 0;
|
34
|
+
int hi = rot->len - 1;
|
35
|
+
int mid;
|
36
|
+
VALUE v;
|
37
|
+
|
38
|
+
if (clas < rot->table->clas || rot->table[hi].clas < clas) {
|
39
|
+
return NULL;
|
40
|
+
}
|
41
|
+
if (rot->table[lo].clas == clas) {
|
42
|
+
return rot->table;
|
43
|
+
}
|
44
|
+
if (rot->table[hi].clas == clas) {
|
45
|
+
return &rot->table[hi];
|
46
|
+
}
|
47
|
+
while (2 <= hi - lo) {
|
48
|
+
mid = (hi + lo) / 2;
|
49
|
+
v = rot->table[mid].clas;
|
50
|
+
if (v == clas) {
|
51
|
+
return &rot->table[mid];
|
52
|
+
}
|
53
|
+
if (v < clas) {
|
54
|
+
lo = mid;
|
55
|
+
} else {
|
56
|
+
hi = mid;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
return NULL;
|
61
|
+
}
|
62
|
+
|
63
|
+
static ROptTable
|
64
|
+
copy_opts(ROptTable src, ROptTable dest) {
|
65
|
+
dest->len = src->len;
|
66
|
+
dest->alen = src->alen;
|
67
|
+
if (NULL == src->table) {
|
68
|
+
dest->table = NULL;
|
69
|
+
} else {
|
70
|
+
dest->table = ALLOC_N(struct _ROpt, dest->alen);
|
71
|
+
memcpy(dest->table, src->table, sizeof(struct _ROpt) * dest->alen);
|
72
|
+
}
|
73
|
+
return NULL;
|
74
|
+
}
|
75
|
+
|
76
|
+
static int
|
77
|
+
dump_attr_cb(ID key, VALUE value, Out out) {
|
78
|
+
int depth = out->depth;
|
79
|
+
size_t size = depth * out->indent + 1;
|
80
|
+
const char *attr = rb_id2name(key);
|
81
|
+
|
82
|
+
#if HAS_EXCEPTION_MAGIC
|
83
|
+
if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
|
84
|
+
return ST_CONTINUE;
|
85
|
+
}
|
86
|
+
#endif
|
87
|
+
assure_size(out, size);
|
88
|
+
fill_indent(out, depth);
|
89
|
+
if ('@' == *attr) {
|
90
|
+
attr++;
|
91
|
+
oj_dump_cstr(attr, strlen(attr), 0, 0, out);
|
92
|
+
} else {
|
93
|
+
char buf[32];
|
94
|
+
|
95
|
+
*buf = '~';
|
96
|
+
strncpy(buf + 1, attr, sizeof(buf) - 2);
|
97
|
+
buf[sizeof(buf) - 1] = '\0';
|
98
|
+
oj_dump_cstr(buf, strlen(buf), 0, 0, out);
|
99
|
+
}
|
100
|
+
*out->cur++ = ':';
|
101
|
+
oj_dump_rails_val(value, depth, out, true);
|
102
|
+
out->depth = depth;
|
103
|
+
*out->cur++ = ',';
|
104
|
+
|
105
|
+
return ST_CONTINUE;
|
106
|
+
}
|
107
|
+
|
108
|
+
static void
|
109
|
+
dump_obj_attrs(VALUE obj, int depth, Out out, bool as_ok) {
|
110
|
+
assure_size(out, 2);
|
111
|
+
*out->cur++ = '{';
|
112
|
+
out->depth = depth + 1;
|
113
|
+
rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
|
114
|
+
if (',' == *(out->cur - 1)) {
|
115
|
+
out->cur--; // backup to overwrite last comma
|
116
|
+
}
|
117
|
+
out->depth = depth;
|
118
|
+
fill_indent(out, depth);
|
119
|
+
*out->cur++ = '}';
|
120
|
+
*out->cur = '\0';
|
121
|
+
}
|
122
|
+
|
123
|
+
static void
|
124
|
+
dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
|
125
|
+
int d3 = depth + 2;
|
126
|
+
size_t size = d3 * out->indent + 2;
|
127
|
+
size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
|
128
|
+
volatile VALUE ma;
|
129
|
+
volatile VALUE v;
|
130
|
+
int cnt;
|
131
|
+
int i;
|
132
|
+
int len;
|
133
|
+
const char *name;
|
134
|
+
|
135
|
+
#ifdef RSTRUCT_LEN
|
136
|
+
#if UNIFY_FIXNUM_AND_BIGNUM
|
137
|
+
cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
|
138
|
+
#else // UNIFY_FIXNUM_AND_INTEGER
|
139
|
+
cnt = (int)RSTRUCT_LEN(obj);
|
140
|
+
#endif // UNIFY_FIXNUM_AND_INTEGER
|
141
|
+
#else
|
142
|
+
// This is a bit risky as a struct in C ruby is not the same as a Struct
|
143
|
+
// class in interpreted Ruby so length() may not be defined.
|
144
|
+
cnt = FIX2INT(rb_funcall(obj, oj_length_id, 0));
|
145
|
+
#endif
|
146
|
+
ma = rb_struct_s_members(rb_obj_class(obj));
|
147
|
+
assure_size(out, 2);
|
148
|
+
*out->cur++ = '{';
|
149
|
+
for (i = 0; i < cnt; i++) {
|
150
|
+
name = rb_id2name(SYM2ID(rb_ary_entry(ma, i)));
|
151
|
+
len = strlen(name);
|
152
|
+
assure_size(out, size + sep_len + 6);
|
153
|
+
if (0 < i) {
|
154
|
+
*out->cur++ = ',';
|
155
|
+
}
|
156
|
+
fill_indent(out, d3);
|
157
|
+
*out->cur++ = '"';
|
158
|
+
memcpy(out->cur, name, len);
|
159
|
+
out->cur += len;
|
160
|
+
*out->cur++ = '"';
|
161
|
+
if (0 < out->opts->dump_opts.before_size) {
|
162
|
+
strcpy(out->cur, out->opts->dump_opts.before_sep);
|
163
|
+
out->cur += out->opts->dump_opts.before_size;
|
164
|
+
}
|
165
|
+
*out->cur++ = ':';
|
166
|
+
if (0 < out->opts->dump_opts.after_size) {
|
167
|
+
strcpy(out->cur, out->opts->dump_opts.after_sep);
|
168
|
+
out->cur += out->opts->dump_opts.after_size;
|
169
|
+
}
|
170
|
+
#ifdef RSTRUCT_LEN
|
171
|
+
v = RSTRUCT_GET(obj, i);
|
172
|
+
#else
|
173
|
+
v = rb_struct_aref(obj, INT2FIX(i));
|
174
|
+
#endif
|
175
|
+
oj_dump_rails_val(v, d3, out, true);
|
176
|
+
}
|
177
|
+
fill_indent(out, depth);
|
178
|
+
*out->cur++ = '}';
|
179
|
+
*out->cur = '\0';
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
static ID to_a_id = 0;
|
184
|
+
|
185
|
+
static void
|
186
|
+
dump_enumerable(VALUE obj, int depth, Out out, bool as_ok) {
|
187
|
+
if (0 == to_a_id) {
|
188
|
+
to_a_id = rb_intern("to_a");
|
189
|
+
}
|
190
|
+
oj_dump_rails_val(rb_funcall(obj, to_a_id, 0), depth, out, false);
|
191
|
+
}
|
192
|
+
|
193
|
+
static void
|
194
|
+
dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
|
195
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
196
|
+
const char *str = rb_string_value_ptr((VALUE*)&rstr);
|
197
|
+
|
198
|
+
if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
|
199
|
+
oj_dump_nil(Qnil, depth, out, false);
|
200
|
+
} else {
|
201
|
+
oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
static void
|
206
|
+
dump_time(VALUE obj, int depth, Out out, bool as_ok) {
|
207
|
+
char buf[64];
|
208
|
+
struct tm *tm;
|
209
|
+
long one = 1000000000;
|
210
|
+
#if HAS_RB_TIME_TIMESPEC
|
211
|
+
struct timespec ts = rb_time_timespec(obj);
|
212
|
+
time_t sec = ts.tv_sec;
|
213
|
+
long nsec = ts.tv_nsec;
|
214
|
+
#else
|
215
|
+
time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
216
|
+
#if HAS_NANO_TIME
|
217
|
+
long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
218
|
+
#else
|
219
|
+
long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
|
220
|
+
#endif
|
221
|
+
#endif
|
222
|
+
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
223
|
+
int tzhour, tzmin;
|
224
|
+
char tzsign = '+';
|
225
|
+
int len;
|
226
|
+
|
227
|
+
if (out->end - out->cur <= 36) {
|
228
|
+
assure_size(out, 36);
|
229
|
+
}
|
230
|
+
if (9 > out->opts->sec_prec) {
|
231
|
+
int i;
|
232
|
+
|
233
|
+
// Rails does not round when reducing precision but instead floors,
|
234
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
235
|
+
nsec = nsec / 10;
|
236
|
+
one /= 10;
|
237
|
+
}
|
238
|
+
if (one <= nsec) {
|
239
|
+
nsec -= one;
|
240
|
+
sec++;
|
241
|
+
}
|
242
|
+
}
|
243
|
+
// 2012-01-05T23:58:07.123456000+09:00 or 2012/01/05 23:58:07 +0900
|
244
|
+
sec += tzsecs;
|
245
|
+
tm = gmtime(&sec);
|
246
|
+
#if 1
|
247
|
+
if (0 > tzsecs) {
|
248
|
+
tzsign = '-';
|
249
|
+
tzhour = (int)(tzsecs / -3600);
|
250
|
+
tzmin = (int)(tzsecs / -60) - (tzhour * 60);
|
251
|
+
} else {
|
252
|
+
tzhour = (int)(tzsecs / 3600);
|
253
|
+
tzmin = (int)(tzsecs / 60) - (tzhour * 60);
|
254
|
+
}
|
255
|
+
#else
|
256
|
+
if (0 > tm->tm_gmtoff) {
|
257
|
+
tzsign = '-';
|
258
|
+
tzhour = (int)(tm->tm_gmtoff / -3600);
|
259
|
+
tzmin = (int)(tm->tm_gmtoff / -60) - (tzhour * 60);
|
260
|
+
} else {
|
261
|
+
tzhour = (int)(tm->tm_gmtoff / 3600);
|
262
|
+
tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
|
263
|
+
}
|
264
|
+
#endif
|
265
|
+
if (!xml_time) {
|
266
|
+
len = sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d %c%02d%02d",
|
267
|
+
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
268
|
+
tm->tm_hour, tm->tm_min, tm->tm_sec, tzsign, tzhour, tzmin);
|
269
|
+
} else if (0 == out->opts->sec_prec) {
|
270
|
+
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
271
|
+
len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ",
|
272
|
+
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
273
|
+
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
274
|
+
} else {
|
275
|
+
len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
|
276
|
+
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
277
|
+
tm->tm_hour, tm->tm_min, tm->tm_sec,
|
278
|
+
tzsign, tzhour, tzmin);
|
279
|
+
}
|
280
|
+
} else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
281
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
|
282
|
+
|
283
|
+
len = 30;
|
284
|
+
if (9 > out->opts->sec_prec) {
|
285
|
+
format[32] = '0' + out->opts->sec_prec;
|
286
|
+
len -= 9 - out->opts->sec_prec;
|
287
|
+
}
|
288
|
+
len = sprintf(buf, format,
|
289
|
+
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
290
|
+
tm->tm_hour, tm->tm_min, tm->tm_sec, nsec);
|
291
|
+
} else {
|
292
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
|
293
|
+
|
294
|
+
len = 35;
|
295
|
+
if (9 > out->opts->sec_prec) {
|
296
|
+
format[32] = '0' + out->opts->sec_prec;
|
297
|
+
len -= 9 - out->opts->sec_prec;
|
298
|
+
}
|
299
|
+
len = sprintf(buf, format,
|
300
|
+
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
301
|
+
tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
|
302
|
+
tzsign, tzhour, tzmin);
|
303
|
+
}
|
304
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
305
|
+
}
|
306
|
+
|
307
|
+
static void
|
308
|
+
dump_to_s(VALUE obj, int depth, Out out, bool as_ok) {
|
309
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
310
|
+
|
311
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
|
312
|
+
}
|
313
|
+
|
314
|
+
typedef struct _NamedFunc {
|
315
|
+
const char *name;
|
316
|
+
DumpFunc func;
|
317
|
+
} *NamedFunc;
|
318
|
+
|
319
|
+
static struct _NamedFunc dump_map[] = {
|
320
|
+
{ "BigDecimal", dump_bigdecimal },
|
321
|
+
{ "Range", dump_to_s },
|
322
|
+
{ "Regexp", dump_to_s },
|
323
|
+
{ "Time", dump_time },
|
324
|
+
{ NULL, NULL },
|
325
|
+
};
|
326
|
+
|
327
|
+
static ROpt
|
328
|
+
create_opt(ROptTable rot, VALUE clas) {
|
329
|
+
ROpt ro;
|
330
|
+
NamedFunc nf;
|
331
|
+
const char *classname = rb_class2name(clas);
|
332
|
+
int olen = rot->len;
|
333
|
+
|
334
|
+
rot->len++;
|
335
|
+
if (NULL == rot->table) {
|
336
|
+
rot->alen = 256;
|
337
|
+
rot->table = ALLOC_N(struct _ROpt, rot->alen);
|
338
|
+
memset(rot->table, 0, sizeof(struct _ROpt) * rot->alen);
|
339
|
+
} else if (rot->alen <= rot->len) {
|
340
|
+
rot->alen *= 2;
|
341
|
+
REALLOC_N(rot->table, struct _ROpt, rot->alen);
|
342
|
+
memset(rot->table + olen, 0, sizeof(struct _ROpt) * olen);
|
343
|
+
}
|
344
|
+
if (0 == olen) {
|
345
|
+
ro = rot->table;
|
346
|
+
} else if (rot->table[olen - 1].clas < clas) {
|
347
|
+
ro = &rot->table[olen];
|
348
|
+
} else {
|
349
|
+
int i;
|
350
|
+
|
351
|
+
for (i = 0, ro = rot->table; i < olen; i++, ro++) {
|
352
|
+
if (clas < ro->clas) {
|
353
|
+
memmove(ro + 1, ro, sizeof(struct _ROpt) * (olen - i));
|
354
|
+
break;
|
355
|
+
}
|
356
|
+
}
|
357
|
+
}
|
358
|
+
ro->clas = clas;
|
359
|
+
ro->on = true;
|
360
|
+
ro->dump = dump_obj_attrs;
|
361
|
+
for (nf = dump_map; NULL != nf->name; nf++) {
|
362
|
+
if (0 == strcmp(nf->name, classname)) {
|
363
|
+
ro->dump = nf->func;
|
364
|
+
break;
|
365
|
+
}
|
366
|
+
}
|
367
|
+
if (ro->dump == dump_obj_attrs) {
|
368
|
+
if (Qtrue == rb_class_inherited_p(clas, rb_cStruct)) { // check before enumerable
|
369
|
+
ro->dump = dump_struct;
|
370
|
+
} else if (Qtrue == rb_class_inherited_p(clas, rb_mEnumerable)) {
|
371
|
+
ro->dump = dump_enumerable;
|
372
|
+
} else if (Qtrue == rb_class_inherited_p(clas, rb_eException)) {
|
373
|
+
ro->dump = dump_to_s;
|
374
|
+
}
|
375
|
+
}
|
376
|
+
return NULL;
|
377
|
+
}
|
378
|
+
|
379
|
+
static void
|
380
|
+
encoder_free(void *ptr) {
|
381
|
+
if (NULL != ptr) {
|
382
|
+
Encoder e = (Encoder)ptr;
|
383
|
+
|
384
|
+
if (NULL != e->ropts.table) {
|
385
|
+
xfree(e->ropts.table);
|
386
|
+
}
|
387
|
+
xfree(ptr);
|
388
|
+
}
|
389
|
+
}
|
390
|
+
|
391
|
+
static void
|
392
|
+
encoder_mark(void *ptr) {
|
393
|
+
if (NULL != ptr) {
|
394
|
+
Encoder e = (Encoder)ptr;
|
395
|
+
|
396
|
+
if (Qnil != e->arg) {
|
397
|
+
rb_gc_mark(e->arg);
|
398
|
+
}
|
399
|
+
}
|
400
|
+
}
|
401
|
+
|
402
|
+
/* Document-method: new
|
403
|
+
* call-seq: new(options=nil)
|
404
|
+
*
|
405
|
+
* Creates a new Encoder.
|
406
|
+
* - *options* [_Hash_] formatting options
|
407
|
+
*/
|
408
|
+
static VALUE
|
409
|
+
encoder_new(int argc, VALUE *argv, VALUE self) {
|
410
|
+
Encoder e = ALLOC(struct _Encoder);
|
411
|
+
|
412
|
+
e->opts = oj_default_options;
|
413
|
+
e->arg = Qnil;
|
414
|
+
copy_opts(&ropts, &e->ropts);
|
415
|
+
|
416
|
+
if (1 <= argc && Qnil != *argv) {
|
417
|
+
oj_parse_options(*argv, &e->opts);
|
418
|
+
e->arg = *argv;
|
419
|
+
}
|
420
|
+
return Data_Wrap_Struct(encoder_class, encoder_mark, encoder_free, e);
|
421
|
+
}
|
422
|
+
|
423
|
+
static void
|
424
|
+
optimize(int argc, VALUE *argv, ROptTable rot, bool on) {
|
425
|
+
ROpt ro;
|
426
|
+
|
427
|
+
if (0 == argc) {
|
428
|
+
int i;
|
429
|
+
|
430
|
+
for (i = 0; i < rot->len; i++) {
|
431
|
+
rot->table[i].on = on;
|
432
|
+
}
|
433
|
+
}
|
434
|
+
for (; 0 < argc; argc--, argv++) {
|
435
|
+
if (rb_cHash == *argv) {
|
436
|
+
oj_rails_hash_opt = on;
|
437
|
+
} else if (rb_cArray == *argv) {
|
438
|
+
oj_rails_array_opt = on;
|
439
|
+
} else if (NULL != (ro = oj_rails_get_opt(rot, *argv)) ||
|
440
|
+
NULL != (ro = create_opt(rot, *argv))) {
|
441
|
+
ro->on = on;
|
442
|
+
}
|
443
|
+
// TBD recurse if there are subclasses
|
444
|
+
}
|
445
|
+
}
|
446
|
+
|
447
|
+
/* Document-method optimize
|
448
|
+
* call-seq: optimize(*classes)
|
449
|
+
*
|
450
|
+
* Use Oj rails optimized routines to encode the specified classes. This
|
451
|
+
* ignores the as_json() method on the class and uses an internal encoding
|
452
|
+
* instead. Passing in no classes indicates all should use the optimized
|
453
|
+
* version of encoding for all previously optimized classes. Passing in the
|
454
|
+
* Object class set a global switch that will then use the optimized behavior
|
455
|
+
* for all classes.
|
456
|
+
*
|
457
|
+
* - *classes* [_Class_] a list of classes to optimize
|
458
|
+
*/
|
459
|
+
static VALUE
|
460
|
+
encoder_optimize(int argc, VALUE *argv, VALUE self) {
|
461
|
+
Encoder e = (Encoder)DATA_PTR(self);
|
462
|
+
|
463
|
+
optimize(argc, argv, &e->ropts, true);
|
464
|
+
|
465
|
+
return Qnil;
|
466
|
+
}
|
467
|
+
|
468
|
+
/* Document-method: optimize
|
469
|
+
* call-seq: optimize(*classes)
|
470
|
+
*
|
471
|
+
* Use Oj rails optimized routines to encode the specified classes. This
|
472
|
+
* ignores the as_json() method on the class and uses an internal encoding
|
473
|
+
* instead. Passing in no classes indicates all should use the optimized
|
474
|
+
* version of encoding for all previously optimized classes. Passing in the
|
475
|
+
* Object class set a global switch that will then use the optimized behavior
|
476
|
+
* for all classes.
|
477
|
+
*
|
478
|
+
* - *classes* [_Class_] a list of classes to optimize
|
479
|
+
*/
|
480
|
+
static VALUE
|
481
|
+
rails_optimize(int argc, VALUE *argv, VALUE self) {
|
482
|
+
optimize(argc, argv, &ropts, true);
|
483
|
+
|
484
|
+
return Qnil;
|
485
|
+
}
|
486
|
+
|
487
|
+
/* Document-method: deoptimize
|
488
|
+
* call-seq: deoptimize(*classes)
|
489
|
+
*
|
490
|
+
* Turn off Oj rails optimization on the specified classes.
|
491
|
+
*
|
492
|
+
* - *classes* [_Class_] a list of classes to deoptimize
|
493
|
+
*/
|
494
|
+
static VALUE
|
495
|
+
encoder_deoptimize(int argc, VALUE *argv, VALUE self) {
|
496
|
+
Encoder e = (Encoder)DATA_PTR(self);
|
497
|
+
|
498
|
+
optimize(argc, argv, &e->ropts, false);
|
499
|
+
|
500
|
+
return Qnil;
|
501
|
+
}
|
502
|
+
|
503
|
+
/* Document-method: deoptimize
|
504
|
+
* call-seq: deoptimize(*classes)
|
505
|
+
*
|
506
|
+
* Turn off Oj rails optimization on the specified classes.
|
507
|
+
*
|
508
|
+
* - *classes* [_Class_] a list of classes to deoptimize
|
509
|
+
*/
|
510
|
+
static VALUE
|
511
|
+
rails_deoptimize(int argc, VALUE *argv, VALUE self) {
|
512
|
+
optimize(argc, argv, &ropts, false);
|
513
|
+
|
514
|
+
return Qnil;
|
515
|
+
}
|
516
|
+
|
517
|
+
/* Document-method:optimized?
|
518
|
+
* call-seq: optimized?(clas)
|
519
|
+
*
|
520
|
+
* - *clas* [_Class_] Class to check
|
521
|
+
*
|
522
|
+
* @return true if the class is being optimized for rails and false otherwise
|
523
|
+
*/
|
524
|
+
static VALUE
|
525
|
+
encoder_optimized(VALUE self, VALUE clas) {
|
526
|
+
Encoder e = (Encoder)DATA_PTR(self);
|
527
|
+
ROpt ro = oj_rails_get_opt(&e->ropts, clas);
|
528
|
+
|
529
|
+
if (NULL == ro) {
|
530
|
+
return Qfalse;
|
531
|
+
}
|
532
|
+
return (ro->on) ? Qtrue : Qfalse;
|
533
|
+
}
|
534
|
+
|
535
|
+
/* Document-method: optimized?
|
536
|
+
* call-seq: optimized?(clas)
|
537
|
+
*
|
538
|
+
* Returns true if the specified Class is being optimized.
|
539
|
+
*/
|
540
|
+
static VALUE
|
541
|
+
rails_optimized(VALUE self, VALUE clas) {
|
542
|
+
ROpt ro = oj_rails_get_opt(&ropts, clas);
|
543
|
+
|
544
|
+
if (NULL == ro) {
|
545
|
+
return Qfalse;
|
546
|
+
}
|
547
|
+
return (ro->on) ? Qtrue : Qfalse;
|
548
|
+
}
|
549
|
+
|
550
|
+
typedef struct _OO {
|
551
|
+
Out out;
|
552
|
+
VALUE obj;
|
553
|
+
} *OO;
|
554
|
+
|
555
|
+
static VALUE
|
556
|
+
protect_dump(VALUE ov) {
|
557
|
+
OO oo = (OO)ov;
|
558
|
+
|
559
|
+
oj_dump_rails_val(oo->obj, 0, oo->out, true);
|
560
|
+
|
561
|
+
return Qnil;
|
562
|
+
}
|
563
|
+
|
564
|
+
static VALUE
|
565
|
+
encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *argv) {
|
566
|
+
char buf[4096];
|
567
|
+
struct _Out out;
|
568
|
+
struct _Options copts = *opts;
|
569
|
+
VALUE rstr = Qnil;
|
570
|
+
struct _OO oo;
|
571
|
+
int line = 0;
|
572
|
+
|
573
|
+
oo.out = &out;
|
574
|
+
oo.obj = obj;
|
575
|
+
copts.str_rx.head = NULL;
|
576
|
+
copts.str_rx.tail = NULL;
|
577
|
+
copts.mode = RailsMode;
|
578
|
+
if (escape_html) {
|
579
|
+
copts.escape_mode = JXEsc;
|
580
|
+
} else {
|
581
|
+
copts.escape_mode = RailsEsc;
|
582
|
+
}
|
583
|
+
out.buf = buf;
|
584
|
+
out.end = buf + sizeof(buf) - 10;
|
585
|
+
out.allocated = 0;
|
586
|
+
out.omit_nil = copts.dump_opts.omit_nil;
|
587
|
+
out.caller = 0;
|
588
|
+
out.cur = out.buf;
|
589
|
+
out.circ_cnt = 0;
|
590
|
+
out.opts = &copts;
|
591
|
+
out.hash_cnt = 0;
|
592
|
+
out.indent = copts.indent;
|
593
|
+
out.argc = argc;
|
594
|
+
out.argv = argv;
|
595
|
+
out.ropts = ropts;
|
596
|
+
if (Yes == copts.circular) {
|
597
|
+
oj_cache8_new(&out.circ_cache);
|
598
|
+
}
|
599
|
+
//oj_dump_rails_val(*argv, 0, &out, true);
|
600
|
+
rb_protect(protect_dump, (VALUE)&oo, &line);
|
601
|
+
|
602
|
+
if (0 == line) {
|
603
|
+
if (0 < out.indent) {
|
604
|
+
switch (*(out.cur - 1)) {
|
605
|
+
case ']':
|
606
|
+
case '}':
|
607
|
+
assure_size(&out, 2);
|
608
|
+
*out.cur++ = '\n';
|
609
|
+
default:
|
610
|
+
break;
|
611
|
+
}
|
612
|
+
}
|
613
|
+
*out.cur = '\0';
|
614
|
+
|
615
|
+
if (0 == out.buf) {
|
616
|
+
rb_raise(rb_eNoMemError, "Not enough memory.");
|
617
|
+
}
|
618
|
+
rstr = rb_str_new2(out.buf);
|
619
|
+
rstr = oj_encode(rstr);
|
620
|
+
}
|
621
|
+
if (Yes == copts.circular) {
|
622
|
+
oj_cache8_delete(out.circ_cache);
|
623
|
+
}
|
624
|
+
if (out.allocated) {
|
625
|
+
xfree(out.buf);
|
626
|
+
}
|
627
|
+
if (0 != line) {
|
628
|
+
rb_jump_tag(line);
|
629
|
+
}
|
630
|
+
return rstr;
|
631
|
+
}
|
632
|
+
|
633
|
+
/* Document-method: encode
|
634
|
+
* call-seq: encode(obj)
|
635
|
+
*
|
636
|
+
* - *obj* [_Object_] object to encode
|
637
|
+
*
|
638
|
+
* Returns encoded object as a JSON string.
|
639
|
+
*/
|
640
|
+
static VALUE
|
641
|
+
encoder_encode(VALUE self, VALUE obj) {
|
642
|
+
Encoder e = (Encoder)DATA_PTR(self);
|
643
|
+
|
644
|
+
if (Qnil != e->arg) {
|
645
|
+
VALUE argv[1] = { e->arg };
|
646
|
+
|
647
|
+
return encode(obj, &e->ropts, &e->opts, 1, argv);
|
648
|
+
}
|
649
|
+
return encode(obj, &e->ropts, &e->opts, 0, NULL);
|
650
|
+
}
|
651
|
+
|
652
|
+
/* Document-method: encode
|
653
|
+
* call-seq: encode(obj, opts=nil)
|
654
|
+
*
|
655
|
+
* Encode obj as a JSON String.
|
656
|
+
*
|
657
|
+
* - *obj* [_Object_|Hash|Array] object to convert to a JSON String
|
658
|
+
* - *opts* [_Hash_] options
|
659
|
+
*
|
660
|
+
* Returns [_String_]
|
661
|
+
*/
|
662
|
+
static VALUE
|
663
|
+
rails_encode(int argc, VALUE *argv, VALUE self) {
|
664
|
+
if (1 > argc) {
|
665
|
+
rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
|
666
|
+
}
|
667
|
+
if (1 == argc) {
|
668
|
+
return encode(*argv, NULL, &oj_default_options, 0, NULL);
|
669
|
+
} else {
|
670
|
+
return encode(*argv, NULL, &oj_default_options, argc - 1, argv + 1);
|
671
|
+
}
|
672
|
+
}
|
673
|
+
|
674
|
+
static VALUE
|
675
|
+
rails_use_standard_json_time_format(VALUE self, VALUE state) {
|
676
|
+
switch (state) {
|
677
|
+
case Qtrue:
|
678
|
+
case Qfalse:
|
679
|
+
break;
|
680
|
+
case Qnil:
|
681
|
+
state = Qfalse;
|
682
|
+
break;
|
683
|
+
default:
|
684
|
+
state = Qtrue;
|
685
|
+
break;
|
686
|
+
}
|
687
|
+
rb_iv_set(self, "@use_standard_json_time_format", state);
|
688
|
+
xml_time = Qtrue == state;
|
689
|
+
|
690
|
+
return state;
|
691
|
+
}
|
692
|
+
|
693
|
+
static VALUE
|
694
|
+
rails_escape_html_entities_in_json(VALUE self, VALUE state) {
|
695
|
+
rb_iv_set(self, "@escape_html_entities_in_json", state);
|
696
|
+
escape_html = Qtrue == state;
|
697
|
+
|
698
|
+
return state;
|
699
|
+
}
|
700
|
+
|
701
|
+
static VALUE
|
702
|
+
rails_time_precision(VALUE self, VALUE prec) {
|
703
|
+
rb_iv_set(self, "@time_precision", prec);
|
704
|
+
oj_default_options.sec_prec = NUM2INT(prec);
|
705
|
+
|
706
|
+
return prec;
|
707
|
+
}
|
708
|
+
|
709
|
+
/* Document-method: set_encoder
|
710
|
+
*call-seq: set_encoder()
|
711
|
+
*
|
712
|
+
* Sets the ActiveSupport.encoder to Oj::Rails::Encoder and wraps some of the
|
713
|
+
* formatting globals used by ActiveSupport to allow the use of those globals
|
714
|
+
* in the Oj::Rails optimizations.
|
715
|
+
*/
|
716
|
+
static VALUE
|
717
|
+
rails_set_encoder(VALUE self) {
|
718
|
+
VALUE active;
|
719
|
+
VALUE json;
|
720
|
+
VALUE encoding;
|
721
|
+
VALUE pv;
|
722
|
+
|
723
|
+
if (rb_const_defined_at(rb_cObject, rb_intern("ActiveSupport"))) {
|
724
|
+
active = rb_const_get_at(rb_cObject, rb_intern("ActiveSupport"));
|
725
|
+
} else {
|
726
|
+
rb_raise(rb_eStandardError, "ActiveSupport not loaded.");
|
727
|
+
}
|
728
|
+
rb_funcall(active, rb_intern("json_encoder="), 1, encoder_class);
|
729
|
+
|
730
|
+
json = rb_const_get_at(active, rb_intern("JSON"));
|
731
|
+
encoding = rb_const_get_at(json, rb_intern("Encoding"));
|
732
|
+
|
733
|
+
rb_undef_method(active, "use_standard_json_time_format=");
|
734
|
+
rb_define_module_function(encoding, "use_standard_json_time_format=", rails_use_standard_json_time_format, 1);
|
735
|
+
|
736
|
+
rb_undef_method(encoding, "escape_html_entities_in_json=");
|
737
|
+
rb_define_module_function(encoding, "escape_html_entities_in_json=", rails_escape_html_entities_in_json, 1);
|
738
|
+
|
739
|
+
pv = rb_iv_get(encoding, "@time_precision");
|
740
|
+
oj_default_options.sec_prec = NUM2INT(pv);
|
741
|
+
rb_undef_method(encoding, "time_precision=");
|
742
|
+
rb_define_module_function(encoding, "time_precision=", rails_time_precision, 1);
|
743
|
+
|
744
|
+
return Qnil;
|
745
|
+
}
|
746
|
+
|
747
|
+
/* Document-method: set_decoder
|
748
|
+
* call-seq: set_decoder()
|
749
|
+
*
|
750
|
+
* Sets the JSON.parse function to be the Oj::parse function which is json gem
|
751
|
+
* compatible.
|
752
|
+
*/
|
753
|
+
static VALUE
|
754
|
+
rails_set_decoder(VALUE self) {
|
755
|
+
VALUE json;
|
756
|
+
VALUE json_error;
|
757
|
+
|
758
|
+
if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
|
759
|
+
json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
|
760
|
+
} else {
|
761
|
+
json = rb_define_module("JSON");
|
762
|
+
}
|
763
|
+
if (rb_const_defined_at(json, rb_intern("JSONError"))) {
|
764
|
+
json_error = rb_const_get(json, rb_intern("JSONError"));
|
765
|
+
} else {
|
766
|
+
json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
|
767
|
+
}
|
768
|
+
if (rb_const_defined_at(json, rb_intern("ParserError"))) {
|
769
|
+
oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
|
770
|
+
} else {
|
771
|
+
oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
|
772
|
+
}
|
773
|
+
rb_undef_method(json, "parse");
|
774
|
+
rb_define_module_function(json, "parse", oj_mimic_parse, -1);
|
775
|
+
|
776
|
+
return Qnil;
|
777
|
+
}
|
778
|
+
|
779
|
+
/* Document-module: Oj::Rails
|
780
|
+
*
|
781
|
+
* Module that provides rails and active support compatibility.
|
782
|
+
*/
|
783
|
+
/* Document-class: Oj::Rails::Encoder
|
784
|
+
*
|
785
|
+
* The Oj ActiveSupport compliant encoder.
|
786
|
+
*/
|
787
|
+
void
|
788
|
+
oj_mimic_rails_init() {
|
789
|
+
VALUE rails = rb_define_module_under(Oj, "Rails");
|
790
|
+
|
791
|
+
rb_define_module_function(rails, "encode", rails_encode, -1);
|
792
|
+
|
793
|
+
encoder_class = rb_define_class_under(rails, "Encoder", rb_cObject);
|
794
|
+
rb_define_module_function(encoder_class, "new", encoder_new, -1);
|
795
|
+
rb_define_module_function(rails, "optimize", rails_optimize, -1);
|
796
|
+
rb_define_module_function(rails, "deoptimize", rails_deoptimize, -1);
|
797
|
+
rb_define_module_function(rails, "optimized?", rails_optimized, 1);
|
798
|
+
|
799
|
+
rb_define_module_function(rails, "set_encoder", rails_set_encoder, 0);
|
800
|
+
rb_define_module_function(rails, "set_decoder", rails_set_decoder, 0);
|
801
|
+
|
802
|
+
rb_define_method(encoder_class, "encode", encoder_encode, 1);
|
803
|
+
rb_define_method(encoder_class, "optimize", encoder_optimize, -1);
|
804
|
+
rb_define_method(encoder_class, "deoptimize", encoder_deoptimize, -1);
|
805
|
+
rb_define_method(encoder_class, "optimized?", encoder_optimized, 1);
|
806
|
+
}
|