json 2.7.6 → 2.8.1

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
@@ -32,9 +32,7 @@ module JSON
32
32
  JSON.generate(object, opts)
33
33
  end
34
34
 
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
35
+ # Returns the JSON parser class that is used by JSON.
38
36
  attr_reader :parser
39
37
 
40
38
  # Set the JSON parser class _parser_ to be used by JSON.
@@ -49,18 +47,9 @@ module JSON
49
47
  # level (absolute namespace path?). If there doesn't exist a constant at
50
48
  # the given path, an ArgumentError is raised.
51
49
  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
50
+ Object.const_get(path)
51
+ rescue NameError => e
52
+ raise ArgumentError, "can't get const #{path}: #{e}"
64
53
  end
65
54
 
66
55
  # Set the module _generator_ to be used by JSON.
@@ -69,7 +58,7 @@ module JSON
69
58
  @generator = generator
70
59
  generator_methods = generator::GeneratorMethods
71
60
  for const in generator_methods.constants
72
- klass = deep_const_get(const)
61
+ klass = const_get(const)
73
62
  modul = generator_methods.const_get(const)
74
63
  klass.class_eval do
75
64
  instance_methods(false).each do |m|
@@ -106,14 +95,10 @@ module JSON
106
95
  )
107
96
  end
108
97
 
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
98
+ # Returns the JSON generator module that is used by JSON.
112
99
  attr_reader :generator
113
100
 
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
101
+ # Sets or Returns the JSON generator state class that is used by JSON.
117
102
  attr_accessor :state
118
103
  end
119
104
 
@@ -195,17 +180,17 @@ module JSON
195
180
  # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
196
181
  #
197
182
  # 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
183
+ # source = <<~JSON
184
+ # {
185
+ # "name": "Dave",
186
+ # "age" :40,
187
+ # "hats": [
188
+ # "Cattleman's",
189
+ # "Panama",
190
+ # "Tophat"
191
+ # ]
192
+ # }
193
+ # JSON
209
194
  # ruby = JSON.parse(source)
210
195
  # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
211
196
  #
@@ -216,16 +201,7 @@ module JSON
216
201
  # JSON.parse('')
217
202
  #
218
203
  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
204
+ Parser.parse(source, opts)
229
205
  end
230
206
 
231
207
  # :call-seq:
@@ -307,11 +283,10 @@ module JSON
307
283
  #
308
284
  def generate(obj, opts = nil)
309
285
  if State === opts
310
- state = opts
286
+ opts.generate(obj)
311
287
  else
312
- state = State.new(opts)
288
+ State.generate(obj, opts)
313
289
  end
314
- state.generate(obj)
315
290
  end
316
291
 
317
292
  # :stopdoc:
@@ -404,6 +379,20 @@ module JSON
404
379
  module_function :pretty_unparse
405
380
  # :startdoc:
406
381
 
382
+ class << self
383
+ # Sets or returns default options for the JSON.unsafe_load method.
384
+ # Initially:
385
+ # opts = JSON.load_default_options
386
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
387
+ attr_accessor :unsafe_load_default_options
388
+ end
389
+ self.unsafe_load_default_options = {
390
+ :max_nesting => false,
391
+ :allow_nan => true,
392
+ :allow_blank => true,
393
+ :create_additions => true,
394
+ }
395
+
407
396
  class << self
408
397
  # Sets or returns default options for the JSON.load method.
409
398
  # Initially:
@@ -412,11 +401,162 @@ module JSON
412
401
  attr_accessor :load_default_options
413
402
  end
414
403
  self.load_default_options = {
415
- :max_nesting => false,
416
404
  :allow_nan => true,
417
405
  :allow_blank => true,
418
- :create_additions => true,
406
+ :create_additions => nil,
419
407
  }
