From 172762c6e4a928cb9afe8abd2bba9e563ed0e23a Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 26 Mar 2025 11:47:51 +0100 Subject: [PATCH] Deprecate all `*_default_options` Globally changing the behavior of the library is a bad idea, as many different libraries may rely on `json` and may not expect it and likely never tested that a different default config works for them. If you need to change the behavior of JSON, it's best to do it only locally, and not globally. In addition the new `JSON::Coder` interface is much more suited for that. Another reason for the deprecation is that it's impossible to make `JSON.load` and `JSON.dump` Ractor-safe with such API. --- CHANGES.md | 4 ++ lib/json/common.rb | 77 +++++++++++++++---------- test/json/json_common_interface_test.rb | 8 ++- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index eca83cda..cd11229c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +* Deprecate `JSON.load_default_options`. +* Deprecate `JSON.unsafe_load_default_options`. +* Deprecate `JSON.dump_default_options`. + ### 2025-03-12 (2.10.2) * Fix a potential crash in the C extension parser. diff --git a/lib/json/common.rb b/lib/json/common.rb index 9094df00..800056c4 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -101,6 +101,29 @@ def create_pretty_state # Sets or Returns the JSON generator state class that is used by JSON. attr_accessor :state + + private + + def deprecated_singleton_attr_accessor(*attrs) + args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : "" + attrs.each do |attr| + singleton_class.class_eval <<~RUBY + def #{attr} + warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args} + @#{attr} + end + + def #{attr}=(val) + warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args} + @#{attr} = val + end + + def _#{attr} + @#{attr} + end + RUBY + end + end end # Sets create identifier, which is used to decide if the _json_create_ @@ -424,28 +447,26 @@ def pretty_generate(obj, opts = nil) module_function :pretty_unparse # :startdoc: - class << self - # Sets or returns default options for the JSON.unsafe_load method. - # Initially: - # opts = JSON.load_default_options - # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} - attr_accessor :unsafe_load_default_options - end - self.unsafe_load_default_options = { + # Sets or returns default options for the JSON.unsafe_load method. + # Initially: + # opts = JSON.load_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} + deprecated_singleton_attr_accessor :unsafe_load_default_options + + @unsafe_load_default_options = { :max_nesting => false, :allow_nan => true, :allow_blank => true, :create_additions => true, } - class << self - # Sets or returns default options for the JSON.load method. - # Initially: - # opts = JSON.load_default_options - # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} - attr_accessor :load_default_options - end - self.load_default_options = { + # Sets or returns default options for the JSON.load method. + # Initially: + # opts = JSON.load_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} + deprecated_singleton_attr_accessor :load_default_options + + @load_default_options = { :allow_nan => true, :allow_blank => true, :create_additions => nil, @@ -581,9 +602,9 @@ class << self # def unsafe_load(source, proc = nil, options = nil) opts = if options.nil? - unsafe_load_default_options + _unsafe_load_default_options else - unsafe_load_default_options.merge(options) + _unsafe_load_default_options.merge(options) end unless source.is_a?(String) @@ -741,9 +762,9 @@ def unsafe_load(source, proc = nil, options = nil) # def load(source, proc = nil, options = nil) opts = if options.nil? - load_default_options + _load_default_options else - load_default_options.merge(options) + _load_default_options.merge(options) end unless source.is_a?(String) @@ -781,14 +802,12 @@ def recurse_proc(result, &proc) # :nodoc: alias restore load module_function :restore - class << self - # Sets or returns the default options for the JSON.dump method. - # Initially: - # opts = JSON.dump_default_options - # opts # => {:max_nesting=>false, :allow_nan=>true} - attr_accessor :dump_default_options - end - self.dump_default_options = { + # Sets or returns the default options for the JSON.dump method. + # Initially: + # opts = JSON.dump_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true} + deprecated_singleton_attr_accessor :dump_default_options + @dump_default_options = { :max_nesting => false, :allow_nan => true, } @@ -841,7 +860,7 @@ def dump(obj, anIO = nil, limit = nil, kwargs = nil) end end - opts = JSON.dump_default_options + opts = JSON._dump_default_options opts = opts.merge(:max_nesting => limit) if limit opts = opts.merge(kwargs) if kwargs diff --git a/test/json/json_common_interface_test.rb b/test/json/json_common_interface_test.rb index 1f157da0..96f7ccdf 100644 --- a/test/json/json_common_interface_test.rb +++ b/test/json/json_common_interface_test.rb @@ -174,9 +174,9 @@ def test_dump_in_io end def test_dump_should_modify_defaults - max_nesting = JSON.dump_default_options[:max_nesting] + max_nesting = JSON._dump_default_options[:max_nesting] dump([], StringIO.new, 10) - assert_equal max_nesting, JSON.dump_default_options[:max_nesting] + assert_equal max_nesting, JSON._dump_default_options[:max_nesting] end def test_JSON @@ -211,6 +211,10 @@ def test_load_file_with_bad_default_external_encoding end end + def test_deprecated_dump_default_options + assert JSON.dump_default_options + end + private def with_external_encoding(encoding)