json 2.7.5 → 2.8.2

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.
data/json.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  version = File.foreach(File.join(__dir__, "lib/json/version.rb")) do |line|
2
4
  /^\s*VERSION\s*=\s*'(.*)'/ =~ line and break $1
3
5
  end rescue nil
@@ -19,7 +21,7 @@ spec = Gem::Specification.new do |s|
19
21
  'wiki_uri' => 'https://github.com/ruby/json/wiki'
20
22
  }
21
23
 
22
- s.required_ruby_version = Gem::Requirement.new(">= 2.3")
24
+ s.required_ruby_version = Gem::Requirement.new(">= 2.7")
23
25
 
24
26
  if java_ext
25
27
  s.description = "A JSON implementation as a JRuby extension."
@@ -48,6 +50,7 @@ spec = Gem::Specification.new do |s|
48
50
 
49
51
  if java_ext
50
52
  s.platform = 'java'
53
+ s.files += Dir["lib/json/ext/**/*.jar"]
51
54
  else
52
55
  s.extensions = Dir["ext/json/**/extconf.rb"]
53
56
  s.files += Dir["ext/json/**/*.{c,h,rl}"]
@@ -35,7 +35,7 @@ class BigDecimal
35
35
  def as_json(*)
36
36
  {
37
37
  JSON.create_id => self.class.name,
38
- 'b' => _dump,
38
+ 'b' => _dump.force_encoding(Encoding::UTF_8),
39
39
  }
40
40
  end
41
41
 
data/lib/json/common.rb CHANGED
@@ -1,4 +1,5 @@
1
- #frozen_string_literal: true
1
+ # frozen_string_literal: true
2
+
2
3
  require 'json/version'
3
4
 
4
5
  module JSON
@@ -25,16 +26,14 @@ module JSON
25
26
  elsif object.respond_to?(:to_str)
26
27
  str = object.to_str
27
28
  if str.is_a?(String)
28
- return JSON.parse(object.to_str, opts)
29
+ return JSON.parse(str, opts)
29
30
  end
30
31
  end
31
32
 
32
33
  JSON.generate(object, opts)
33
34
  end
34
35
 
35
- # Returns the JSON parser class that is used by JSON. This is either
36
- # JSON::Ext::Parser or JSON::Pure::Parser:
37
- # JSON.parser # => JSON::Ext::Parser
36
+ # Returns the JSON parser class that is used by JSON.
38
37
  attr_reader :parser
39
38
 
40
39
  # Set the JSON parser class _parser_ to be used by JSON.
@@ -49,18 +48,9 @@ module JSON
49
48
  # level (absolute namespace path?). If there doesn't exist a constant at
50
49
  # the given path, an ArgumentError is raised.
51
50
  def deep_const_get(path) # :nodoc:
52
- path.to_s.split(/::/).inject(Object) do |p, c|
53
- case
54
- when c.empty? then p
55
- when p.const_defined?(c, true) then p.const_get(c)
56
- else
57
- begin
58
- p.const_missing(c)
59
- rescue NameError => e
60
- raise ArgumentError, "can't get const #{path}: #{e}"
61
- end
62
- end
63
- end
51
+ Object.const_get(path)
52
+ rescue NameError => e
53
+ raise ArgumentError, "can't get const #{path}: #{e}"
64
54
  end
65
55
 
66
56
  # Set the module _generator_ to be used by JSON.
@@ -69,7 +59,7 @@ module JSON
69
59
  @generator = generator
70
60
  generator_methods = generator::GeneratorMethods
71
61
  for const in generator_methods.constants
72
- klass = deep_const_get(const)
62
+ klass = const_get(const)
73
63
  modul = generator_methods.const_get(const)
74
64
  klass.class_eval do
75
65
  instance_methods(false).each do |m|
@@ -106,14 +96,10 @@ module JSON
106
96
  )
107
97
  end
108
98
 
109
- # Returns the JSON generator module that is used by JSON. This is
110
- # either JSON::Ext::Generator or JSON::Pure::Generator:
111
- # JSON.generator # => JSON::Ext::Generator
99
+ # Returns the JSON generator module that is used by JSON.
112
100
  attr_reader :generator