408
+ # :call-seq:
409
+ # JSON.unsafe_load(source, proc = nil, options = {}) -> object
410
+ #
411
+ # Returns the Ruby objects created by parsing the given +source+.
412
+ #
413
+ # - Argument +source+ must be, or be convertible to, a \String:
414
+ # - If +source+ responds to instance method +to_str+,
415
+ # <tt>source.to_str</tt> becomes the source.
416
+ # - If +source+ responds to instance method +to_io+,
417
+ # <tt>source.to_io.read</tt> becomes the source.
418
+ # - If +source+ responds to instance method +read+,
419
+ # <tt>source.read</tt> becomes the source.
420
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
421
+ # - Option +allow_blank+ specifies a truthy value.
422
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
423
+ # - Otherwise, +source+ remains the source.
424
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
425
+ # It will be called recursively with each result (depth-first order).
426
+ # See details below.
427
+ # BEWARE: This method is meant to serialise data from trusted user input,
428
+ # like from your own database server or clients under your control, it could
429
+ # be dangerous to allow untrusted users to pass JSON sources into it.
430
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
431
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
432
+ # The default options can be changed via method JSON.unsafe_load_default_options=.
433
+ #
434
+ # ---
435
+ #
436
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
437
+ # <tt>parse(source, opts)</tt>; see #parse.
438
+ #
439
+ # Source for following examples:
440
+ # source = <<~JSON
441
+ # {
442
+ # "name": "Dave",
443
+ # "age" :40,
444
+ # "hats": [
445
+ # "Cattleman's",
446
+ # "Panama",
447
+ # "Tophat"
448
+ # ]
449
+ # }
450
+ # JSON
451
+ #
452
+ # Load a \String:
453
+ # ruby = JSON.unsafe_load(source)
454
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
455
+ #
456
+ # Load an \IO object:
457
+ # require 'stringio'
458
+ # object = JSON.unsafe_load(StringIO.new(source))
459
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
460
+ #
461
+ # Load a \File object:
462
+ # path = 't.json'
463
+ # File.write(path, source)
464
+ # File.open(path) do |file|
465
+ # JSON.unsafe_load(file)
466
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
467
+ #
468
+ # ---
469
+ #
470
+ # When +proc+ is given:
471
+ # - Modifies +source+ as above.
472
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
473
+ # - Recursively calls <tt>proc(result)</tt>.
474
+ # - Returns the final result.
475
+ #
476
+ # Example:
477
+ # require 'json'
478
+ #
479
+ # # Some classes for the example.
480
+ # class Base
481
+ # def initialize(attributes)
482
+ # @attributes = attributes
483
+ # end
484
+ # end
485
+ # class User < Base; end
486
+ # class Account < Base; end
487
+ # class Admin < Base; end
488
+ # # The JSON source.
489
+ # json = <<-EOF
490
+ # {
491
+ # "users": [
492
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
493
+ # {"type": "User", "username": "john", "email": "john@example.com"}
494
+ # ],
495
+ # "accounts": [
496
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
497
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
498
+ # ],
499
+ # "admins": {"type": "Admin", "password": "0wn3d"}
500
+ # }
501
+ # EOF
502
+ # # Deserializer method.
503
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
504
+ # type = obj.is_a?(Hash) && obj["type"]
505
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
506
+ # end
507
+ # # Call to JSON.unsafe_load
508
+ # ruby = JSON.unsafe_load(json, proc {|obj|
509
+ # case obj
510
+ # when Hash
511
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
512
+ # when Array
513
+ # obj.map! {|v| deserialize_obj v }
514
+ # end
515
+ # })
516
+ # pp ruby
517
+ # Output:
518
+ # {"users"=>
519
+ # [#<User:0x00000000064c4c98
520
+ # @attributes=
521
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
522
+ # #<User:0x00000000064c4bd0
523
+ # @attributes=
524
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
525
+ # "accounts"=>
526
+ # [{"account"=>
527
+ # #<Account:0x00000000064c4928
528
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
529
+ # {"account"=>
530
+ # #<Account:0x00000000064c4680
531
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
532
+ # "admins"=>
533
+ # #<Admin:0x00000000064c41f8
534
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
535
+ #
536
+ def unsafe_load(source, proc = nil, options = nil)
537
+ opts = if options.nil?
538
+ unsafe_load_default_options
539
+ else
540
+ unsafe_load_default_options.merge(options)
541
+ end
542
+
543
+ unless source.is_a?(String)
544
+ if source.respond_to? :to_str
545
+ source = source.to_str
546
+ elsif source.respond_to? :to_io
547
+ source = source.to_io.read
548
+ elsif source.respond_to?(:read)
549
+ source = source.read
550
+ end
551
+ end
552
+
553
+ if opts[:allow_blank] && (source.nil? || source.empty?)
554
+ source = 'null'
555
+ end
556
+ result = parse(source, opts)
557
+ recurse_proc(result, &proc) if proc
558
+ result
559
+ end
420
560
 
421
561
  # :call-seq:
422
562
  # JSON.load(source, proc = nil, options = {}) -> object
@@ -440,6 +580,7 @@ module JSON
440
580
  # BEWARE: This method is meant to serialise data from trusted user input,
441
581
  # like from your own database server or clients under your control, it could
442
582
  # be dangerous to allow untrusted users to pass JSON sources into it.
583
+ # If you must use it, use JSON.unsafe_load instead to make it clear.
443
584
  # - Argument +opts+, if given, contains a \Hash of options for the parsing.
444
585
  # See {Parsing Options}[#module-JSON-label-Parsing+Options].
445
586
  # The default options can be changed via method JSON.load_default_options=.
@@ -450,17 +591,17 @@ module JSON
450
591
  # <tt>parse(source, opts)</tt>; see #parse.
451
592
  #
452
593
  # 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
594
+ # source = <<~JSON
595
+ # {
596
+ # "name": "Dave",
597
+ # "age" :40,
598
+ # "hats": [
599
+ # "Cattleman's",
600
+ # "Panama",
601
+ # "Tophat"
602
+ # ]
603
+ # }
604
+ # JSON
464
605
  #
465
606
  # Load a \String:
466
607
  # 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