Project

General

Profile

Actions

Feature #7836

closed

Need a way to get Method and UnboundMethod objects to methods overridden by prepended modules

Added by banister (john mair) over 11 years ago. Updated almost 7 years ago.

Status:
Closed
Target version:
[ruby-core:52160]

Description

See the following code:

module P
  def hello
    puts "from P"
    super
  end
end

class A
  def hello
    puts 'from A'
  end

  prepend P
end

A.instance_method(:hello).source_location == P.instance_method(:hello).source_location  #=> true

Discussion

Since A.instance_method(:hello) effectively returns P.instance_method(:hello) it is impossible to get an UnboundMethod object to the original A#hello method.

Tools like Pry need to access UnboundMethod objects to every active method in the system for debugging purposes.

Possible solution

Simply allow instance_method() to take a second boolean parameter, indicating whether methods injected by prepended modules are to be included, it would default to true:

example:

A.instance_method(:hello) #=> same as P#hello
A.instance_method(:hello, false) #=> return strictly A#hello

Files


Related issues 2 (0 open2 closed)

Related to Ruby master - Bug #7842: An alias of a "prepend"ed method skips the original method when calling superClosednobu (Nobuyoshi Nakada)02/13/2013Actions
Related to Ruby master - Feature #9781: Feature Proposal: Method#super_methodClosedokkez (okkez _)04/28/2014Actions
Actions #1

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

  • Tracker changed from Feature to Bug

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

  • Description updated (diff)
  • ruby -v set to 2.0.0dev

It's a bug.

Actions #3

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r39224.
john, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


proc.c: skip prepends

  • proc.c (mnew): skip prepending modules and return the method bound
    on the given class. [ruby-core:52160] [Bug #7836]

Updated by marcandre (Marc-Andre Lafortune) over 11 years ago

  • Status changed from Closed to Open
  • Assignee set to matz (Yukihiro Matsumoto)

Matz, could you please confirm?

I'm not sure what is the right approach. What should A.instance_method(:hello) return:

  1. the method that A.new.hello will execute, or
  2. the method defined in A?

I feel that 1) a more accurate description of the behavior in 1.9.3. It reflects my understanding. Otherwise, strictly speaking, String.instance_method(:object_id) would raise a NameError!

I would expect:

meth = A.instance_method :hello
a = A.new
meth.bind(a).call # should be the same effect as a.bar
meth.source_location # thus should be the same as a.method(:hello).source_location
meth.owner # and thus should be the same as a.method(:hello).owner

This is always true in Ruby 1.9, and this patch changes that.

I agree with John Mair that there should be a way to get the proper instance method of a class, as he suggests. I would add that String.instance_method(:object_id, false) should raise a NameError, as there is no such instance method defined in String.

Updated by banister (john mair) over 11 years ago

@ marcandre. Another possible approach is to provide UnboundMethod#super. We do something similar in our Method wrapper for Pry: https://github.com/pry/pry/blob/master/lib/pry/method.rb#L394-L402

While we're at it, other useful methods for Method/UnboundMethod could be private?, public?, aliases, singleton? :) but that could be asking for too much ;)

John

Actions #6

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

  • Tracker changed from Bug to Feature

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

  • % Done changed from 100 to 0

Updated by marcandre (Marc-Andre Lafortune) over 11 years ago

banister (john mair) wrote:

@ marcandre. Another possible approach is to provide UnboundMethod#super.

Not a bad idea, for Method also, although I'm not sure if it would be useful to many. You might want to make another feature request for this (and provide a decent justification!).

While we're at it, other useful methods for Method/UnboundMethod could be private?, public?, aliases, singleton? :) but that could be asking for too much ;)

These might be more problematic:

  • privacy
    It is not really an attribute of the method itself. It's an attribute of the class, i.e. does the class provide public access to a method.
    class F
      def priv; end
      alias_method :pub, :priv
      private :priv
    end
    F.new.pub # => nil
    F.new.priv # => NoMethodError: private method `priv' called
    F.instance_method(:pub) == F.instance_method(:priv) # => true

So I think that Module#private_method_defined? and Module#private_instance_methods are the ones you want to use.

  • aliases
    Not sure who would use this, but you can already easily do this by comparing the unbound methods:
    String.instance_methods.group_by{|x| String.instance_method(x)}.map(&:last).reject(&:one?)
     # => [[:==, :===], [:[], :slice], [:length, :size], [:succ, :next], [:succ!, :next!], [:to_s, :to_str], [:concat, :<<],
               [:intern, :to_sym], [:kind_of?, :is_a?], [:send, :__send__], [:object_id, :__id__], [:to_enum, :enum_for]]

Or if you prefer:

    class UnboundMethod
      def aliases
        owner.instance_methods.select{|m| owner.instance_method(m) == self}
      end
    end

    String.instance_method(:size).aliases # => [:length, :size]
  • singleton?

This is a property of the owner, no? Module#singleton_class? already accepted: https://bugs.ruby-lang.org/issues/7609

So if you really want, you will be able to roll your own easily:

    class UnboundMethod
      def singleton?
        owner.is_a?(Class) && owner.singleton_class?
      end
    end

Updated by marcandre (Marc-Andre Lafortune) over 11 years ago

  • Category set to core
  • Target version set to 2.1.0

nobu: Patch looks good, but I would go further and have String.instance_method(:object_id, false) also raise a NameError, for consistency with String.instance_methods(false).include? :object_id # => false.

Did Matz confirm any of this?

Actions #11

Updated by prijutme4ty (Ilya Vorontsov) over 11 years ago

Also it should be mentioned that there is no way to get Method for super call. So one cannot know, for example, number of argments of method down the ancestry chain. For prepending methods there is a workaround - to save link on prepend_features. Or to maintain full hierarchy of methods, but it looks akward.
E.g. I needed such method when created #coerce(other,meth) prepending either usual #coerce(other) method or already defined #coerce(other,meth). It's hard to know whether prepended module should call super with one or two arguments.

Updated by hsbt (Hiroshi SHIBATA) almost 11 years ago

  • Target version changed from 2.1.0 to 2.2.0

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

  • Related to Feature #9781: Feature Proposal: Method#super_method added

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

  • Description updated (diff)

Can't you achieve this by Method#super_method?

Actions #15

Updated by marcandre (Marc-Andre Lafortune) almost 7 years ago

  • Status changed from Open to Closed
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0