113
101
 
114
- # Sets or Returns the JSON generator state class that is used by JSON. This is
115
- # either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
116
- # JSON.state # => JSON::Ext::Generator::State
102
+ # Sets or Returns the JSON generator state class that is used by JSON.
117
103
  attr_accessor :state
118
104
  end
119
105
 
@@ -195,17 +181,17 @@ module JSON
195
181
  # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
196
182
  #
197
183
  # Parses nested JSON objects:
198
- # source = <<-EOT
199
- # {
200
- # "name": "Dave",
201
- # "age" :40,
202
- # "hats": [
203
- # "Cattleman's",
204
- # "Panama",
205
- # "Tophat"
206
- # ]
207
- # }
208
- # EOT
184
+ # source = <<~JSON
185
+ # {
186
+ # "name": "Dave",
187
+ # "age" :40,
188
+ # "hats": [
189
+ # "Cattleman's",
190
+ # "Panama",
191
+ # "Tophat"
192
+ # ]
193
+ # }
194
+ # JSON
209
195
  # ruby = JSON.parse(source)
210
196
  # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
211
197
  #
@@ -216,16 +202,7 @@ module JSON
216
202
  # JSON.parse('')
217
203
  #
218
204
  def parse(source, opts = nil)
219
- if opts.nil?
220
- Parser.new(source).parse
221
- else
222
- # NB: The ** shouldn't be required, but we have to deal with
223
- # different versions of the `json` and `json_pure` gems being
224
- # loaded concurrently.
225
- # Prior to 2.7.3, `JSON::Ext::Parser` would only take kwargs.
226
- # Ref: https://github.com/ruby/json/issues/650
227
- Parser.new(source, **opts).parse
228
- end
205
+ Parser.parse(source, opts)
229
206
  end
230
207
 
231
208
  # :call-seq:
@@ -254,8 +231,8 @@ module JSON
254
231
  # parse(File.read(path), opts)
255
232
  #
256
233
  # See method #parse.
257
- def load_file(filespec, opts = {})
258
- parse(File.read(filespec), opts)
234
+ def load_file(filespec, opts = nil)
235
+ parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
259
236
  end
260
237
 
261
238
  # :call-seq:
@@ -266,7 +243,7 @@ module JSON
266
243
  #
267
244
  # See method #parse!
268
245
  def load_file!(filespec, opts = {})
269
- parse!(File.read(filespec), opts)
246
+ parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
270
247
  end
271
248
 
272
249
  # :call-seq:
@@ -307,11 +284,10 @@ module JSON
307
284
  #
308
285
  def generate(obj, opts = nil)
309
286
  if State === opts
310
- state = opts
287
+ opts.generate(obj)
311
288
  else
312
- state = State.new(opts)
289
+ State.generate(obj, opts)
313
290
  end
314
- state.generate(obj)
315
291
  end
316
292
 
317
293
  # :stopdoc:
@@ -404,6 +380,20 @@ module JSON
404
380
  module_function :pretty_unparse
405
381
  # :startdoc:
406
382
 
383
+ class << self
384
+ # Sets or returns default options for the JSON.unsafe_load method.
385
+ # Initially:
386
+ # opts = JSON.load_default_options
387
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
388
+ attr_accessor :unsafe_load_default_options
389
+ end
390
+ self.unsafe_load_default_options = {
391
+ :max_nesting => false,
392
+ :allow_nan => true,
393
+ :allow_blank => true,
394
+ :create_additions => true,
395
+ }
396
+
407
397
  class << self
408
398
  # Sets or returns default options for the JSON.load method.
409
399
  # Initially:
@@ -412,11 +402,162 @@ module JSON
412
402
  attr_accessor :load_default_options
413
403
  end
414
404
  self.load_default_options = {
415
- :max_nesting => false,
416
405
  :allow_nan => true,
417
406
  :allow_blank => true,
418
- :create_additions => true,
407
+ :create_additions => nil,
419
408
  }
