oj 2.18.3 → 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.
Files changed (182) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1324 -0
  3. data/README.md +51 -204
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +49 -72
  6. data/ext/oj/cache.c +326 -0
  7. data/ext/oj/cache.h +21 -0
  8. data/ext/oj/cache8.c +61 -64
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +37 -68
  11. data/ext/oj/circarray.h +16 -42
  12. data/ext/oj/code.c +221 -0
  13. data/ext/oj/code.h +40 -0
  14. data/ext/oj/compat.c +231 -107
  15. data/ext/oj/custom.c +1125 -0
  16. data/ext/oj/debug.c +132 -0
  17. data/ext/oj/dump.c +935 -2513
  18. data/ext/oj/dump.h +108 -0
  19. data/ext/oj/dump_compat.c +936 -0
  20. data/ext/oj/dump_leaf.c +164 -0
  21. data/ext/oj/dump_object.c +761 -0
  22. data/ext/oj/dump_strict.c +410 -0
  23. data/ext/oj/encode.h +7 -42
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +40 -54
  26. data/ext/oj/err.h +52 -46
  27. data/ext/oj/extconf.rb +21 -30
  28. data/ext/oj/fast.c +1097 -1080
  29. data/ext/oj/intern.c +301 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +893 -0
  32. data/ext/oj/object.c +549 -620
  33. data/ext/oj/odd.c +155 -167
  34. data/ext/oj/odd.h +37 -63
  35. data/ext/oj/oj.c +1661 -2063
  36. data/ext/oj/oj.h +341 -270
  37. data/ext/oj/parse.c +974 -737
  38. data/ext/oj/parse.h +105 -97
  39. data/ext/oj/parser.c +1526 -0
  40. data/ext/oj/parser.h +90 -0
  41. data/ext/oj/rails.c +1504 -0
  42. data/ext/oj/rails.h +18 -0
  43. data/ext/oj/reader.c +141 -163
  44. data/ext/oj/reader.h +75 -113
  45. data/ext/oj/resolve.c +45 -93
  46. data/ext/oj/resolve.h +7 -34
  47. data/ext/oj/rxclass.c +143 -0
  48. data/ext/oj/rxclass.h +26 -0
  49. data/ext/oj/saj.c +447 -511
  50. data/ext/oj/saj2.c +348 -0
  51. data/ext/oj/scp.c +91 -138
  52. data/ext/oj/sparse.c +793 -644
  53. data/ext/oj/stream_writer.c +331 -0
  54. data/ext/oj/strict.c +145 -109
  55. data/ext/oj/string_writer.c +493 -0
  56. data/ext/oj/trace.c +72 -0
  57. data/ext/oj/trace.h +28 -0
  58. data/ext/oj/usual.c +1254 -0
  59. data/ext/oj/util.c +136 -0
  60. data/ext/oj/util.h +20 -0
  61. data/ext/oj/val_stack.c +62 -70
  62. data/ext/oj/val_stack.h +95 -129
  63. data/ext/oj/validate.c +51 -0
  64. data/ext/oj/wab.c +622 -0
  65. data/lib/oj/bag.rb +1 -0
  66. data/lib/oj/easy_hash.rb +17 -8
  67. data/lib/oj/error.rb +10 -11
  68. data/lib/oj/json.rb +176 -0
  69. data/lib/oj/mimic.rb +158 -19
  70. data/lib/oj/state.rb +132 -0
  71. data/lib/oj/version.rb +2 -2
  72. data/lib/oj.rb +1 -31
  73. data/pages/Advanced.md +22 -0
  74. data/pages/Compatibility.md +25 -0
  75. data/pages/Custom.md +23 -0
  76. data/pages/Encoding.md +65 -0
  77. data/pages/JsonGem.md +94 -0
  78. data/pages/Modes.md +161 -0
  79. data/pages/Options.md +327 -0
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +167 -0
  82. data/pages/Security.md +20 -0
  83. data/pages/WAB.md +13 -0
  84. data/test/activerecord/result_test.rb +32 -0
  85. data/test/activesupport4/decoding_test.rb +108 -0
  86. data/test/activesupport4/encoding_test.rb +531 -0
  87. data/test/activesupport4/test_helper.rb +41 -0
  88. data/test/activesupport5/abstract_unit.rb +45 -0
  89. data/test/activesupport5/decoding_test.rb +133 -0
  90. data/test/activesupport5/encoding_test.rb +500 -0
  91. data/test/activesupport5/encoding_test_cases.rb +98 -0
  92. data/test/activesupport5/test_helper.rb +72 -0
  93. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  94. data/test/activesupport6/abstract_unit.rb +44 -0
  95. data/test/activesupport6/decoding_test.rb +133 -0
  96. data/test/activesupport6/encoding_test.rb +507 -0
  97. data/test/activesupport6/encoding_test_cases.rb +98 -0
  98. data/test/activesupport6/test_common.rb +17 -0
  99. data/test/activesupport6/test_helper.rb +163 -0
  100. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  101. data/test/activesupport7/abstract_unit.rb +49 -0
  102. data/test/activesupport7/decoding_test.rb +125 -0
  103. data/test/activesupport7/encoding_test.rb +486 -0
  104. data/test/activesupport7/encoding_test_cases.rb +104 -0
  105. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  106. data/test/bar.rb +9 -0
  107. data/test/baz.rb +16 -0
  108. data/test/bug.rb +11 -46
  109. data/test/foo.rb +69 -16
  110. data/test/helper.rb +10 -1
  111. data/test/isolated/shared.rb +12 -8
  112. data/test/isolated/test_mimic_rails_after.rb +3 -3
  113. data/test/isolated/test_mimic_rails_before.rb +3 -3
  114. data/test/json_gem/json_addition_test.rb +216 -0
  115. data/test/json_gem/json_common_interface_test.rb +153 -0
  116. data/test/json_gem/json_encoding_test.rb +107 -0
  117. data/test/json_gem/json_ext_parser_test.rb +20 -0
  118. data/test/json_gem/json_fixtures_test.rb +35 -0
  119. data/test/json_gem/json_generator_test.rb +397 -0
  120. data/test/json_gem/json_generic_object_test.rb +90 -0
  121. data/test/json_gem/json_parser_test.rb +470 -0
  122. data/test/json_gem/json_string_matching_test.rb +42 -0
  123. data/test/json_gem/test_helper.rb +26 -0
  124. data/test/mem.rb +33 -0
  125. data/test/perf.rb +1 -1
  126. data/test/perf_compat.rb +30 -28
  127. data/test/perf_dump.rb +50 -0
  128. data/test/perf_object.rb +1 -1
  129. data/test/perf_once.rb +58 -0
  130. data/test/perf_parser.rb +189 -0
  131. data/test/perf_scp.rb +11 -10
  132. data/test/perf_strict.rb +30 -19
  133. data/test/perf_wab.rb +131 -0
  134. data/test/prec.rb +23 -0
  135. data/test/sample.rb +0 -1
  136. data/test/sample_json.rb +1 -1
  137. data/test/test_compat.rb +219 -102
  138. data/test/test_custom.rb +533 -0
  139. data/test/test_fast.rb +107 -35
  140. data/test/test_file.rb +19 -25
  141. data/test/test_generate.rb +21 -0
  142. data/test/test_hash.rb +11 -1
  143. data/test/test_integer_range.rb +72 -0
  144. data/test/test_null.rb +376 -0
  145. data/test/test_object.rb +357 -70
  146. data/test/test_parser.rb +27 -0
  147. data/test/test_parser_saj.rb +245 -0
  148. data/test/test_parser_usual.rb +217 -0
  149. data/test/test_rails.rb +35 -0
  150. data/test/test_saj.rb +1 -1
  151. data/test/test_scp.rb +39 -2
  152. data/test/test_strict.rb +186 -7
  153. data/test/test_various.rb +160 -774
  154. data/test/test_wab.rb +307 -0
  155. data/test/test_writer.rb +90 -2
  156. data/test/tests.rb +24 -0
  157. data/test/tests_mimic.rb +14 -0
  158. data/test/tests_mimic_addition.rb +7 -0
  159. data/test/zoo.rb +13 -0
  160. metadata +194 -56
  161. data/ext/oj/hash.c +0 -163
  162. data/ext/oj/hash.h +0 -46
  163. data/ext/oj/hash_test.c +0 -512
  164. data/test/activesupport_datetime_test.rb +0 -23
  165. data/test/bug2.rb +0 -10
  166. data/test/bug3.rb +0 -46
  167. data/test/bug_fast.rb +0 -32
  168. data/test/bug_load.rb +0 -24
  169. data/test/crash.rb +0 -111
  170. data/test/curl/curl_oj.rb +0 -46
  171. data/test/curl/get_oj.rb +0 -24
  172. data/test/curl/just_curl.rb +0 -31
  173. data/test/curl/just_oj.rb +0 -51
  174. data/test/example.rb +0 -11
  175. data/test/io.rb +0 -48
  176. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  177. data/test/mod.rb +0 -16
  178. data/test/rails.rb +0 -50
  179. data/test/russian.rb +0 -18
  180. data/test/struct.rb +0 -29
  181. data/test/test_serializer.rb +0 -59
  182. data/test/write_timebars.rb +0 -31
