oj 3.13.13 → 3.13.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c59bab43c76d548574059f41aab7b1604fa74161b59bbc0e958a0919e1cdabe
4
- data.tar.gz: 8b60a10bb54b6a03f9a23b1db561a6402d1710c942f01d1f175582a08c566a9c
3
+ metadata.gz: 6b0eec52170402a3d26527fa072d57fef1647f767e8559b8462893ced4f4cf21
4
+ data.tar.gz: 93bba77447858ae77366ff7d8c05ad3199457e2b55a3584dcb6ccca3288d0d4a
5
5
  SHA512:
6
- metadata.gz: 70ea6740a38970443000f03b67a5222fd450529f6d5d8edbb613feb277ecbeb27350784de26204b962a7815b4bf9cae821cfc8753be6f23ca845d118dfa6465b
7
- data.tar.gz: 57d7f8b7f7f3d4e2ac37949e820d392840c0d3f36ff35be55c5ba0ba589a6656bf572de39c0f82482d23aa992bb3c8b9a634c106c753c077be8ef994c61c251f
6
+ metadata.gz: 9c95fa98149c6b8686376ab6d0f5744ca8a1312f62336a04c23da68d1e76c66c1d0a82ed755e945478623abfae85dac0d58ea84715d61d42dc0441455c3982d6
7
+ data.tar.gz: 00eb3b90452e1adc6cf58e4eec3be107076ee99544c7b0d70b7cff6aa9a758b9a330391669a0622f95681cf3e4dc3cf47bdc2be8496a322f1d9432f2557b052a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.13.14 - 2022-06-03
4
+
5
+ - Double fclose() due to bad merger fixed by tonobo.
6
+
3
7
  ## 3.13.13 - 2022-05-20
4
8
 
5
9
  - Fixed flooding stdout with debug output when dumping.
data/ext/oj/dump.c CHANGED
@@ -614,7 +614,6 @@ void oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
614
614
 
615
615
  oj_out_free(&out);
616
616
 
