Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby - actually get ALL methods for an instance

Tags:

ruby

In this question:

How to list all methods for an object in Ruby?

You can call foo.methods and get all methods. But this does not get all methods. Example in Rails, with ActiveStorage:

Image.first.specimens.first.methods.include?(:variant)
# => false
Image.first.specimens.first.respond_to?(:variant)
# => true
Image.first.specimens.first.variant
Traceback (most recent call last):
        1: from (irb):3
ArgumentError (wrong number of arguments (given 0, expected 1))
Image.first.specimens.first.method(:variant)
# => #<Method: ActiveStorage::Attachment(id: integer, name: string, record_type: string, record_id: integer, blob_id: integer, created_at: datetime)#variant>

Given that an ArgumentError is being raised, it respond_to?s, and I can grab the method, it does have the variant method. But it is not shown with .methods. How can I see a full list of methods?

like image 345
xxjjnn Avatar asked Oct 14 '19 11:10

xxjjnn


1 Answers

Maybe you missed the last paragraph in the accepted answer of linked post How to list all methods for an object in Ruby?:

Added Note that :has_many does not add methods directly. Instead, the ActiveRecord machinery uses the Ruby method_missing and responds_to techniques to handle method calls on the fly. As a result, the methods are not listed in the methods method result.

To make it clearer what happens a code example:

class Foo
  def hello
    puts 'Hello'
  end

  def method_missing(name, *args, &block)
    case name
      when :unknown_method
        puts "handle unknown method %s" % name # name is a symbol
      else
        super #raises NoMethodError unless there is something else defined
    end
  end
end

foo = Foo.new
p foo.respond_to?(:hello) #-> true
p foo.respond_to?(:unknown_method) #-> false
foo.unknown_method  #-> 'handle unknown method unknown_method'
foo.another_unknown_method  #-> Exception

The method unknown_method is never defined, but there is a method to handle unknown methods. So the class makes the impression of an existing method, but there is none.

Maybe How can I get source code of a method dynamically and also which file is this method locate in helps the get information about the content:

Foo.instance_method(:method_missing).source_location

Addition

When you define your own method_missing, then you should also change the behaviour of respond_to? with respond_to_missing?

  def respond_to_missing?(method, *)
    return method == :unknown_method || super
    #or if you have a list of methods:
    #~ return %i{unknown_method}.include?(method) || super
    #or with a regex for 
    #~ method =~ /another_(\w+)/ || super
  end
end

See also `respond_to?` vs. `respond_to_missing?` for details.

like image 116
knut Avatar answered Oct 21 '22 22:10

knut