Project

General

Profile

Actions

Bug #19711

closed

NoMethodError "private method `new' called for class" since bebd05fb51ea65bc57344b67100748200f8311eb

Added by yahonda (Yasuo Honda) 11 months ago. Updated 10 months ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 3.3.0dev (2023-06-03T00:35:18Z master bebd05fb51) [x86_64-linux]
[ruby-core:113756]

Description

Rails CI against ruby3.3.0dev has been failing https://buildkite.com/rails/rails/builds/96929#0188841c-cf1f-46d6-b48b-f510a5675262/1069-1078
According to git bisect, this change has been triggered via bebd05fb51ea65bc57344b67100748200f8311eb

Steps to reproduce (that may not be minimum though)

$ gem install activesupport
$irb
require 'active_support/deprecation'
klass = Class.new(ActiveSupport::Deprecation)
deprecator = klass.new

Expected behavior

It should successfully work as the previous commit does.

$ ruby -v
ruby 3.3.0dev (2023-06-02T21:16:52Z master 4e26ae3cb9) [x86_64-linux]
$ gem install activesupport
Successfully installed activesupport-7.0.5
Parsing documentation for activesupport-7.0.5
Done installing documentation for activesupport after 1 seconds
1 gem installed
$ irb
irb(main):001:0> require 'active_support/deprecation'
=> true
irb(main):002:0> klass = Class.new(ActiveSupport::Deprecation)
=> #<Class:0x00007f683e01a5b0>
irb(main):003:0> deprecator = klass.new
=>
#<#<Class:0x00007f683e01a5b0>:0x00007f683e2207b0
...
irb(main):004:0>

Actual behavior

It gets "(irb):5:in <main>': private method new' called for class #Class:0x00007fbd57dd9b10 (NoMethodError)"

$ ruby -v
ruby 3.3.0dev (2023-06-03T00:35:18Z master bebd05fb51) [x86_64-linux]
$ gem install activesupport
Successfully installed activesupport-7.0.5
Parsing documentation for activesupport-7.0.5
Done installing documentation for activesupport after 1 seconds
1 gem installed
$ irb
irb(main):001:0> require 'active_support/deprecation'
=> true
irb(main):002:0> klass = Class.new(ActiveSupport::Deprecation)
irb(main):003:0> deprecator = klass.new
(irb):3:in `<main>': private method `new' called for class #<Class:0x00007f7e13282bc8> (NoMethodError)
	from <internal:kernel>:187:in `loop'
	from /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/irb-1.7.0/exe/irb:9:in `<top (required)>'
	from /home/yahonda/.rbenv/versions/trunk/bin/irb:25:in `load'
	from /home/yahonda/.rbenv/versions/trunk/bin/irb:25:in `<main>'
irb(main):004:0>
irb(main):003:0> require 'active_support/deprecation'
=> true
irb(main):004:0>       klass = Class.new(ActiveSupport::Deprecation)
=> #<Class:0x00007fbd57dd9b10>
irb(main):005:0>       deprecator = klass.new
(irb):5:in `<main>': private method `new' called for class #<Class:0x00007fbd57dd9b10> (NoMethodError)
	from <internal:kernel>:187:in `loop'
	from /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/irb-1.7.0/exe/irb:9:in `<top (required)>'
	from /home/yahonda/.rbenv/versions/trunk/bin/irb:25:in `load'
	from /home/yahonda/.rbenv/versions/trunk/bin/irb:25:in `<main>'
irb(main):006:0>

Updated by ioquatix (Samuel Williams) 11 months ago

module ActiveSupport
  class Deprecation
    include Singleton

Why is Rails trying to call new on a singleton class?

What is the expected behaviour of calling new on a subclass of a Singleton?

Updated by dpepper (Daniel Pepper) 11 months ago

Looks like https://github.com/rails/rails/blob/main/activesupport/test/deprecation_test.rb calls .new on the Deprecation Singleton a bunch of places. This behavior should not be possible on a singleton, however .new is explicitly marked as public in InstanceDelegator.

module ActiveSupport
  class Deprecation
    include Singleton
    include InstanceDelegator  # <==
module ActiveSupport
  class Deprecation
    module InstanceDelegator # :nodoc:
      def self.included(base)
        ...
        base.public_class_method :new  # <== makes otherwise private method public again
      end

https://github.com/rails/rails/blob/main/activesupport/lib/active_support/deprecation/instance_delegator.rb#L10

Essentially, this is what's happening:

klass = Class.new do
  include Singleton
  public_class_method :new
end

Class.new(klass).new  # <== used to work but explodes after the change

The Singleton PR inadvertently changed .new to be marked private in both included and inherited (priorly just the when included), hence .new was made private, public, then unexpectedly private again. I agree with ioquatix in wondering why .new is called on a singleton, but happy to scope down the Singleton change to avoid changing behavior until we want to do that explicitly.

Updated by ioquatix (Samuel Williams) 11 months ago

@yahonda (Yasuo Honda) do you have any opinion on Rails' usage as outlined above?

Updated by byroot (Jean Boussier) 11 months ago

do you have any opinion on Rails' usage as outlined above?

Before even looking at the issue, we'll fix it if necessary, but we need advance notice first, otherwise this will cause a mess for people upgrading to 3.3.

Updated by yahonda (Yasuo Honda) 11 months ago

This code was added almost 10 years ago via https://github.com/rails/rails/commit/71993c6f9770b1350aa41fe8c68f1dd2c7800403 and I have not found the reason why yet.

My opinion is I also want some deprecation cycle before dropping this behavior.

Actions #7

Updated by jeremyevans0 (Jeremy Evans) 10 months ago

  • Status changed from Open to Closed
Actions

Also available in: Atom PDF

Like0
Like0Like1Like0Like0Like0Like1Like0