409
+ # :call-seq:
410
+ # JSON.unsafe_load(source, proc = nil, options = {}) -> object
411
+ #
412
+ # Returns the Ruby objects created by parsing the given +source+.
413
+ #
414
+ # - Argument +source+ must be, or be convertible to, a \String:
415
+ # - If +source+ responds to instance method +to_str+,
416
+ # <tt>source.to_str</tt> becomes the source.
417
+ # - If +source+ responds to instance method +to_io+,
418
+ # <tt>source.to_io.read</tt> becomes the source.
419
+ # - If +source+ responds to instance method +read+,
420
+ # <tt>source.read</tt> becomes the source.
421
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
422
+ # - Option +allow_blank+ specifies a truthy value.
423
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
424
+ # - Otherwise, +source+ remains the source.
425
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
426
+ # It will be called recursively with each result (depth-first order).
427
+ # See details below.
428
+ # BEWARE: This method is meant to serialise data from trusted user input,
429
+ # like from your own database server or clients under your control, it could
430
+ # be dangerous to allow untrusted users to pass JSON sources into it.
431
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
432
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
433
+ # The default options can be changed via method JSON.unsafe_load_default_options=.
434
+ #
435
+ # ---
436
+ #
437
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
438
+ # <tt>parse(source, opts)</tt>; see #parse.
439
+ #
440
+ # Source for following examples:
441
+ # source = <<~JSON
442
+ # {
443
+ # "name": "Dave",
444
+ # "age" :40,
445
+ # "hats": [
446
+ # "Cattleman's",
447
+ # "Panama",
448
+ # "Tophat"
449
+ # ]
450
+ # }
451
+ # JSON
452
+ #
453
+ # Load a \String:
454
+ # ruby = JSON.unsafe_load(source)
455
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
456
+ #
457
+ # Load an \IO object:
458
+ # require 'stringio'
459
+ # object = JSON.unsafe_load(StringIO.new(source))
460
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
461
+ #
462
+ # Load a \File object:
463
+ # path = 't.json'
464
+ # File.write(path, source)
465
+ # File.open(path) do |file|
466
+ # JSON.unsafe_load(file)
467
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
468
+ #
469
+ # ---
470
+ #
471
+ # When +proc+ is given:
472
+ # - Modifies +source+ as above.
473
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
474
+ # - Recursively calls <tt>proc(result)</tt>.
475
+ # - Returns the final result.
476
+ #
477
+ # Example:
478
+ # require 'json'
479
+ #
480
+ # # Some classes for the example.
481
+ # class Base
482
+ # def initialize(attributes)
483
+ # @attributes = attributes
484
+ # end
485
+ # end
486
+ # class User < Base; end
487
+ # class Account < Base; end
488
+ # class Admin < Base; end
489
+ # # The JSON source.
490
+ # json = <<-EOF
491
+ # {
492
+ # "users": [
493
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
494
+ # {"type": "User", "username": "john", "email": "john@example.com"}
495
+ # ],
496
+ # "accounts": [
497
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
498
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
499
+ # ],
500
+ # "admins": {"type": "Admin", "password": "0wn3d"}
501
+ # }
502
+ # EOF
503
+ # # Deserializer method.
504
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
505
+ # type = obj.is_a?(Hash) && obj["type"]
506
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
507
+ # end
508
+ # # Call to JSON.unsafe_load
509
+ # ruby = JSON.unsafe_load(json, proc {|obj|
510
+ # case obj
511
+ # when Hash
512
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
513
+ # when Array
514
+ # obj.map! {|v| deserialize_obj v }
515
+ # end
516
+ # })
517
+ # pp ruby
518
+ # Output:
519
+ # {"users"=>
520
+ # [#<User:0x00000000064c4c98
521
+ # @attributes=
522
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
523
+ # #<User:0x00000000064c4bd0
524
+ # @attributes=
525
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
526
+ # "accounts"=>
527
+ # [{"account"=>
528
+ # #<Account:0x00000000064c4928
529
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
530
+ # {"account"=>
531
+ # #<Account:0x00000000064c4680
532
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
533
+ # "admins"=>
534
+ # #<Admin:0x00000000064c41f8
535
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
536
+ #
537
+ def unsafe_load(source, proc = nil, options = nil)
538
+ opts = if options.nil?
539
+ unsafe_load_default_options
540
+ else
541
+ unsafe_load_default_options.merge(options)
542
+ end
543
+
544
+ unless source.is_a?(String)
545
+ if source.respond_to? :to_str
546
+ source = source.to_str
547
+ elsif source.respond_to? :to_io
548
+ source = source.to_io.read
549
+ elsif source.respond_to?(:read)
550
+ source = source.read
551
+ end
552
+ end
553
+
554
+ if opts[:allow_blank] && (source.nil? || source.empty?)
555
+ source = 'null'
556
+ end
557
+ result = parse(source, opts)
558
+ recurse_proc(result, &proc) if proc
559
+ result
560
+ end
420
561
 
