Skip to content

json 3.0.x arguments breaking change #1078

Description

@noraj

Am I reading the changelog wrong https://github.com/ruby/json/releases/tag/v3.0.0 or is this a big breaking change?

json 2.21.2

irb(main):001> require 'json'                                                                                                                                                                                        
=> true                                                                                                                                                                                                              
irb(main):002> JSON.load_file('test/file_sample/ssllabs-scan_newwebsite.json', {})                                                                                                                                   
=>                                                                                                                                                                                                                   
[{"host" => "pwn.by",                                                                                                                                                                                                
  "port" => 443,  

json 3.0.2

irb(main):001> require 'json'                                                                                                                                                                                        
=> true                                                                                                                                                                                                              
irb(main):002> JSON.load_file('test/file_sample/ssllabs-scan_newwebsite.json', {})                                                                                                                                   
/home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/json-3.0.2/lib/json/common.rb:327:in 'JSON.load_file': wrong number of arguments (given 2, expected 1) (ArgumentError)                                
                                                                                                                                                                                                                     
    caller: (irb):2                                                                                                                                                                                                  
    callee: /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/json-3.0.2/lib/json/common.rb:327                                                                                                         
    |   def load_file(filespec, **options)                                                                                                                                                                           
            ^^^^^^^^^                                                                                                                                                                                                
        from (irb):2:in '<main>'                                                                                                                                                                                     
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/irb-1.18.0/exe/irb:9:in '<top (required)>'                                                                                               
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/4.0.0/rubygems.rb:305:in 'Kernel#load'                                                                                                                   
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/4.0.0/rubygems.rb:305:in 'Gem.activate_and_load_bin_path'                                                                                                
        from /home/noraj/.asdf/installs/ruby/4.0.6/bin/irb:25:in '<top (required)>'                                                                                                                                  
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:61:in 'Kernel.load'                                                                                
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:61:in 'Bundler::CLI::Exec#kernel_load'                                                             
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:24:in 'Bundler::CLI::Exec#run'                                                                     
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:500:in 'Bundler::CLI#exec'                                                                              
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/command.rb:28:in 'Bundler::Thor::Command#run'                                             
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in 'Bundler::Thor::Invocation#invoke_command'                           
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor.rb:538:in 'Bundler::Thor.dispatch'                                                        
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:35:in 'Bundler::CLI.dispatch'                                                                           
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/base.rb:584:in 'Bundler::Thor::Base::ClassMethods#start'                                  
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:29:in 'Bundler::CLI.start'                                                                              
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/exe/bundle:28:in 'block in <top (required)>'                                                                               
        ... 5 levels...   

it's the same for parse, on 3.0.2 :

irb(main):001> require 'json'
=> true
irb(main):002> JSON.parse(File.read('test/file_sample/ssllabs-scan_newwebsite.json'), {})
/home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/json-3.0.2/lib/json/common.rb:296:in 'JSON.parse': wrong number of arguments (given 2, expected 1) (ArgumentError)

    caller: (irb):2
    callee: /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/json-3.0.2/lib/json/common.rb:296
    |   def parse(source, on_load: nil, object_class: nil, array_class: nil, **options)
            ^^^^^
        from (irb):2:in '<main>'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/irb-1.18.0/exe/irb:9:in '<top (required)>'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/4.0.0/rubygems.rb:305:in 'Kernel#load'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/4.0.0/rubygems.rb:305:in 'Gem.activate_and_load_bin_path'
        from /home/noraj/.asdf/installs/ruby/4.0.6/bin/irb:25:in '<top (required)>'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:61:in 'Kernel.load'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:61:in 'Bundler::CLI::Exec#kernel_load'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:24:in 'Bundler::CLI::Exec#run'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:500:in 'Bundler::CLI#exec'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/command.rb:28:in 'Bundler::Thor::Command#run'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in 'Bundler::Thor::Invocation#invoke_command'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor.rb:538:in 'Bundler::Thor.dispatch'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:35:in 'Bundler::CLI.dispatch'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/base.rb:584:in 'Bundler::Thor::Base::ClassMethods#start'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:29:in 'Bundler::CLI.start'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/exe/bundle:28:in 'block in <top (required)>'
        ... 5 levels...

load_file seems fines

def load_file(filespec, **options)

only the Coder version seems to only take one argument

def load_file(path)

But I thought Coder was to implement a custom serializer


For context, my use case was this compatibility method wrapper, use JSON.load_file on Ruby 3.0+ and JSON.parse(File.read for ruby 2.x.

    # bring JSON.load_file before ruby 3.0.0
    # https://ruby-doc.org/stdlib-3.0.0/libdoc/json/rdoc/JSON.html#method-i-load_file
    def self.json_load_file(filespec, opts = {})
      if RUBY_VERSION < '3.0.0'
        JSON.parse(File.read(filespec), opts)
      else
        JSON.load_file(filespec, opts)
      end
    end

I guess this sentence in the changelog is a massive breaking change:

All methods options are now either keyword arguments or checked like keyword arguments, meaning unknown options such as typos raise ArgumentError.

Introduced in ebf0f29#diff-e08e7b431f64661c031319f3bbbec311665959cd74a86f605975fe62ffe8c93dL19-L250.

But in fact the true culprit is 29dad14 which is totally not retro-compatible. See minimal reproducible code:

irb(main):004* def test_arg_a(main, opts=nil)
irb(main):005> end
=> :test_arg_a
irb(main):006* def test_arg_b(main, ...)
irb(main):007> end
=> :test_arg_b
irb(main):008* def test_arg_c(main, **options)
irb(main):009> end
=> :test_arg_c
irb(main):010> test_arg_a(Object.new, {})
=> nil
irb(main):011> test_arg_b(Object.new, {})
=> nil
irb(main):012> test_arg_c(Object.new, {})
(irb):8:in 'Object#test_arg_c': wrong number of arguments (given 2, expected 1) (ArgumentError)

    caller: (irb):12
    callee: (irb):8
        from (irb):12:in '<main>'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/irb-1.18.0/exe/irb:9:in '<top (required)>'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/4.0.0/rubygems.rb:305:in 'Kernel#load'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/4.0.0/rubygems.rb:305:in 'Gem.activate_and_load_bin_path'
        from /home/noraj/.asdf/installs/ruby/4.0.6/bin/irb:25:in '<top (required)>'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:61:in 'Kernel.load'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:61:in 'Bundler::CLI::Exec#kernel_load'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli/exec.rb:24:in 'Bundler::CLI::Exec#run'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:500:in 'Bundler::CLI#exec'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/command.rb:28:in 'Bundler::Thor::Command#run'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in 'Bundler::Thor::Invocation#invoke_command'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor.rb:538:in 'Bundler::Thor.dispatch'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:35:in 'Bundler::CLI.dispatch'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/vendor/thor/lib/thor/base.rb:584:in 'Bundler::Thor::Base::ClassMethods#start'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/lib/bundler/cli.rb:29:in 'Bundler::CLI.start'
        from /home/noraj/.asdf/installs/ruby/4.0.6/lib/ruby/gems/4.0.0/gems/bundler-4.0.3/exe/bundle:28:in 'block in <top (required)>'
        ... 5 levels...

Which is why my code broke (https://github.com/noraj/tls-map/actions/runs/34795093668/job/104498185535?pr=234) or why it's not safe for rubocop to include json with a lax version constrain (see rubocop/rubocop#15752).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions