oj 3.8.1 → 3.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/ext/oj/compat.c +5 -5
- data/ext/oj/custom.c +65 -38
- 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 +6 -0
- data/ext/oj/mimic_json.c +15 -3
- data/ext/oj/object.c +8 -5
- data/ext/oj/oj.c +50 -31
- data/ext/oj/oj.h +6 -4
- data/ext/oj/parse.c +16 -3
- data/ext/oj/parse.h +1 -0
- data/ext/oj/rails.c +40 -4
- data/ext/oj/resolve.c +3 -3
- data/ext/oj/sparse.c +5 -0
- data/ext/oj/util.c +5 -5
- data/ext/oj/val_stack.c +9 -9
- data/ext/oj/val_stack.h +9 -9
- data/ext/oj/wab.c +9 -9
- data/lib/oj/version.rb +1 -1
- data/pages/Options.md +4 -0
- data/pages/Rails.md +21 -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 +42 -157
- 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 +4 -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 +22 -2
data/ext/oj/resolve.c
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
#include <stdlib.h>
|
7
7
|
#include <stdio.h>
|
8
8
|
#include <string.h>
|
9
|
-
#
|
9
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
10
10
|
#include <pthread.h>
|
11
11
|
#endif
|
12
12
|
|
@@ -75,7 +75,7 @@ oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE
|
|
75
75
|
if (No == pi->options.class_cache) {
|
76
76
|
return resolve_classpath(pi, name, len, auto_define, error_class);
|
77
77
|
}
|
78
|
-
#
|
78
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
79
79
|
pthread_mutex_lock(&oj_cache_mutex);
|
80
80
|
#else
|
81
81
|
rb_mutex_lock(oj_cache_mutex);
|
@@ -85,7 +85,7 @@ oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE
|
|
85
85
|
*slot = clas;
|
86
86
|
}
|
87
87
|
}
|
88
|
-
#
|
88
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
89
89
|
pthread_mutex_unlock(&oj_cache_mutex);
|
90
90
|
#else
|
91
91
|
rb_mutex_unlock(oj_cache_mutex);
|
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/val_stack.c
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
/* val_stack.c
|
2
2
|
* Copyright (c) 2011, Peter Ohler
|
3
3
|
* All rights reserved.
|
4
|
-
*
|
4
|
+
*
|
5
5
|
* Redistribution and use in source and binary forms, with or without
|
6
6
|
* modification, are permitted provided that the following conditions are met:
|
7
|
-
*
|
7
|
+
*
|
8
8
|
* - Redistributions of source code must retain the above copyright notice, this
|
9
9
|
* list of conditions and the following disclaimer.
|
10
|
-
*
|
10
|
+
*
|
11
11
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
12
12
|
* this list of conditions and the following disclaimer in the documentation
|
13
13
|
* and/or other materials provided with the distribution.
|
14
|
-
*
|
14
|
+
*
|
15
15
|
* - Neither the name of Peter Ohler nor the names of its contributors may be
|
16
16
|
* used to endorse or promote products derived from this software without
|
17
17
|
* specific prior written permission.
|
18
|
-
*
|
18
|
+
*
|
19
19
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
20
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
21
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
@@ -42,7 +42,7 @@ mark(void *ptr) {
|
|
42
42
|
if (0 == ptr) {
|
43
43
|
return;
|
44
44
|
}
|
45
|
-
#
|
45
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
46
46
|
pthread_mutex_lock(&stack->mutex);
|
47
47
|
#else
|
48
48
|
rb_mutex_lock(stack->mutex);
|
@@ -66,7 +66,7 @@ mark(void *ptr) {
|
|
66
66
|
}
|
67
67
|
}
|
68
68
|
}
|
69
|
-
#
|
69
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
70
70
|
pthread_mutex_unlock(&stack->mutex);
|
71
71
|
#else
|
72
72
|
rb_mutex_unlock(stack->mutex);
|
@@ -75,9 +75,9 @@ mark(void *ptr) {
|
|
75
75
|
|
76
76
|
VALUE
|
77
77
|
oj_stack_init(ValStack stack) {
|
78
|
-
#
|
78
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
79
79
|
int err;
|
80
|
-
|
80
|
+
|
81
81
|
if (0 != (err = pthread_mutex_init(&stack->mutex, 0))) {
|
82
82
|
rb_raise(rb_eException, "failed to initialize a mutex. %s", strerror(err));
|
83
83
|
}
|
data/ext/oj/val_stack.h
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
/* val_stack.h
|
2
2
|
* Copyright (c) 2011, Peter Ohler
|
3
3
|
* All rights reserved.
|
4
|
-
*
|
4
|
+
*
|
5
5
|
* Redistribution and use in source and binary forms, with or without
|
6
6
|
* modification, are permitted provided that the following conditions are met:
|
7
|
-
*
|
7
|
+
*
|
8
8
|
* - Redistributions of source code must retain the above copyright notice, this
|
9
9
|
* list of conditions and the following disclaimer.
|
10
|
-
*
|
10
|
+
*
|
11
11
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
12
12
|
* this list of conditions and the following disclaimer in the documentation
|
13
13
|
* and/or other materials provided with the distribution.
|
14
|
-
*
|
14
|
+
*
|
15
15
|
* - Neither the name of Peter Ohler nor the names of its contributors may be
|
16
16
|
* used to endorse or promote products derived from this software without
|
17
17
|
* specific prior written permission.
|
18
|
-
*
|
18
|
+
*
|
19
19
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
20
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
21
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
@@ -34,7 +34,7 @@
|
|
34
34
|
#include "ruby.h"
|
35
35
|
#include "odd.h"
|
36
36
|
#include <stdint.h>
|
37
|
-
#
|
37
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
38
38
|
#include <pthread.h>
|
39
39
|
#endif
|
40
40
|
|
@@ -72,7 +72,7 @@ typedef struct _valStack {
|
|
72
72
|
Val head; // current stack
|
73
73
|
Val end; // stack end
|
74
74
|
Val tail; // pointer to one past last element name on stack
|
75
|
-
#
|
75
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
76
76
|
pthread_mutex_t mutex;
|
77
77
|
#else
|
78
78
|
VALUE mutex;
|
@@ -110,7 +110,7 @@ stack_push(ValStack stack, VALUE val, ValNext next) {
|
|
110
110
|
} else {
|
111
111
|
REALLOC_N(head, struct _val, len + STACK_INC);
|
112
112
|
}
|
113
|
-
#
|
113
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
114
114
|
pthread_mutex_lock(&stack->mutex);
|
115
115
|
#else
|
116
116
|
rb_mutex_lock(stack->mutex);
|
@@ -118,7 +118,7 @@ stack_push(ValStack stack, VALUE val, ValNext next) {
|
|
118
118
|
stack->head = head;
|
119
119
|
stack->tail = stack->head + toff;
|
120
120
|
stack->end = stack->head + len + STACK_INC;
|
121
|
-
#
|
121
|
+
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
122
122
|
pthread_mutex_unlock(&stack->mutex);
|
123
123
|
#else
|
124
124
|
rb_mutex_unlock(stack->mutex);
|
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/Options.md
CHANGED
@@ -89,6 +89,10 @@ with the key.
|
|
89
89
|
The :create_id option specifies that key is used for dumping and loading when
|
90
90
|
specifying the class for an encoded object. The default is `json_create`.
|
91
91
|
|
92
|
+
In the `:custom` mode setting the `:create_id` to nil will cause Complex,
|
93
|
+
Rational, Range, and Regexp to be output as strings instead of as JSON
|
94
|
+
objects.
|
95
|
+
|
92
96
|
### :empty_string [Boolean]
|
93
97
|
|
94
98
|
If true an empty or all whitespace input will not raise an Exception. The
|
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,8 @@ 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
82
|
|
83
83
|
### Notes:
|
84
84
|
|
@@ -87,8 +87,8 @@ Oj::Rails.set_decoder() method replaces that method with the Oj equivalent.
|
|
87
87
|
significant digits which can be either 16 or 17 depending on the value.
|
88
88
|
|
89
89
|
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
|
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
|
92
92
|
that has already been used. This could occur is a mix of String and Symbols
|
93
93
|
are used as keys or if a other non-String objects such as Numerics are mixed
|
94
94
|
with numbers as Strings.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ORIG_ARGV = ARGV.dup
|
4
|
+
|
5
|
+
require "active_support/core_ext/kernel/reporting"
|
6
|
+
|
7
|
+
silence_warnings do
|
8
|
+
Encoding.default_internal = Encoding::UTF_8
|
9
|
+
Encoding.default_external = Encoding::UTF_8
|
10
|
+
end
|
11
|
+
|
12
|
+
require "active_support/testing/autorun"
|
13
|
+
require "active_support/testing/method_call_assertions"
|
14
|
+
|
15
|
+
ENV["NO_RELOAD"] = "1"
|
16
|
+
require "active_support"
|
17
|
+
|
18
|
+
Thread.abort_on_exception = true
|
19
|
+
|
20
|
+
# Show backtraces for deprecated behavior for quicker cleanup.
|
21
|
+
ActiveSupport::Deprecation.debug = true
|
22
|
+
|
23
|
+
# Default to old to_time behavior but allow running tests with new behavior
|
24
|
+
ActiveSupport.to_time_preserves_timezone = ENV["PRESERVE_TIMEZONES"] == "1"
|
25
|
+
|
26
|
+
# Disable available locale checks to avoid warnings running the test suite.
|
27
|
+
I18n.enforce_available_locales = false
|
28
|
+
|
29
|
+
class ActiveSupport::TestCase
|
30
|
+
include ActiveSupport::Testing::MethodCallAssertions
|
31
|
+
|
32
|
+
# Skips the current run on Rubinius using Minitest::Assertions#skip
|
33
|
+
private def rubinius_skip(message = "")
|
34
|
+
skip message if RUBY_ENGINE == "rbx"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Skips the current run on JRuby using Minitest::Assertions#skip
|
38
|
+
private def jruby_skip(message = "")
|
39
|
+
skip message if defined?(JRUBY_VERSION)
|
40
|
+
end
|
41
|
+
|
42
|
+
def frozen_error_class
|
43
|
+
Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError
|
44
|
+
end
|
45
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "abstract_unit"
|
4
|
+
require "active_support/json"
|
5
|
+
require "active_support/time"
|
6
|
+
require_relative "time_zone_test_helpers"
|
5
7
|
|
6
8
|
require 'oj'
|
7
9
|
|
@@ -10,6 +12,11 @@ Oj::Rails.set_decoder()
|
|
10
12
|
class TestJSONDecoding < ActiveSupport::TestCase
|
11
13
|
include TimeZoneTestHelpers
|
12
14
|
|
15
|
+
# Added for testing if Oj is used.
|
16
|
+
test "oj is used as an encoder" do
|
17
|
+
assert_equal ActiveSupport.json_encoder, Oj::Rails::Encoder
|
18
|
+
end
|
19
|
+
|
13
20
|
class Foo
|
14
21
|
def self.json_create(object)
|
15
22
|
"Foo"
|
@@ -17,76 +24,78 @@ class TestJSONDecoding < ActiveSupport::TestCase
|
|
17
24
|
end
|
18
25
|
|
19
26
|
TESTS = {
|
20
|
-
%q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
|
21
|
-
%q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}},
|
22
|
-
%q({"returnTo":{"\/categories":1}})
|
23
|
-
%({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]},
|
24
|
-
%({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]},
|
25
|
-
%({"a": "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"},
|
26
|
-
%({"a": "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"},
|
27
|
+
%q({"returnTo":{"\/categories":"\/"}}) => { "returnTo" => { "/categories" => "/" } },
|
28
|
+
%q({"return\\"To\\":":{"\/categories":"\/"}}) => { "return\"To\":" => { "/categories" => "/" } },
|
29
|
+
%q({"returnTo":{"\/categories":1}}) => { "returnTo" => { "/categories" => 1 } },
|
30
|
+
%({"returnTo":[1,"a"]}) => { "returnTo" => [1, "a"] },
|
31
|
+
%({"returnTo":[1,"\\"a\\",", "b"]}) => { "returnTo" => [1, "\"a\",", "b"] },
|
32
|
+
%({"a": "'", "b": "5,000"}) => { "a" => "'", "b" => "5,000" },
|
33
|
+
%({"a": "a's, b's and c's", "b": "5,000"}) => { "a" => "a's, b's and c's", "b" => "5,000" },
|
27
34
|
# multibyte
|
28
|
-
%({"matzue": "松江", "asakusa": "浅草"}) => {"matzue" => "松江", "asakusa" => "浅草"},
|
29
|
-
%({"a": "2007-01-01"}) => {
|
30
|
-
%({"a": "2007-01-01 01:12:34 Z"}) => {
|
35
|
+
%({"matzue": "松江", "asakusa": "浅草"}) => { "matzue" => "松江", "asakusa" => "浅草" },
|
36
|
+
%({"a": "2007-01-01"}) => { "a" => Date.new(2007, 1, 1) },
|
37
|
+
%({"a": "2007-01-01 01:12:34 Z"}) => { "a" => Time.utc(2007, 1, 1, 1, 12, 34) },
|
31
38
|
%(["2007-01-01 01:12:34 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34)],
|
32
39
|
%(["2007-01-01 01:12:34 Z", "2007-01-01 01:12:35 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34), Time.utc(2007, 1, 1, 1, 12, 35)],
|
33
40
|
# no time zone
|
34
|
-
%({"a": "2007-01-01 01:12:34"}) => {
|
41
|
+
%({"a": "2007-01-01 01:12:34"}) => { "a" => Time.new(2007, 1, 1, 1, 12, 34, "-05:00") },
|
35
42
|
# invalid date
|
36
|
-
%({"a": "1089-10-40"}) => {
|
43
|
+
%({"a": "1089-10-40"}) => { "a" => "1089-10-40" },
|
37
44
|
# xmlschema date notation
|
38
|
-
%({"a": "2009-08-10T19:01:02"}) => {
|
39
|
-
%({"a": "2009-08-10T19:01:02Z"}) => {
|
40
|
-
%({"a": "2009-08-10T19:01:02+02:00"}) => {
|
41
|
-
%({"a": "2009-08-10T19:01:02-05:00"}) => {
|
45
|
+
%({"a": "2009-08-10T19:01:02"}) => { "a" => Time.new(2009, 8, 10, 19, 1, 2, "-04:00") },
|
46
|
+
%({"a": "2009-08-10T19:01:02Z"}) => { "a" => Time.utc(2009, 8, 10, 19, 1, 2) },
|
47
|
+
%({"a": "2009-08-10T19:01:02+02:00"}) => { "a" => Time.utc(2009, 8, 10, 17, 1, 2) },
|
48
|
+
%({"a": "2009-08-10T19:01:02-05:00"}) => { "a" => Time.utc(2009, 8, 11, 00, 1, 2) },
|
42
49
|
# needs to be *exact*
|
43
|
-
%({"a": " 2007-01-01 01:12:34 Z "}) => {
|
44
|
-
%({"a": "2007-01-01 : it's your birthday"}) => {
|
50
|
+
%({"a": " 2007-01-01 01:12:34 Z "}) => { "a" => " 2007-01-01 01:12:34 Z " },
|
51
|
+
%({"a": "2007-01-01 : it's your birthday"}) => { "a" => "2007-01-01 : it's your birthday" },
|
45
52
|
%([]) => [],
|
46
53
|
%({}) => {},
|
47
|
-
%({"a":1})
|
48
|
-
%({"a": ""})
|
49
|
-
%({"a":"\\""}) => {"a" => "\""},
|
50
|
-
%({"a": null}) => {"a" => nil},
|
51
|
-
%({"a": true}) => {"a" => true},
|
52
|
-
%({"a": false}) => {"a" => false},
|
53
|
-
|
54
|
-
%q({"a": "http:\/\/test.host\/posts\/1"}) => {"a" => "http://test.host/posts/1"},
|
55
|
-
%q({"a": "\u003cunicode\u0020escape\u003e"}) => {"a" => "<unicode escape>"},
|
56
|
-
|
57
|
-
%q({"a": "\u003cbr /\u003e"}) => {
|
58
|
-
%q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {
|
54
|
+
%({"a":1}) => { "a" => 1 },
|
55
|
+
%({"a": ""}) => { "a" => "" },
|
56
|
+
%({"a":"\\""}) => { "a" => "\"" },
|
57
|
+
%({"a": null}) => { "a" => nil },
|
58
|
+
%({"a": true}) => { "a" => true },
|
59
|
+
%({"a": false}) => { "a" => false },
|
60
|
+
'{"bad":"\\\\","trailing":""}' => { "bad" => "\\", "trailing" => "" },
|
61
|
+
%q({"a": "http:\/\/test.host\/posts\/1"}) => { "a" => "http://test.host/posts/1" },
|
62
|
+
%q({"a": "\u003cunicode\u0020escape\u003e"}) => { "a" => "<unicode escape>" },
|
63
|
+
'{"a": "\\\\u0020skip double backslashes"}' => { "a" => "\\u0020skip double backslashes" },
|
64
|
+
%q({"a": "\u003cbr /\u003e"}) => { "a" => "<br />" },
|
65
|
+
%q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => { "b" => ["<i>", "<b>", "<u>"] },
|
59
66
|
# test combination of dates and escaped or unicode encoded data in arrays
|
60
67
|
%q([{"d":"1970-01-01", "s":"\u0020escape"},{"d":"1970-01-01", "s":"\u0020escape"}]) =>
|
61
|
-
[{
|
68
|
+
[{ "d" => Date.new(1970, 1, 1), "s" => " escape" }, { "d" => Date.new(1970, 1, 1), "s" => " escape" }],
|
62
69
|
%q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) =>
|
63
|
-
[{
|
64
|
-
{
|
70
|
+
[{ "d" => Date.new(1970, 1, 1), "s" => "http://example.com" },
|
71
|
+
{ "d" => Date.new(1970, 1, 1), "s" => "http://example.com" }],
|
65
72
|
# tests escaping of "\n" char with Yaml backend
|
66
|
-
%q({"a":"\n"})
|
67
|
-
%q({"a":"\u000a"}) => {"a"=>"\n"},
|
68
|
-
%q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"},
|
73
|
+
%q({"a":"\n"}) => { "a" => "\n" },
|
74
|
+
%q({"a":"\u000a"}) => { "a" => "\n" },
|
75
|
+
%q({"a":"Line1\u000aLine2"}) => { "a" => "Line1\nLine2" },
|
69
76
|
# prevent json unmarshalling
|
70
|
-
|
77
|
+
'{"json_class":"TestJSONDecoding::Foo"}' => { "json_class" => "TestJSONDecoding::Foo" },
|
71
78
|
# json "fragments" - these are invalid JSON, but ActionPack relies on this
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
+
'"a string"' => "a string",
|
80
|
+
"1.1" => 1.1,
|
81
|
+
"1" => 1,
|
82
|
+
"-1" => -1,
|
83
|
+
"true" => true,
|
84
|
+
"false" => false,
|
85
|
+
"null" => nil
|
79
86
|
}
|
80
87
|
|
81
88
|
TESTS.each_with_index do |(json, expected), index|
|
89
|
+
fail_message = "JSON decoding failed for #{json}"
|
90
|
+
|
82
91
|
test "json decodes #{index}" do
|
83
|
-
with_tz_default
|
92
|
+
with_tz_default "Eastern Time (US & Canada)" do
|
84
93
|
with_parse_json_times(true) do
|
85
94
|
silence_warnings do
|
86
95
|
if expected.nil?
|
87
|
-
assert_nil
|
96
|
+
assert_nil ActiveSupport::JSON.decode(json), fail_message
|
88
97
|
else
|
89
|
-
assert_equal
|
98
|
+
assert_equal expected, ActiveSupport::JSON.decode(json), fail_message
|
90
99
|
end
|
91
100
|
end
|
92
101
|
end
|
@@ -96,7 +105,7 @@ class TestJSONDecoding < ActiveSupport::TestCase
|
|
96
105
|
|
97
106
|
test "json decodes time json with time parsing disabled" do
|
98
107
|
with_parse_json_times(false) do
|
99
|
-
expected = {"a" => "2007-01-01 01:12:34 Z"}
|
108
|
+
expected = { "a" => "2007-01-01 01:12:34 Z" }
|
100
109
|
assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"}))
|
101
110
|
end
|
102
111
|
end
|
@@ -114,12 +123,11 @@ class TestJSONDecoding < ActiveSupport::TestCase
|
|
114
123
|
|
115
124
|
private
|
116
125
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
126
|
+
def with_parse_json_times(value)
|
127
|
+
old_value = ActiveSupport.parse_json_times
|
128
|
+
ActiveSupport.parse_json_times = value
|
129
|
+
yield
|
130
|
+
ensure
|
131
|
+
ActiveSupport.parse_json_times = old_value
|
132
|
+
end
|
124
133
|
end
|
125
|
-
|