421
562
  # :call-seq:
422
563
  # JSON.load(source, proc = nil, options = {}) -> object
@@ -440,6 +581,7 @@ module JSON
440
581
  # BEWARE: This method is meant to serialise data from trusted user input,
441
582
  # like from your own database server or clients under your control, it could
442
583
  # be dangerous to allow untrusted users to pass JSON sources into it.
584
+ # If you must use it, use JSON.unsafe_load instead to make it clear.
443
585
  # - Argument +opts+, if given, contains a \Hash of options for the parsing.
444
586
  # See {Parsing Options}[#module-JSON-label-Parsing+Options].
445
587
  # The default options can be changed via method JSON.load_default_options=.
@@ -450,17 +592,17 @@ module JSON
450
592
  # <tt>parse(source, opts)</tt>; see #parse.
451
593
  #
452
594
  # Source for following examples:
453
- # source = <<-EOT
454
- # {
455
- # "name": "Dave",
456
- # "age" :40,
457
- # "hats": [
458
- # "Cattleman's",
459
- # "Panama",
460
- # "Tophat"
461
- # ]
462
- # }
463
- # EOT
595
+ # source = <<~JSON
596
+ # {
597
+ # "name": "Dave",
598
+ # "age" :40,
599
+ # "hats": [
600
+ # "Cattleman's",
601
+ # "Panama",
602
+ # "Tophat"
603
+ # ]
604
+ # }
605
+ # JSON
464
606
  #
465
607
  # Load a \String:
466
608
  # ruby = JSON.load(source)
@@ -42,37 +42,7 @@ module JSON
42
42
  raise TypeError, "can't convert #{opts.class} into Hash"
43
43
  end
44
44
  end
45
-
46
- opts.each do |key, value|
47
- case key
48
- when :indent
49
- self.indent = value || ''
50
- when :space
51
- self.space = value || ''
52
- when :space_before
53
- self.space_before = value || ''
54
- when :array_nl
55
- self.array_nl = value || ''
56
- when :object_nl
57
- self.object_nl = value || ''
58
- when :max_nesting
59
- self.max_nesting = value || 0
60
- when :depth
61
- self.depth = value
62
- when :buffer_initial_length
63
- self.buffer_initial_length = value
64
- when :allow_nan
65
- self.allow_nan = value
66
- when :ascii_only
67
- self.ascii_only = value
68
- when :script_safe, :escape_slash
69
- self.script_safe = value
70
- when :strict
71
- self.strict = value
72
- end
73
- end
74
-
75
- self
45
+ _configure(opts)
76
46
  end
77
47
 
78
48
  alias_method :merge, :configure
data/lib/json/ext.rb CHANGED
@@ -8,14 +8,12 @@ module JSON
8
8
  module Ext
9
9
  if RUBY_ENGINE == 'truffleruby'
10
10
  require 'json/ext/parser'
11
- require 'json/pure'
12
- $DEBUG and warn "Using Ext extension for JSON parser and Pure library for JSON generator."
11
+ require 'json/truffle_ruby/generator'
13
12
  JSON.parser = Parser
14
- JSON.generator = JSON::Pure::Generator
13
+ JSON.generator = ::JSON::TruffleRuby::Generator
15
14
  else
16
15
  require 'json/ext/parser'
17
16
  require 'json/ext/generator'
18
- $DEBUG and warn "Using Ext extension for JSON."
19
17
  JSON.parser = Parser
20
18
  JSON.generator = Generator
21
19
  end