Home > Mobile >  Ruby - how to retrieve the superclass/module that defines a method?
Ruby - how to retrieve the superclass/module that defines a method?

Time:09-16

I'm reviewing a ruby codebase at the moment which makes use of metaprogramming. This means that I can't always grep for the names of methods to find out where they're defined.

Is there a way to use ruby reflection to resolve which module or class contains the definition of the method?

For example, if I'm reviewing class A which has a long chain of classes and modules that are included in it's definition. Given an instance a = A.new(a), is there some functionality in ruby I can use to retrieve the class/module that defines a.some_method?

CodePudding user response:

In Ruby, you can get access to a Method reflective proxy object that represents a method using the Object#method method:

foo = []

method = foo.method(:find)
#=> #<Method: Array(Enumerable)#find(*)>

You can ask the method about its owner using the Method#owner method:

method.owner
#=> Enumerable

It is not really possible to distinguish attributes programmatically. An attribute is more about how a method is used than how it is defined.

You can get the parameter list of a method using the Method#parameters method, but that does not get you all the way there. Not having parameters is a necessary but not sufficient condition for an attribute.

Example:

class Foo
  def bar;      42 end # attribute
  def baz; puts 42 end # not an attribute
end

foo = Foo.new

foo.method(:bar).parameters
#=> []

foo.method(:baz).parameters
#=> []

You can try every method of Method or UnboundMethod you can think of, you can try every meta programming trick there is, you simply will not be able to find anything which distinguishes bar being an attribute from baz not being an attribute.

Even worse, methods like Array#length, which clearly are attributes are typically not called attributes. So, even if there were a way to programmatically distinguish attributes from other parameter-less methods, you still wouldn't be able to determine whether or not something is thought of as an attribute. Note that in Rubinius, for example, Array#length is clearly even implemented as an attribute, but it would still typically not be thought of as one.

CodePudding user response:

Not a standard ruby way, but pry gem has a handy ls command, where you could pass your instance as parameter and it will display every method defined for the instance and the classes where those are defined. For example, checking the methods of an integer:

pry(main)(deve)> ls 1
Comparable#methods: between?
Numeric#methods:
   @     as_json  coerce     days         exabyte     gigabyte   html_safe?  in_milliseconds  megabytes  petabyte   pretty_print        real?        second                  terabyte        week
  abs2   blank?   conj       duplicable?  exabytes    gigabytes  i           kilobyte         minute     petabytes  pretty_print_cycle  rect         seconds                 terabytes       weeks
  angle  byte     conjugate  encode_json  fortnight   hour       imag        kilobytes        minutes    phase      quo                 rectangular  singleton_method_added  to_c
  arg    bytes    day        eql?         fortnights  hours      imaginary   megabyte         nonzero?   polar      real                remainder    step                    to_formatted_s
RQRCode::CoreExtensions::Integer::Bitwise#methods: rszf
Integer#methods:
  ceil  denominator  floor  gcdlcm    lcm    months        next       ord      ordinalize  rationalize  times  to_d  to_int                               to_r      upto  years
  chr   downto       gcd    integer?  month  multiple_of?  numerator  ordinal  pred        round        to_bn  to_i  to_json_with_active_support_encoder  truncate  year
JSON::Ext::Generator::GeneratorMethods::Fixnum#methods: to_json
Fixnum#methods:
  %  *      -@  <   <=   ==   >   >>  ^    bit_length  div     even?  inspect    modulo  paragraph   sentence   size  to_csv        to_f        to_s  words  |
  &  **  -  /   <<  <=>  ===  >=  []  abs  dclone      divmod  fdiv   magnitude  odd?    paragraphs  sentences  succ  to_default_s  to_msgpack  word  zero?  ~

a string:

pry(main)(deve)> ls 'asdf'
Comparable#methods: <  <=  >  >=  between?
JSON::Ext::Generator::GeneratorMethods::String#methods: to_json_raw  to_json_raw_object  to_json_without_active_support_encoder
String#methods:
  %                  blueish            codepoints      each_line       greenish      issjis         next              pale          rstrip!           strip          to_datetime                          tr_s
  *                  bold               colorize        empty?          gsub          isutf8         next!             parameterize  safe_constantize  strip!         to_f                                 tr_s!
                     bullet_class_name  colorized?      encode          gsub!         kconv          oct               parse_csv     scan              strip_heredoc  to_i                                 trueish?
  <<                 bytes              concat          encode!         hash          last           on_black          partition     scanf             sub            to_json                              truncate
  <=>                bytesize           constantize     encode_json     hex           length         on_blue           pathmap       scrub             sub!           to_json_with_active_support_encoder  truncate_words
  ==                 byteslice          count           encoding        hide          light_black    on_cyan           pluralize     scrub!            succ           to_msgpack                           uncolorize
  ===                camelcase          crypt           end_with?       html_safe     light_blue     on_green          prepend       setbyte           succ!          to_r                                 underline
  =~                 camelize           cyan            ends_with?      humanize      light_cyan     on_light_black    purple        shellescape       sum            to_s                                 underscore
  []                 capitalize         cyanish         eql?            in_time_zone  light_green    on_light_blue     purpleish     shellsplit        swap           to_str                               unicode_normalize
  []=                capitalize!        dasherize       exclude?        include?      light_magenta  on_light_cyan     red           singularize       swapcase       to_sym                               unicode_normalize!
  acts_like_string?  casecmp            deconstantize   ext             indent        light_red      on_light_green    redish        size              swapcase!      to_time                              unicode_normalized?
  as_json            center             delete          first           indent!       light_white    on_light_magenta  remove        slice             tableize       toeuc                                unpack
  ascii_only?        chars              delete!         force_encoding  index         light_yellow   on_light_red      remove!       slice!            titlecase      tojis                                upcase
  at                 chomp              demodulize      foreign_key     inquiry       lines          on_light_white    replace       split             titleize       tolocale                             upcase!
  b                  chomp!             downcase        freeze          insert        ljust          on_light_yellow   reverse       squeeze           to             tosjis                               upto
  black              chop               downcase!       from            inspect       lstrip         on_magenta        reverse!      squeeze!          to_builder     toutf16                              valid_encoding?
  blank?             chop!              dump            getbyte         intern        lstrip!        on_red            rindex        squish            to_c           toutf32                              white
  blink              chr                each_byte       gray            is_utf8?      magenta        on_white          rjust         squish!           to_csv         toutf8                               whiteish
  block_scanf        classify           each_char       grayish         iseuc         match          on_yellow         rpartition    start_with?       to_d           tr                                   yellow
  blue               clear              each_codepoint  green           isjis         mb_chars       ord               rstrip        starts_with?      to_date        tr!                                  yellowish

or any other instance you have in mind.

  •  Tags:  
  • ruby
  • Related