Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method name is in list returned by Object#singleton_methods but not accessible using Object#singleton_method

I'm confused with the difference between Object#singleton_method and Object#singleton_methods.

I thought that the result in Object#singleton_methods is the true set of !!Object#singleton_method(:name), but it seems to be different.

Here is the example script:

require "active_support/deprecation"
# [:debug=, :debug]
ActiveSupport::Deprecation.singleton_methods(false).grep(/debug/)
# [:debug=, :debug]
ActiveSupport::Deprecation.singleton_methods.grep(/debug/) 

begin
  ActiveSupport::Deprecation.singleton_method(:debug) # exception
rescue => e
  puts e.backtrace
  raise
end

Gemfile is

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
gem 'activesupport', '5.1.6'

The result is this:

% bundle install
Fetching gem metadata from https://rubygems.org/..............
Resolving dependencies...
Using concurrent-ruby 1.0.5
Using i18n 1.0.0
Using minitest 5.11.3
Using thread_safe 0.3.6
Using tzinfo 1.2.5
Using activesupport 5.1.6
Using bundler 1.16.1
Bundle complete! 1 Gemfile dependency, 7 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
% bundle exec ruby test.rb
test.rb:8:in `singleton_method'
test.rb:8:in `<main>'
Traceback (most recent call last):
    1: from test.rb:8:in `<main>'
test.rb:8:in `singleton_method': undefined singleton method `debug' for `ActiveSupport::Deprecation' (NameError)

I expected that ActiveSupport::Deprecation.singleton_method(:debug) would return #<Method: ActiveSupport::Deprecation.debug>, but raise exception.

I don't understand why. Of course, I know the basic usage in singleton_method and singleton_methods:

# everything is OK!
class Sample; end

class << Sample
  def test_method; end
end

# These two expressions behave as I expect.
## [:test_method]
Sample.singleton_methods(false).grep(/test_method/) 
## #<Method: Sample.test_method>
Sample.singleton_method(:test_method)

Thanks.

like image 521
Kenta Nakajima Avatar asked Jan 28 '26 23:01

Kenta Nakajima


1 Answers

Update: It looks like this is a bug in Ruby. Vasiliy Ermolovich has created an issue along with a patch to address it. The fix has already been merged so this will be resolved in an upcoming update.


This isn't a full answer to your question I'm afraid, but after a bit of digging I've been able to make a minimal example that demonstrates the same thing without the dependency on Active Support. I thought this might still be useful to you for your investigations, or might help someone else who can come along with a complete explanation.

It appears that the unexpected behaviour of singleton_method comes from use of Module.prepend which ActiveSupport::Deprecation uses here.

The same error can be seen with this small example:

module Empty; end

class MyClass
  singleton_class.prepend(Empty)
  def self.my_method
    puts "my method called"
  end
end

p MyClass.singleton_methods(:false)
m = MyClass.singleton_method(:my_method)
m.call

Running this gives:

❯ ruby example.rb
[:my_method]
Traceback (most recent call last):
    1: from example.rb:11:in `<main>'
example.rb:11:in `singleton_method': undefined singleton method `my_method' for
`MyClass' (NameError)

With the call to prepend removed this runs as you would expect:

❯ ruby example.rb
[:my_method]
my method called
like image 70
mikej Avatar answered Jan 31 '26 12:01

mikej



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!