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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With