617
- fclose(f);
618
617
  if (!ok) {
619
618
  int err = ferror(f);
620
619
  fclose(f);
data/ext/oj/mimic_json.c CHANGED
@@ -460,7 +460,7 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
460
460
  if (0 == argc) {
461
461
  rb_raise(rb_eArgError, "wrong number of arguments (0))");
462
462
  }
463
- if (1 == argc) {
463
+ if (1 == argc || Qnil == argv[1]) {
464
464
  h = rb_hash_new();
465
465
  } else {
466
466
  h = argv[1];
data/lib/oj/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '3.13.13'
4
+ VERSION = '3.13.14'
5
5
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ ORIG_ARGV = ARGV.dup
4
+
5
+ require "bundler/setup"
6
+ require "active_support/core_ext/kernel/reporting"
7
+
8
+ silence_warnings do
9
+ Encoding.default_internal = Encoding::UTF_8
10
+ Encoding.default_external = Encoding::UTF_8
11
+ end
12
+
13
+ require "active_support/testing/autorun"
14
+ require "active_support/testing/method_call_assertions"
15
+
16
+ ENV["NO_RELOAD"] = "1"
17
+ require "active_support"
18
+
19
+ Thread.abort_on_exception = true
20
+
21
+ # Show backtraces for deprecated behavior for quicker cleanup.
22
+ ActiveSupport::Deprecation.debug = true
23
+
24
+ # Default to old to_time behavior but allow running tests with new behavior
25
+ ActiveSupport.to_time_preserves_timezone = ENV["PRESERVE_TIMEZONES"] == "1"
26
+
27
+ # Disable available locale checks to avoid warnings running the test suite.
28
+ I18n.enforce_available_locales = false
29
+
30
+ class ActiveSupport::TestCase
31
+ if Process.respond_to?(:fork) && !Gem.win_platform?
32
+ parallelize
33
+ else
34
+ parallelize(with: :threads)
35
+ end
36
+
37
+ include ActiveSupport::Testing::MethodCallAssertions
38
+
39
+ private
40
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
41
+ def rubinius_skip(message = "")
42
+ skip message if RUBY_ENGINE == "rbx"
43
+ end
44
+
45
+ # Skips the current run on JRuby using Minitest::Assertions#skip
46
+ def jruby_skip(message = "")
47
+ skip message if defined?(JRUBY_VERSION)
48
+ end
49
+ end
@@ -0,0 +1,125 @@
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"
7
+
8
+ class TestJSONDecoding < ActiveSupport::TestCase
9
+ include TimeZoneTestHelpers
10
+
11
+ class Foo
12
+ def self.json_create(object)
13
+ "Foo"
14
+ end
15
+ end
16
+
17
+ TESTS = {
18
+ %q({"returnTo":{"\/categories":"\/"}}) => { "returnTo" => { "/categories" => "/" } },
19
+ %q({"return\\"To\\":":{"\/categories":"\/"}}) => { "return\"To\":" => { "/categories" => "/" } },
20
+ %q({"returnTo":{"\/categories":1}}) => { "returnTo" => { "/categories" => 1 } },
21
+ %({"returnTo":[1,"a"]}) => { "returnTo" => [1, "a"] },
22
+ %({"returnTo":[1,"\\"a\\",", "b"]}) => { "returnTo" => [1, "\"a\",", "b"] },
23
+ %({"a": "'", "b": "5,000"}) => { "a" => "'", "b" => "5,000" },
24
+ %({"a": "a's, b's and c's", "b": "5,000"}) => { "a" => "a's, b's and c's", "b" => "5,000" },
25
+ # multibyte
26
+ %({"matzue": "松江", "asakusa": "浅草"}) => { "matzue" => "松江", "asakusa" => "浅草" },
27
+ %({"a": "2007-01-01"}) => { "a" => Date.new(2007, 1, 1) },
28
+ %({"a": "2007-01-01 01:12:34 Z"}) => { "a" => Time.utc(2007, 1, 1, 1, 12, 34) },
29
+ %(["2007-01-01 01:12:34 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34)],
30
+ %(["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)],
31
+ # no time zone
32
+ %({"a": "2007-01-01 01:12:34"}) => { "a" => Time.new(2007, 1, 1, 1, 12, 34, "-05:00") },
33
+ # invalid date
34
+ %({"a": "1089-10-40"}) => { "a" => "1089-10-40" },
35
+ # xmlschema date notation
36
+ %({"a": "2009-08-10T19:01:02"}) => { "a" => Time.new(2009, 8, 10, 19, 1, 2, "-04:00") },
37
+ %({"a": "2009-08-10T19:01:02Z"}) => { "a" => Time.utc(2009, 8, 10, 19, 1, 2) },
38
+ %({"a": "2009-08-10T19:01:02+02:00"}) => { "a" => Time.utc(2009, 8, 10, 17, 1, 2) },
39
+ %({"a": "2009-08-10T19:01:02-05:00"}) => { "a" => Time.utc(2009, 8, 11, 00, 1, 2) },
40
+ # needs to be *exact*
41
+ %({"a": " 2007-01-01 01:12:34 Z "}) => { "a" => " 2007-01-01 01:12:34 Z " },
42
+ %({"a": "2007-01-01 : it's your birthday"}) => { "a" => "2007-01-01 : it's your birthday" },
43
+ %({"a": "Today is:\\n2020-05-21"}) => { "a" => "Today is:\n2020-05-21" },
44
+ %({"a": "2007-01-01 01:12:34 Z\\nwas my birthday"}) => { "a" => "2007-01-01 01:12:34 Z\nwas my birthday" },
45
+ %([]) => [],
46
+ %({}) => {},
47
+ %({"a":1}) => { "a" => 1 },
48
+ %({"a": ""}) => { "a" => "" },
49
+ %({"a":"\\""}) => { "a" => "\"" },
50
+ %({"a": null}) => { "a" => nil },
51
+ %({"a": true}) => { "a" => true },
52
+ %({"a": false}) => { "a" => false },
53
+ '{"bad":"\\\\","trailing":""}' => { "bad" => "\\", "trailing" => "" },
54
+ %q({"a": "http:\/\/test.host\/posts\/1"}) => { "a" => "http://test.host/posts/1" },
55
+ %q({"a": "\u003cunicode\u0020escape\u003e"}) => { "a" => "<unicode escape>" },
56
+ '{"a": "\\\\u0020skip double backslashes"}' => { "a" => "\\u0020skip double backslashes" },
57
+ %q({"a": "\u003cbr /\u003e"}) => { "a" => "<br />" },
58
+ %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => { "b" => ["<i>", "<b>", "<u>"] },
59
+ # test combination of dates and escaped or unicode encoded data in arrays
60
+ %q([{"d":"1970-01-01", "s":"\u0020escape"},{"d":"1970-01-01", "s":"\u0020escape"}]) =>
61
+ [{ "d" => Date.new(1970, 1, 1), "s" => " escape" }, { "d" => Date.new(1970, 1, 1), "s" => " escape" }],
62
+ %q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) =>
63
+ [{ "d" => Date.new(1970, 1, 1), "s" => "http://example.com" },
64
+ { "d" => Date.new(1970, 1, 1), "s" => "http://example.com" }],
65
+ # tests escaping of "\n" char with Yaml backend
66
+ %q({"a":"\n"}) => { "a" => "\n" },
67
+ %q({"a":"\u000a"}) => { "a" => "\n" },
68
+ %q({"a":"Line1\u000aLine2"}) => { "a" => "Line1\nLine2" },
69
+ # prevent json unmarshalling
70
+ '{"json_class":"TestJSONDecoding::Foo"}' => { "json_class" => "TestJSONDecoding::Foo" },
71
+ # json "fragments" - these are invalid JSON, but ActionPack relies on this
72
+ '"a string"' => "a string",
73
+ "1.1" => 1.1,
74
+ "1" => 1,
75
+ "-1" => -1,
76
+ "true" => true,
77
+ "false" => false,
78
+ "null" => nil
79
+ }
80
+
81
+ TESTS.each_with_index do |(json, expected), index|
82
+ fail_message = "JSON decoding failed for #{json}"
83
+
84
+ test "json decodes #{index}" do
85
+ with_tz_default "Eastern Time (US & Canada)" do
86
+ with_parse_json_times(true) do
87
+ silence_warnings do
88
+ if expected.nil?
89
+ assert_nil ActiveSupport::JSON.decode(json), fail_message
90
+ else
91
+ assert_equal expected, ActiveSupport::JSON.decode(json), fail_message
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ test "json decodes time json with time parsing disabled" do
100
+ with_parse_json_times(false) do
101
+ expected = { "a" => "2007-01-01 01:12:34 Z" }
102
+ assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"}))
103
+ end
104
+ end
105
+
106
+ def test_failed_json_decoding
107
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%(undefined)) }
108
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({a: 1})) }
109
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({: 1})) }
110
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%()) }
111
+ end
112
+
113
+ def test_cannot_pass_unsupported_options
114
+ assert_raise(ArgumentError) { ActiveSupport::JSON.decode("", create_additions: true) }
115
+ end
116
+
117
+ private
118
+ def with_parse_json_times(value)
119
+ old_value = ActiveSupport.parse_json_times
120
+ ActiveSupport.parse_json_times = value
121
+ yield
122
+ ensure
123
+ ActiveSupport.parse_json_times = old_value
124
+ end
125
+ end
@@ -0,0 +1,486 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require_relative "abstract_unit"
5
+ require "active_support/core_ext/string/inflections"
6
+ require "active_support/json"
7
+ require "active_support/time"
8
+ require_relative "time_zone_test_helpers"
9
+ require_relative "encoding_test_cases"
10
+
11
+ class TestJSONEncoding < ActiveSupport::TestCase
12
+ include TimeZoneTestHelpers
13
+
14
+ def sorted_json(json)
15
+ if json.start_with?("{") && json.end_with?("}")
16
+ "{" + json[1..-2].split(",").sort.join(",") + "}"
17
+ else
18
+ json
19
+ end
20
+ end
21
+
22
+ JSONTest::EncodingTestCases.constants.each do |class_tests|
23
+ define_method("test_#{class_tests[0..-6].underscore}") do
24
+ prev = ActiveSupport.use_standard_json_time_format
25
+
26
+ standard_class_tests = /Standard/.match?(class_tests)
27
+
28
+ ActiveSupport.escape_html_entities_in_json = !standard_class_tests
29
+ ActiveSupport.use_standard_json_time_format = standard_class_tests
30
+ JSONTest::EncodingTestCases.const_get(class_tests).each do |pair|
31
+ assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
32
+ end
33
+ ensure
34
+ ActiveSupport.escape_html_entities_in_json = false
35
+ ActiveSupport.use_standard_json_time_format = prev
36
+ end
37
+ end
38
+
39
+ def test_process_status
40
+ rubinius_skip "https://github.com/rubinius/rubinius/issues/3334"
41
+
42
+ # There doesn't seem to be a good way to get a handle on a Process::Status object without actually
43
+ # creating a child process, hence this to populate $?
44
+ system("not_a_real_program_#{SecureRandom.hex}")
45
+ assert_equal %({"exitstatus":#{$?.exitstatus},"pid":#{$?.pid}}), ActiveSupport::JSON.encode($?)
46
+ end
47
+
48
+ def test_hash_encoding
49
+ assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(a: :b)
50
+ assert_equal %({\"a\":1}), ActiveSupport::JSON.encode("a" => 1)
51
+ assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode("a" => [1, 2])
52
+ assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2)
53
+
54
+ assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(a: :b, c: :d))
55
+ end
56
+
57
+ def test_hash_keys_encoding
58
+ ActiveSupport.escape_html_entities_in_json = true
59
+ assert_equal "{\"\\u003c\\u003e\":\"\\u003c\\u003e\"}", ActiveSupport::JSON.encode("<>" => "<>")
60
+ ensure
61
+ ActiveSupport.escape_html_entities_in_json = false
62
+ end
63
+
64
+ def test_utf8_string_encoded_properly
65
+ result = ActiveSupport::JSON.encode("€2.99")
66
+ assert_equal '"€2.99"', result
67
+ assert_equal(Encoding::UTF_8, result.encoding)
68
+
69
+ result = ActiveSupport::JSON.encode("✎☺")
70
+ assert_equal '"✎☺"', result
71
+ assert_equal(Encoding::UTF_8, result.encoding)
72
+ end
73
+
74
+ def test_non_utf8_string_transcodes
75
+ s = "二".encode("Shift_JIS")
76
+ result = ActiveSupport::JSON.encode(s)
77
+ assert_equal '"二"', result
78
+ assert_equal Encoding::UTF_8, result.encoding
79
+ end
80
+
81
+ def test_wide_utf8_chars
82
+ w = "𠜎"
83
+ result = ActiveSupport::JSON.encode(w)
84
+ assert_equal '"𠜎"', result
85
+ end
86
+
87
+ def test_wide_utf8_roundtrip
88
+ hash = { string: "𐒑" }
89
+ json = ActiveSupport::JSON.encode(hash)
90
+ decoded_hash = ActiveSupport::JSON.decode(json)
91
+ assert_equal "𐒑", decoded_hash["string"]
92
+ end
93
+
94
+ def test_hash_key_identifiers_are_always_quoted
95
+ values = { 0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B" }
96
+ assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
97
+ end
98
+
99
+ def test_hash_should_allow_key_filtering_with_only
100
+ assert_equal %({"a":1}), ActiveSupport::JSON.encode({ "a" => 1, :b => 2, :c => 3 }, { only: "a" })
101
+ end
102
+
103
+ def test_hash_should_allow_key_filtering_with_except
104
+ assert_equal %({"b":2}), ActiveSupport::JSON.encode({ "foo" => "bar", :b => 2, :c => 3 }, { except: ["foo", :c] })
105
+ end
106
+
107
+ def test_time_to_json_includes_local_offset
108
+ with_standard_json_time_format(true) do
109
+ with_env_tz "US/Eastern" do
110
+ assert_equal %("2005-02-01T15:15:10.000-05:00"), ActiveSupport::JSON.encode(Time.local(2005, 2, 1, 15, 15, 10))
111
+ end
112
+ end
113
+ end
114
+
115
+ def test_hash_with_time_to_json
116
+ with_standard_json_time_format(false) do
117
+ assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { time: Time.utc(2009) }.to_json
118
+ end
119
+ end
120
+
121
+ def test_nested_hash_with_float
122
+ assert_nothing_raised do
123
+ hash = {
124
+ "CHI" => {
125
+ display_name: "chicago",
126
+ latitude: 123.234
127
+ }
128
+ }
129
+ ActiveSupport::JSON.encode(hash)
130
+ end
131
+ end
132
+
133
+ def test_hash_like_with_options
134
+ h = JSONTest::Hashlike.new
135
+ json = h.to_json only: [:foo]
136
+
137
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
138
+ end
139
+
140
+ def test_object_to_json_with_options
141
+ obj = Object.new
142
+ obj.instance_variable_set :@foo, "hello"
143
+ obj.instance_variable_set :@bar, "world"
144
+ json = obj.to_json only: ["foo"]
145
+
146
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
147
+ end
148
+
149
+ def test_struct_to_json_with_options
150
+ struct = Struct.new(:foo, :bar).new
151
+ struct.foo = "hello"
152
+ struct.bar = "world"
153
+ json = struct.to_json only: [:foo]
154
+
155
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
156
+ end
157
+
158
+ def test_struct_to_json_with_options_nested
159
+ klass = Struct.new(:foo, :bar)
160
+ struct = klass.new "hello", "world"
161
+ parent_struct = klass.new struct, "world"
162
+ json = parent_struct.to_json only: [:foo]
163
+
164
+ assert_equal({ "foo" => { "foo" => "hello" } }, JSON.parse(json))
165
+ end
166
+
167
+ def test_hash_should_pass_encoding_options_to_children_in_as_json
168
+ person = {
169
+ name: "John",
170
+ address: {
171
+ city: "London",
172
+ country: "UK"
173
+ }
174
+ }
175
+ json = person.as_json only: [:address, :city]
176
+
177
+ assert_equal({ "address" => { "city" => "London" } }, json)
178
+ end
179
+
180
+ def test_hash_should_pass_encoding_options_to_children_in_to_json
181
+ person = {
182
+ name: "John",
183
+ address: {
184
+ city: "London",
185
+ country: "UK"
186
+ }
187
+ }
188
+ json = person.to_json only: [:address, :city]
189
+
190
+ assert_equal(%({"address":{"city":"London"}}), json)
191
+ end
192
+
193
+ def test_array_should_pass_encoding_options_to_children_in_as_json
194
+ people = [
195
+ { name: "John", address: { city: "London", country: "UK" } },
196
+ { name: "Jean", address: { city: "Paris", country: "France" } }
197
+ ]
198
+ json = people.as_json only: [:address, :city]
199
+ expected = [
200
+ { "address" => { "city" => "London" } },
201
+ { "address" => { "city" => "Paris" } }
202
+ ]
203
+
204
+ assert_equal(expected, json)
205
+ end
206
+
207
+ def test_array_should_pass_encoding_options_to_children_in_to_json
208
+ people = [
209
+ { name: "John", address: { city: "London", country: "UK" } },
210
+ { name: "Jean", address: { city: "Paris", country: "France" } }
211
+ ]
212
+ json = people.to_json only: [:address, :city]
213
+
214
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
215
+ end
216
+
217
+ People = Class.new(BasicObject) do
218
+ include Enumerable
219
+ def initialize
220
+ @people = [
221
+ { name: "John", address: { city: "London", country: "UK" } },
222
+ { name: "Jean", address: { city: "Paris", country: "France" } }
223
+ ]
224
+ end
225
+ def each(*, &blk)
226
+ @people.each do |p|
227
+ yield p if blk
228
+ p
229
+ end.each
230
+ end
231
+ end
232
+
233
+ def test_enumerable_should_generate_json_with_as_json
234
+ json = People.new.as_json only: [:address, :city]
235
+ expected = [
236
+ { "address" => { "city" => "London" } },
237
+ { "address" => { "city" => "Paris" } }
238
+ ]
239
+
240
+ assert_equal(expected, json)
241
+ end
242
+
243
+ def test_enumerable_should_generate_json_with_to_json
244
+ json = People.new.to_json only: [:address, :city]
245
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
246
+ end
247
+
248
+ def test_enumerable_should_pass_encoding_options_to_children_in_as_json
249
+ json = People.new.each.as_json only: [:address, :city]
250
+ expected = [
251
+ { "address" => { "city" => "London" } },
252
+ { "address" => { "city" => "Paris" } }
253
+ ]
254
+
255
+ assert_equal(expected, json)
256
+ end
257
+
258
+ def test_enumerable_should_pass_encoding_options_to_children_in_to_json
259
+ json = People.new.each.to_json only: [:address, :city]
260
+
261
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
262
+ end
263
+
264
+ class CustomWithOptions
265
+ attr_accessor :foo, :bar
266
+
267
+ def as_json(options = {})
268
+ options[:only] = %w(foo bar)
269
+ super(options)
270
+ end
271
+ end
272
+
273
+ def test_hash_to_json_should_not_keep_options_around
274
+ f = CustomWithOptions.new
275
+ f.foo = "hello"
276
+ f.bar = "world"
277
+
278
+ hash = { "foo" => f, "other_hash" => { "foo" => "other_foo", "test" => "other_test" } }
279
+ assert_equal({ "foo" => { "foo" => "hello", "bar" => "world" },
280
+ "other_hash" => { "foo" => "other_foo", "test" => "other_test" } }, ActiveSupport::JSON.decode(hash.to_json))
281
+ end
282
+
283
+ def test_array_to_json_should_not_keep_options_around
284
+ f = CustomWithOptions.new
285
+ f.foo = "hello"
286
+ f.bar = "world"
287
+
288
+ array = [f, { "foo" => "other_foo", "test" => "other_test" }]
289
+ assert_equal([{ "foo" => "hello", "bar" => "world" },
290
+ { "foo" => "other_foo", "test" => "other_test" }], ActiveSupport::JSON.decode(array.to_json))
291
+ end
292
+
293
+ class OptionsTest
294
+ def as_json(options = :default)
295
+ options
296
+ end
297
+ end
298
+
299
+ def test_hash_as_json_without_options
300
+ json = { foo: OptionsTest.new }.as_json
301
+ assert_equal({ "foo" => :default }, json)
302
+ end
303
+
304
+ def test_array_as_json_without_options
305
+ json = [ OptionsTest.new ].as_json
306
+ assert_equal([:default], json)
307
+ end
308
+
309
+ def test_struct_encoding
310
+ Struct.new("UserNameAndEmail", :name, :email)
311
+ Struct.new("UserNameAndDate", :name, :date)
312
+ Struct.new("Custom", :name, :sub)
313
+ user_email = Struct::UserNameAndEmail.new "David", "sample@example.com"
314
+ user_birthday = Struct::UserNameAndDate.new "David", Date.new(2010, 01, 01)
315
+ custom = Struct::Custom.new "David", user_birthday
316
+
317
+ json_strings = ""
318
+ json_string_and_date = ""
319
+ json_custom = ""
320
+
321
+ assert_nothing_raised do
322
+ json_strings = user_email.to_json
323
+ json_string_and_date = user_birthday.to_json
324
+ json_custom = custom.to_json
325
+ end
326
+
327
+ assert_equal({ "name" => "David",
328
+ "sub" => {
329
+ "name" => "David",
330
+ "date" => "2010-01-01" } }, ActiveSupport::JSON.decode(json_custom))
331
+
332
+ assert_equal({ "name" => "David", "email" => "sample@example.com" },
333
+ ActiveSupport::JSON.decode(json_strings))
334
+
335
+ assert_equal({ "name" => "David", "date" => "2010-01-01" },
336
+ ActiveSupport::JSON.decode(json_string_and_date))
337
+ end
338
+
339
+ def test_nil_true_and_false_represented_as_themselves
340
+ assert_nil nil.as_json
341
+ assert_equal true, true.as_json
342
+ assert_equal false, false.as_json
343
+ end
344
+
345
+ class HashWithAsJson < Hash
346
+ attr_accessor :as_json_called
347
+
348
+ def initialize(*)
349
+ super
350
+ end
351
+
352
+ def as_json(options = {})
353
+ @as_json_called = true
354
+ super
355
+ end
356
+ end
357
+
358
+ def test_json_gem_dump_by_passing_active_support_encoder
359
+ h = HashWithAsJson.new
360
+ h[:foo] = "hello"
361
+ h[:bar] = "world"
362
+
363
+ assert_equal %({"foo":"hello","bar":"world"}), JSON.dump(h)
364
+ assert_nil h.as_json_called
365
+ end
366
+
367
+ def test_json_gem_generate_by_passing_active_support_encoder
368
+ h = HashWithAsJson.new
369
+ h[:foo] = "hello"
370
+ h[:bar] = "world"
371
+
372
+ assert_equal %({"foo":"hello","bar":"world"}), JSON.generate(h)
373
+ assert_nil h.as_json_called
374
+ end
375
+
376
+ def test_json_gem_pretty_generate_by_passing_active_support_encoder
377
+ h = HashWithAsJson.new
378
+ h[:foo] = "hello"
379
+ h[:bar] = "world"
380
+
381
+ assert_equal <<EXPECTED.chomp, JSON.pretty_generate(h)
382
+ {
383
+ "foo": "hello",
384
+ "bar": "world"
385
+ }
386
+ EXPECTED
387
+ assert_nil h.as_json_called
388
+ end
389
+
390
+ def test_twz_to_json_with_use_standard_json_time_format_config_set_to_false
391
+ with_standard_json_time_format(false) do
392
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
393
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
394
+ assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(time)
395
+ end
396
+ end
397
+
398
+ def test_twz_to_json_with_use_standard_json_time_format_config_set_to_true
399
+ with_standard_json_time_format(true) do
400
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
401
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
402
+ assert_equal "\"1999-12-31T19:00:00.000-05:00\"", ActiveSupport::JSON.encode(time)
403
+ end
404
+ end
405
+
406
+ def test_twz_to_json_with_custom_time_precision
407
+ with_standard_json_time_format(true) do
408
+ with_time_precision(0) do
409
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
410
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
411
+ assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(time)
412
+ end
413
+ end
414
+ end
415
+
416
+ def test_time_to_json_with_custom_time_precision
417
+ with_standard_json_time_format(true) do
418
+ with_time_precision(0) do
419
+ assert_equal "\"2000-01-01T00:00:00Z\"", ActiveSupport::JSON.encode(Time.utc(2000))
420
+ end
421
+ end
422
+ end
423
+
424
+ def test_datetime_to_json_with_custom_time_precision
425
+ with_standard_json_time_format(true) do
426
+ with_time_precision(0) do
427
+ assert_equal "\"2000-01-01T00:00:00+00:00\"", ActiveSupport::JSON.encode(DateTime.new(2000))
428
+ end
429
+ end
430
+ end
431
+
432
+ def test_twz_to_json_when_wrapping_a_date_time
433
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
434
+ time = ActiveSupport::TimeWithZone.new(DateTime.new(2000), zone)
435
+ assert_equal '"1999-12-31T19:00:00.000-05:00"', ActiveSupport::JSON.encode(time)
436
+ end
437
+
438
+ def test_exception_to_json
439
+ exception = Exception.new("foo")
440
+ assert_equal '"foo"', ActiveSupport::JSON.encode(exception)
441
+ end
442
+
443
+ class InfiniteNumber
444
+ def as_json(options = nil)
445
+ { "number" => Float::INFINITY }
446
+ end
447
+ end
448
+
449
+ def test_to_json_works_when_as_json_returns_infinite_number
450
+ assert_equal '{"number":null}', InfiniteNumber.new.to_json
451
+ end
452
+
453
+ class NaNNumber
454
+ def as_json(options = nil)
455
+ { "number" => Float::NAN }
456
+ end
457
+ end
458
+
459
+ def test_to_json_works_when_as_json_returns_NaN_number
460
+ assert_equal '{"number":null}', NaNNumber.new.to_json
461
+ end
462
+
463
+ def test_to_json_works_on_io_objects
464
+ assert_equal STDOUT.to_s.to_json, STDOUT.to_json
465
+ end
466
+
467
+ private
468
+ def object_keys(json_object)
469
+ json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
470
+ end
471
+
472
+ def with_standard_json_time_format(boolean = true)
473
+ old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, boolean
474
+ yield
475
+ ensure
476
+ ActiveSupport.use_standard_json_time_format = old
477
+ end
478
+
479
+ def with_time_precision(value)
480
+ old_value = ActiveSupport::JSON::Encoding.time_precision
481
+ ActiveSupport::JSON::Encoding.time_precision = value
482
+ yield
483
+ ensure
484
+ ActiveSupport::JSON::Encoding.time_precision = old_value
485
+ end
486
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+ require "date"
5
+ require "time"
6
+ require "pathname"
7
+ require "uri"
8
+
9
+ module JSONTest
10
+ class Foo
11
+ def initialize(a, b)
12
+ @a, @b = a, b
13
+ end
14
+ end
15
+
16
+ class Hashlike
17
+ def to_hash
18
+ { foo: "hello", bar: "world" }
19
+ end
20
+ end
21
+
22
+ class Custom
23
+ def initialize(serialized)
24
+ @serialized = serialized
25
+ end
26
+
27
+ def as_json(options = nil)
28
+ @serialized
29
+ end
30
+ end
31
+
32
+ MyStruct = Struct.new(:name, :value) do
33
+ def initialize(*)
34
+ @unused = "unused instance variable"
35
+ super
36
+ end
37
+ end
38
+
39
+ module EncodingTestCases
40
+ TrueTests = [[ true, %(true) ]]
41
+ FalseTests = [[ false, %(false) ]]
42
+ NilTests = [[ nil, %(null) ]]
43
+ NumericTests = [[ 1, %(1) ],
44
+ [ 2.5, %(2.5) ],
45
+ [ 0.0 / 0.0, %(null) ],
46
+ [ 1.0 / 0.0, %(null) ],
47
+ [ -1.0 / 0.0, %(null) ],
48
+ [ BigDecimal("0.0") / BigDecimal("0.0"), %(null) ],
49
+ [ BigDecimal("2.5"), %("#{BigDecimal('2.5')}") ]]
50
+
51
+ StringTests = [[ "this is the <string>", %("this is the \\u003cstring\\u003e")],
52
+ [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
53
+ [ "http://test.host/posts/1", %("http://test.host/posts/1")],
54
+ [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\u2028\u2029",
55
+ %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\\u2028\\u2029") ]]
56
+
57
+ ArrayTests = [[ ["a", "b", "c"], %([\"a\",\"b\",\"c\"]) ],
58
+ [ [1, "a", :b, nil, false], %([1,\"a\",\"b\",null,false]) ]]
59
+
60
+ HashTests = [[ { foo: "bar" }, %({\"foo\":\"bar\"}) ],
61
+ [ { 1 => 1, 2 => "a", 3 => :b, 4 => nil, 5 => false }, %({\"1\":1,\"2\":\"a\",\"3\":\"b\",\"4\":null,\"5\":false}) ]]
62
+
63
+ RangeTests = [[ 1..2, %("1..2")],
64
+ [ 1...2, %("1...2")],
65
+ [ 1.5..2.5, %("1.5..2.5")]]
66
+
67
+ SymbolTests = [[ :a, %("a") ],
68
+ [ :this, %("this") ],
69
+ [ :"a b", %("a b") ]]
70
+
71
+ ModuleTests = [[ Module, %("Module") ],
72
+ [ Class, %("Class") ],
73
+ [ ActiveSupport, %("ActiveSupport") ],
74
+ [ ActiveSupport::MessageEncryptor, %("ActiveSupport::MessageEncryptor") ]]
75
+ ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]]
76
+ HashlikeTests = [[ Hashlike.new, %({\"bar\":\"world\",\"foo\":\"hello\"}) ]]
77
+ StructTests = [[ MyStruct.new(:foo, "bar"), %({\"name\":\"foo\",\"value\":\"bar\"}) ],
78
+ [ MyStruct.new(nil, nil), %({\"name\":null,\"value\":null}) ]]
79
+ CustomTests = [[ Custom.new("custom"), '"custom"' ],
80
+ [ Custom.new(nil), "null" ],
81
+ [ Custom.new(:a), '"a"' ],
82
+ [ Custom.new([ :foo, "bar" ]), '["foo","bar"]' ],
83
+ [ Custom.new(foo: "hello", bar: "world"), '{"bar":"world","foo":"hello"}' ],
84
+ [ Custom.new(Hashlike.new), '{"bar":"world","foo":"hello"}' ],
85
+ [ Custom.new(Custom.new(Custom.new(:a))), '"a"' ]]
86
+
87
+ RegexpTests = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']]
88
+
89
+ URITests = [[ URI.parse("http://example.com"), %("http://example.com") ]]
90
+
91
+ PathnameTests = [[ Pathname.new("lib/index.rb"), %("lib/index.rb") ]]
92
+
93
+ IPAddrTests = [[ IPAddr.new("127.0.0.1"), %("127.0.0.1") ]]
94
+
95
+ DateTests = [[ Date.new(2005, 2, 1), %("2005/02/01") ]]
96
+ TimeTests = [[ Time.utc(2005, 2, 1, 15, 15, 10), %("2005/02/01 15:15:10 +0000") ]]
97
+ DateTimeTests = [[ DateTime.civil(2005, 2, 1, 15, 15, 10), %("2005/02/01 15:15:10 +0000") ]]
98
+
99
+ StandardDateTests = [[ Date.new(2005, 2, 1), %("2005-02-01") ]]
100
+ StandardTimeTests = [[ Time.utc(2005, 2, 1, 15, 15, 10), %("2005-02-01T15:15:10.000Z") ]]
101
+ StandardDateTimeTests = [[ DateTime.civil(2005, 2, 1, 15, 15, 10), %("2005-02-01T15:15:10.000+00:00") ]]
102
+ StandardStringTests = [[ "this is the <string>", %("this is the <string>")]]
103
+ end
104
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TimeZoneTestHelpers
4
+ def with_tz_default(tz = nil)
5
+ old_tz = Time.zone
6
+ Time.zone = tz
7
+ yield
8
+ ensure
9
+ Time.zone = old_tz
10
+ end
11
+
12
+ def with_env_tz(new_tz = "US/Eastern")
13
+ old_tz, ENV["TZ"] = ENV["TZ"], new_tz
14
+ yield
15
+ ensure
16
+ old_tz ? ENV["TZ"] = old_tz : ENV.delete("TZ")
17
+ end
18
+
19
+ def with_preserve_timezone(value)
20
+ old_preserve_tz = ActiveSupport.to_time_preserves_timezone
21
+ ActiveSupport.to_time_preserves_timezone = value
22
+ yield
23
+ ensure
24
+ ActiveSupport.to_time_preserves_timezone = old_preserve_tz
25
+ end
26
+
27
+ def with_tz_mappings(mappings)
28
+ old_mappings = ActiveSupport::TimeZone::MAPPING.dup
29
+ ActiveSupport::TimeZone.clear
30
+ ActiveSupport::TimeZone::MAPPING.clear
31
+ ActiveSupport::TimeZone::MAPPING.merge!(mappings)
32
+
33
+ yield
34
+ ensure
35
+ ActiveSupport::TimeZone.clear
36
+ ActiveSupport::TimeZone::MAPPING.clear
37
+ ActiveSupport::TimeZone::MAPPING.merge!(old_mappings)
38
+ end
39
+
40
+ def with_utc_to_local_returns_utc_offset_times(value)
41
+ old_tzinfo2_format = ActiveSupport.utc_to_local_returns_utc_offset_times
42
+ ActiveSupport.utc_to_local_returns_utc_offset_times = value
43
+ yield
44
+ ensure
45
+ ActiveSupport.utc_to_local_returns_utc_offset_times = old_tzinfo2_format
46
+ end
47
+ end
@@ -72,6 +72,8 @@ EOT
72
72
  parsed_json = JSON.parse(json)
73
73
  assert_equal({"1"=>2}, parsed_json)
74
74
  assert_equal '666', JSON.pretty_generate(666)
75
+ json_nil_opts = JSON.pretty_generate({1=>2}, nil)
76
+ assert_equal json, json_nil_opts
75
77
  end
76
78
 
77
79
  def test_generate_custom
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.13
4
+ version: 3.13.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-20 00:00:00.000000000 Z
11
+ date: 2022-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -58,20 +58,6 @@ dependencies:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '3.0'
61
- - !ruby/object:Gem::Dependency
62
- name: wwtd
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '0'
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '0'
75
61
  description: The fastest JSON parser and object serializer.
76
62
  email: peter@ohler.com
77
63
  executables: []
@@ -200,6 +186,11 @@ files:
200
186
  - test/activesupport6/test_common.rb
201
187
  - test/activesupport6/test_helper.rb
202
188
  - test/activesupport6/time_zone_test_helpers.rb
189
+ - test/activesupport7/abstract_unit.rb
190
+ - test/activesupport7/decoding_test.rb
191
+ - test/activesupport7/encoding_test.rb
192
+ - test/activesupport7/encoding_test_cases.rb
193
+ - test/activesupport7/time_zone_test_helpers.rb
203
194
  - test/bar.rb
204
195
  - test/baz.rb
205
196
  - test/bug.rb
@@ -333,6 +324,11 @@ test_files:
333
324
  - test/activesupport6/test_common.rb
334
325
  - test/activesupport6/test_helper.rb
335
326
  - test/activesupport6/time_zone_test_helpers.rb
327
+ - test/activesupport7/abstract_unit.rb
328
+ - test/activesupport7/decoding_test.rb
329
+ - test/activesupport7/encoding_test.rb
330
+ - test/activesupport7/encoding_test_cases.rb
331
+ - test/activesupport7/time_zone_test_helpers.rb
336
332
  - test/bar.rb
337
333
  - test/baz.rb
338
334
  - test/bug.rb