3

In one of Ruby examples I see the following code:

require 'net/http'
req = Net::HTTP::Get.new( "http://localhost:8080/" )
req.basic_auth( "user", "password" )

What is the easiest way to know what Ruby class actually implements this basic_auth method or is it dynamically generated? I have checked public_methods of Net::HTTP::Get and it's definitely not there. But how to check what class actually implements it?

prusswan
  • 6,853
  • 4
  • 40
  • 61
grigoryvp
  • 40,413
  • 64
  • 174
  • 277
  • In general, you can use something akin to `req.method(:basic_auth).owner` - _but_ this doesn't work here because `method` has been redefined in `Net::HTTP::Get`... – Chowlett Jan 25 '12 at 12:32
  • @Chowlett Thanks, that's exactly what i'm looking for. Maybe it's possible to call original `Kernel.method` on `Net::HTTP::Get` so redefinition will not interfere? – grigoryvp Jan 25 '12 at 12:41
  • I couldn't figure out a way to do so; `method`'s receiver should be the object which receives the method you're interested in (because it wants to bind the method). But perhaps you can find something I can't. – Chowlett Jan 25 '12 at 12:51

2 Answers2

8

Generally, you would use the Kernel#method method to get the Method object for the method in question and then you would use the Method#owner method to ask the Method object where it was defined.

So,

req.method(:basic_auth).owner
# => Net::HTTPHeader

should answer your question.

Except, in this particular case, that won't work because req is a Net::HTTP::Get object and Net::HTTP::Get overrides the method method to mean something completely different. In particular, it doesn't take an argument, thus the above code will actually raise an ArgumentError.

However, since Net::HTTP::Get inherits from Object and Object mixes in Kernel, it is legal to bind the Kernel#method method to an instance of Net::HTTP::Get:

Kernel.instance_method(:method).bind(req).(:basic_auth).owner
# => Net::HTTPHeader

So, there's your answer: the method is defined in Net::HTTPHeader.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • For this particular case, as alternative, you can do this without binding method: `req.class.instance_method(:basic_auth).owner #=> HTTPHeader` – Aliaksei Kliuchnikau Jan 25 '12 at 12:53
  • Just to clean up any misconceptions though, `Object` doesn't inherit from `Kernel`, `Kernel` is a module which `Object` includes. I realize you probably know this but I didn't want the wording to confuse anybody who may have thought the chain was `Net::HTTP::Get < Object < Kernel`. +1 Great answer! – Lee Jarvis Jan 25 '12 at 12:54
  • @injekt: A mixin is just a class parameterized by its superclass :-) And mixin inheritance is inheritance. But you're right, that's not how those terms are typically used in Ruby, I'll edit that. – Jörg W Mittag Jan 25 '12 at 13:01
0

I defined an Object method called which (to resemble the *nix which command) to discover where a method is defined.

Here is the code:

class Object
  def which method
    self.class.ancestors.find { |a| a.instance_methods(false).include? method.to_s }
  end
end

Here is how I use:

puts req.which :basic_auth    #=> Net::HTTPHeader
Sony Santos
  • 5,435
  • 30
  • 41