oj 3.13.13 → 3.13.14

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 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