@@ -0,0 +1,531 @@
1
+ # encoding: utf-8
2
+ require 'activesupport4/test_helper'
3
+ require 'securerandom'
4
+ require 'active_support/core_ext/string/inflections'
5
+ require 'active_support/json'
6
+ require 'active_support/time'
7
+
8
+
9
+ class TestJSONEncoding < ActiveSupport::TestCase
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
+ class CustomWithOptions
33
+ attr_accessor :foo, :bar
34
+
35
+ def as_json(options={})
36
+ options[:only] = %w(foo bar)
37
+ super(options)
38
+ end
39
+ end
40
+
41
+ class OptionsTest
42
+ def as_json(options = :default)
43
+ options
44
+ end
45
+ end
46
+
47
+ class HashWithAsJson < Hash
48
+ attr_accessor :as_json_called
49
+
50
+ def initialize(*)
51
+ super
52
+ end
53
+
54
+ def as_json(options={})
55
+ @as_json_called = true
56
+ super
57
+ end
58
+ end
59
+
60
+ TrueTests = [[ true, %(true) ]]
61
+ FalseTests = [[ false, %(false) ]]
62
+ NilTests = [[ nil, %(null) ]]
63
+ NumericTests = [[ 1, %(1) ],
64
+ [ 2.5, %(2.5) ],
65
+ [ 0.0/0.0, %(null) ],
66
+ [ 1.0/0.0, %(null) ],
67
+ [ -1.0/0.0, %(null) ],
68
+ [ BigDecimal('0.0')/BigDecimal('0.0'), %(null) ],
69
+ [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]]
70
+
71
+ StringTests = [[ 'this is the <string>', %("this is the \\u003cstring\\u003e")],
72
+ [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
73
+ [ 'http://test.host/posts/1', %("http://test.host/posts/1")],
74
+ [ "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",
75
+ %("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") ]]
76
+
77
+ ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ],
78
+ [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]]
79
+
80
+ RangeTests = [[ 1..2, %("1..2")],
81
+ [ 1...2, %("1...2")],
82
+ [ 1.5..2.5, %("1.5..2.5")]]
83
+
84
+ SymbolTests = [[ :a, %("a") ],
85
+ [ :this, %("this") ],
86
+ [ :"a b", %("a b") ]]
87
+
88
+ ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]]
89
+ HashlikeTests = [[ Hashlike.new, %({\"bar\":\"world\",\"foo\":\"hello\"}) ]]
90
+ CustomTests = [[ Custom.new("custom"), '"custom"' ],
91
+ [ Custom.new(nil), 'null' ],
92
+ [ Custom.new(:a), '"a"' ],
93
+ [ Custom.new([ :foo, "bar" ]), '["foo","bar"]' ],
94
+ [ Custom.new({ :foo => "hello", :bar => "world" }), '{"bar":"world","foo":"hello"}' ],
95
+ [ Custom.new(Hashlike.new), '{"bar":"world","foo":"hello"}' ],
96
+ [ Custom.new(Custom.new(Custom.new(:a))), '"a"' ]]
97
+
98
+ RegexpTests = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']]
99
+
100
+ DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]]
101
+ TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
102
+ DateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
103
+
104
+ StandardDateTests = [[ Date.new(2005,2,1), %("2005-02-01") ]]
105
+ StandardTimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000Z") ]]
106
+ StandardDateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000+00:00") ]]
107
+ StandardStringTests = [[ 'this is the <string>', %("this is the <string>")]]
108
+
109
+ def sorted_json(json)
110
+ return json unless json =~ /^\{.*\}$/
111
+ '{' + json[1..-2].split(',').sort.join(',') + '}'
112
+ end
113
+
114
+ constants.grep(/Tests$/).each do |class_tests|
115
+ define_method("test_#{class_tests[0..-6].underscore}") do
116
+ begin
117
+ prev = ActiveSupport.use_standard_json_time_format
118
+
119
+ ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/
120
+ ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/
121
+ self.class.const_get(class_tests).each do |pair|
122
+ assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
123
+ end
124
+ ensure
125
+ ActiveSupport.escape_html_entities_in_json = false
126
+ ActiveSupport.use_standard_json_time_format = prev
127
+ end
128
+ end
129
+ end
130
+
131
+ def test_process_status
132
+ # There doesn't seem to be a good way to get a handle on a Process::Status object without actually
133
+ # creating a child process, hence this to populate $?
134
+ system("not_a_real_program_#{SecureRandom.hex}")
135
+ assert_equal %({"exitstatus":#{$?.exitstatus},"pid":#{$?.pid}}), ActiveSupport::JSON.encode($?)
136
+ end
137
+
138
+ def test_hash_encoding
139
+ assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b)
140
+ assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1)
141
+ assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode('a' => [1,2])
142
+ assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2)
143
+
144
+ assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(:a => :b, :c => :d))
145
+ end
146
+
147
+ def test_hash_keys_encoding
148
+ ActiveSupport.escape_html_entities_in_json = true
149
+ assert_equal "{\"\\u003c\\u003e\":\"\\u003c\\u003e\"}", ActiveSupport::JSON.encode("<>" => "<>")
150
+ ensure
151
+ ActiveSupport.escape_html_entities_in_json = false
152
+ end
153
+
154
+ def test_utf8_string_encoded_properly
155
+ result = ActiveSupport::JSON.encode('€2.99')
156
+ assert_equal '"€2.99"', result
157
+ assert_equal(Encoding::UTF_8, result.encoding)
158
+
159
+ result = ActiveSupport::JSON.encode('✎☺')
160
+ assert_equal '"✎☺"', result
161
+ assert_equal(Encoding::UTF_8, result.encoding)
162
+ end
163
+
164
+ def test_non_utf8_string_transcodes
165
+ s = '二'.encode('Shift_JIS')
166
+ result = ActiveSupport::JSON.encode(s)
167
+ assert_equal '"二"', result
168
+ assert_equal Encoding::UTF_8, result.encoding
169
+ end
170
+
171
+ def test_wide_utf8_chars
172
+ w = '𠜎'
173
+ result = ActiveSupport::JSON.encode(w)
174
+ assert_equal '"𠜎"', result
175
+ end
176
+
177
+ def test_wide_utf8_roundtrip
178
+ hash = { string: "𐒑" }
179
+ json = ActiveSupport::JSON.encode(hash)
180
+ decoded_hash = ActiveSupport::JSON.decode(json)
181
+ assert_equal "𐒑", decoded_hash['string']
182
+ end
183
+
184
+ def test_reading_encode_big_decimal_as_string_option
185
+ assert_deprecated do
186
+ assert ActiveSupport.encode_big_decimal_as_string
187
+ end
188
+ end
189
+
190
+ def test_setting_deprecated_encode_big_decimal_as_string_option
191
+ assert_raise(NotImplementedError) do
192
+ ActiveSupport.encode_big_decimal_as_string = true
193
+ end
194
+
195
+ assert_raise(NotImplementedError) do
196
+ ActiveSupport.encode_big_decimal_as_string = false
197
+ end
198
+ end
199
+
200
+ def test_exception_raised_when_encoding_circular_reference_in_array
201
+ a = [1]
202
+ a << a
203
+ assert_deprecated do
204
+ assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
205
+ end
206
+ end
207
+
208
+ def test_exception_raised_when_encoding_circular_reference_in_hash
209
+ a = { :name => 'foo' }
210
+ a[:next] = a
211
+ assert_deprecated do
212
+ assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
213
+ end
214
+ end
215
+
216
+ def test_exception_raised_when_encoding_circular_reference_in_hash_inside_array
217
+ a = { :name => 'foo', :sub => [] }
218
+ a[:sub] << a
219
+ assert_deprecated do
220
+ assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
221
+ end
222
+ end
223
+
224
+ def test_hash_key_identifiers_are_always_quoted
225
+ values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"}
226
+ assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
227
+ end
228
+
229
+ def test_hash_should_allow_key_filtering_with_only
230
+ assert_equal %({"a":1}), ActiveSupport::JSON.encode({'a' => 1, :b => 2, :c => 3}, :only => 'a')
231
+ end
232
+
233
+ def test_hash_should_allow_key_filtering_with_except
234
+ assert_equal %({"b":2}), ActiveSupport::JSON.encode({'foo' => 'bar', :b => 2, :c => 3}, :except => ['foo', :c])
235
+ end
236
+
237
+ def test_time_to_json_includes_local_offset
238
+ with_standard_json_time_format(true) do
239
+ with_env_tz 'US/Eastern' do
240
+ assert_equal %("2005-02-01T15:15:10.000-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10))
241
+ end
242
+ end
243
+ end
244
+
245
+ def test_hash_with_time_to_json
246
+ with_standard_json_time_format(false) do
247
+ assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { :time => Time.utc(2009) }.to_json
248
+ end
249
+ end
250
+
251
+ def test_nested_hash_with_float
252
+ assert_nothing_raised do
253
+ hash = {
254
+ "CHI" => {
255
+ :display_name => "chicago",
256
+ :latitude => 123.234
257
+ }
258
+ }
259
+ ActiveSupport::JSON.encode(hash)
260
+ end
261
+ end
262
+
263
+ def test_hash_like_with_options
264
+ h = Hashlike.new
265
+ json = h.to_json :only => [:foo]
266
+
267
+ assert_equal({"foo"=>"hello"}, JSON.parse(json))
268
+ end
269
+
270
+ def test_object_to_json_with_options
271
+ obj = Object.new
272
+ obj.instance_variable_set :@foo, "hello"
273
+ obj.instance_variable_set :@bar, "world"
274
+ json = obj.to_json :only => ["foo"]
275
+
276
+ assert_equal({"foo"=>"hello"}, JSON.parse(json))
277
+ end
278
+
279
+ def test_struct_to_json_with_options
280
+ struct = Struct.new(:foo, :bar).new
281
+ struct.foo = "hello"
282
+ struct.bar = "world"
283
+ json = struct.to_json :only => [:foo]
284
+
285
+ assert_equal({"foo"=>"hello"}, JSON.parse(json))
286
+ end
287
+
288
+ def test_hash_should_pass_encoding_options_to_children_in_as_json
289
+ person = {
290
+ :name => 'John',
291
+ :address => {
292
+ :city => 'London',
293
+ :country => 'UK'
294
+ }
295
+ }
296
+ json = person.as_json :only => [:address, :city]
297
+
298
+ assert_equal({ 'address' => { 'city' => 'London' }}, json)
299
+ end
300
+
301
+ def test_hash_should_pass_encoding_options_to_children_in_to_json
302
+ person = {
303
+ :name => 'John',
304
+ :address => {
305
+ :city => 'London',
306
+ :country => 'UK'
307
+ }
308
+ }
309
+ json = person.to_json :only => [:address, :city]
310
+
311
+ assert_equal(%({"address":{"city":"London"}}), json)
312
+ end
313
+
314
+ def test_array_should_pass_encoding_options_to_children_in_as_json
315
+ people = [
316
+ { :name => 'John', :address => { :city => 'London', :country => 'UK' }},
317
+ { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
318
+ ]
319
+ json = people.as_json :only => [:address, :city]
320
+ expected = [
321
+ { 'address' => { 'city' => 'London' }},
322
+ { 'address' => { 'city' => 'Paris' }}
323
+ ]
324
+
325
+ assert_equal(expected, json)
326
+ end
327
+
328
+ def test_array_should_pass_encoding_options_to_children_in_to_json
329
+ people = [
330
+ { :name => 'John', :address => { :city => 'London', :country => 'UK' }},
331
+ { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
332
+ ]
333
+ json = people.to_json :only => [:address, :city]
334
+
335
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
336
+ end
337
+
338
+ def test_enumerable_should_pass_encoding_options_to_children_in_as_json
339
+ people = [
340
+ { :name => 'John', :address => { :city => 'London', :country => 'UK' }},
341
+ { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
342
+ ]
343
+ json = people.each.as_json :only => [:address, :city]
344
+ expected = [
345
+ { 'address' => { 'city' => 'London' }},
346
+ { 'address' => { 'city' => 'Paris' }}
347
+ ]
348
+
349
+ assert_equal(expected, json)
350
+ end
351
+
352
+ def test_enumerable_should_pass_encoding_options_to_children_in_to_json
353
+ people = [
354
+ { :name => 'John', :address => { :city => 'London', :country => 'UK' }},
355
+ { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
356
+ ]
357
+ json = people.each.to_json :only => [:address, :city]
358
+
359
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
360
+ end
361
+
362
+ def test_hash_to_json_should_not_keep_options_around
363
+ f = CustomWithOptions.new
364
+ f.foo = "hello"
365
+ f.bar = "world"
366
+
367
+ hash = {"foo" => f, "other_hash" => {"foo" => "other_foo", "test" => "other_test"}}
368
+ assert_equal({"foo"=>{"foo"=>"hello","bar"=>"world"},
369
+ "other_hash" => {"foo"=>"other_foo","test"=>"other_test"}}, ActiveSupport::JSON.decode(hash.to_json))
370
+ end
371
+
372
+ def test_array_to_json_should_not_keep_options_around
373
+ f = CustomWithOptions.new
374
+ f.foo = "hello"
375
+ f.bar = "world"
376
+
377
+ array = [f, {"foo" => "other_foo", "test" => "other_test"}]
378
+ assert_equal([{"foo"=>"hello","bar"=>"world"},
379
+ {"foo"=>"other_foo","test"=>"other_test"}], ActiveSupport::JSON.decode(array.to_json))
380
+ end
381
+
382
+ def test_hash_as_json_without_options
383
+ json = { foo: OptionsTest.new }.as_json
384
+ assert_equal({"foo" => :default}, json)
385
+ end
386
+
387
+ def test_array_as_json_without_options
388
+ json = [ OptionsTest.new ].as_json
389
+ assert_equal([:default], json)
390
+ end
391
+
392
+ def test_struct_encoding
393
+ Struct.new('UserNameAndEmail', :name, :email)
394
+ Struct.new('UserNameAndDate', :name, :date)
395
+ Struct.new('Custom', :name, :sub)
396
+ user_email = Struct::UserNameAndEmail.new 'David', 'sample@example.com'
397
+ user_birthday = Struct::UserNameAndDate.new 'David', Date.new(2010, 01, 01)
398
+ custom = Struct::Custom.new 'David', user_birthday
399
+
400
+
401
+ json_strings = ""
402
+ json_string_and_date = ""
403
+ json_custom = ""
404
+
405
+ assert_nothing_raised do
406
+ json_strings = user_email.to_json
407
+ json_string_and_date = user_birthday.to_json
408
+ json_custom = custom.to_json
409
+ end
410
+
411
+ assert_equal({"name" => "David",
412
+ "sub" => {
413
+ "name" => "David",
414
+ "date" => "2010-01-01" }}, ActiveSupport::JSON.decode(json_custom))
415
+
416
+ assert_equal({"name" => "David", "email" => "sample@example.com"},
417
+ ActiveSupport::JSON.decode(json_strings))
418
+
419
+ assert_equal({"name" => "David", "date" => "2010-01-01"},
420
+ ActiveSupport::JSON.decode(json_string_and_date))
421
+ end
422
+
423
+ def test_nil_true_and_false_represented_as_themselves
424
+ assert_nil(nil.as_json)
425
+ assert_equal true, true.as_json
426
+ assert_equal false, false.as_json
427
+ end
428
+
429
+ def test_json_gem_dump_by_passing_active_support_encoder
430
+ h = HashWithAsJson.new
431
+ h[:foo] = "hello"
432
+ h[:bar] = "world"
433
+
434
+ assert_equal %({"foo":"hello","bar":"world"}), JSON.dump(h)
435
+ assert_nil h.as_json_called
436
+ end
437
+
438
+ def test_json_gem_generate_by_passing_active_support_encoder
439
+ h = HashWithAsJson.new
440
+ h[:foo] = "hello"
441
+ h[:bar] = "world"
442
+
443
+ assert_equal %({"foo":"hello","bar":"world"}), JSON.generate(h)
444
+ assert_nil h.as_json_called
445
+ end
446
+
447
+ def test_json_gem_pretty_generate_by_passing_active_support_encoder
448
+ h = HashWithAsJson.new
449
+ h[:foo] = "hello"
450
+ h[:bar] = "world"
451
+
452
+ assert_equal <<EXPECTED.chomp, JSON.pretty_generate(h)
453
+ {
454
+ "foo": "hello",
455
+ "bar": "world"
456
+ }
457
+ EXPECTED
458
+ assert_nil h.as_json_called
459
+ end
460
+
461
+ def test_twz_to_json_with_use_standard_json_time_format_config_set_to_false
462
+ with_standard_json_time_format(false) do
463
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
464
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
465
+ assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(time)
466
+ end
467
+ end
468
+
469
+ def test_twz_to_json_with_use_standard_json_time_format_config_set_to_true
470
+ with_standard_json_time_format(true) do
471
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
472
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
473
+ assert_equal "\"1999-12-31T19:00:00.000-05:00\"", ActiveSupport::JSON.encode(time)
474
+ end
475
+ end
476
+
477
+ def test_twz_to_json_with_custom_time_precision
478
+ with_standard_json_time_format(true) do
479
+ ActiveSupport::JSON::Encoding.time_precision = 0
480
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
481
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
482
+ assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(time)
483
+ end
484
+ ensure
485
+ ActiveSupport::JSON::Encoding.time_precision = 3
486
+ end
487
+
488
+ def test_time_to_json_with_custom_time_precision
489
+ with_standard_json_time_format(true) do
490
+ ActiveSupport::JSON::Encoding.time_precision = 0
491
+ assert_equal "\"2000-01-01T00:00:00Z\"", ActiveSupport::JSON.encode(Time.utc(2000))
492
+ end
493
+ ensure
494
+ ActiveSupport::JSON::Encoding.time_precision = 3
495
+ end
496
+
497
+ def test_datetime_to_json_with_custom_time_precision
498
+ with_standard_json_time_format(true) do
499
+ ActiveSupport::JSON::Encoding.time_precision = 0
500
+ assert_equal "\"2000-01-01T00:00:00+00:00\"", ActiveSupport::JSON.encode(DateTime.new(2000))
501
+ end
502
+ ensure
503
+ ActiveSupport::JSON::Encoding.time_precision = 3
504
+ end
505
+
506
+ def test_twz_to_json_when_wrapping_a_date_time
507
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
508
+ time = ActiveSupport::TimeWithZone.new(DateTime.new(2000), zone)
509
+ assert_equal '"1999-12-31T19:00:00.000-05:00"', ActiveSupport::JSON.encode(time)
510
+ end
511
+
512
+ protected
513
+
514
+ def object_keys(json_object)
515
+ json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
516
+ end
517
+
518
+ def with_env_tz(new_tz = 'US/Eastern')
519
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
520
+ yield
521
+ ensure
522
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
523
+ end
524
+
525
+ def with_standard_json_time_format(boolean = true)
526
+ old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, boolean
527
+ yield
528
+ ensure
529
+ ActiveSupport.use_standard_json_time_format = old
530
+ end
531
+ end
@@ -0,0 +1,41 @@
1
+ require 'active_support/testing/assertions'
2
+ require 'active_support/testing/deprecation'
3
+ require 'active_support/testing/declarative'
4
+ require 'minitest/autorun'
5
+
6
+ module ActiveSupport
7
+ class TestCase < ::Minitest::Test
8
+ Assertion = Minitest::Assertion
9
+
10
+ alias_method :method_name, :name
11
+
12
+ include ActiveSupport::Testing::Assertions
13
+ include ActiveSupport::Testing::Deprecation
14
+ extend ActiveSupport::Testing::Declarative
15
+
16
+ # test/unit backwards compatibility methods
17
+ alias :assert_raise :assert_raises
18
+ alias :assert_not_empty :refute_empty
19
+ alias :assert_not_equal :refute_equal
20
+ alias :assert_not_in_delta :refute_in_delta
21
+ alias :assert_not_in_epsilon :refute_in_epsilon
22
+ alias :assert_not_includes :refute_includes
23
+ alias :assert_not_instance_of :refute_instance_of
24
+ alias :assert_not_kind_of :refute_kind_of
25
+ alias :assert_no_match :refute_match
26
+ alias :assert_not_nil :refute_nil
27
+ alias :assert_not_operator :refute_operator
28
+ alias :assert_not_predicate :refute_predicate
29
+ alias :assert_not_respond_to :refute_respond_to
30
+ alias :assert_not_same :refute_same
31
+
32
+ # Fails if the block raises an exception.
33
+ #
34
+ # assert_nothing_raised do
35
+ # ...
36
+ # end
37
+ def assert_nothing_raised(*args)
38
+ yield
39
+ end
40
+ end
41
+ end
@@ -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