Feature #7836

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

Added by john mair about 1 year ago. Updated 3 months ago.

[ruby-core:52160]
Status:Open
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:core
Target version:current: 2.2.0

Description

=begin
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.instancemethod(:hello).sourcelocation == P.instancemethod(:hello).sourcelocation #=> true

== Discussion

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

Tools like ((<[Pry]|URL:http://pryrepl.org>)) 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.instancemethod(:hello) #=> same as P#hello
A.instance
method(:hello, false) #=> return strictly A#hello

=end

0001-proc.c-include-prepended-method-flag.patch Magnifier (3.17 KB) Nobuyoshi Nakada, 03/05/2013 08:32 PM


Related issues

Related to ruby-trunk - Bug #7842: An alias of a "prepend"ed method skips the original metho... Closed 02/13/2013

Associated revisions

Revision 39224
Added by Nobuyoshi Nakada about 1 year ago

proc.c: skip prepends

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

History

#1 Updated by Nobuyoshi Nakada about 1 year ago

  • Tracker changed from Feature to Bug

#2 Updated by Nobuyoshi Nakada about 1 year ago

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

It's a bug.

#3 Updated by Nobuyoshi Nakada about 1 year 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. [Bug #7836]

#4 Updated by Marc-Andre Lafortune about 1 year ago

  • Status changed from Closed to Open
  • Assignee set to 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.instancemethod :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.instancemethod(:objectid, false) should raise a NameError, as there is no such instance method defined in String.

#5 Updated by john mair about 1 year 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

#6 Updated by Nobuyoshi Nakada about 1 year ago

  • Tracker changed from Bug to Feature

#7 Updated by Nobuyoshi Nakada about 1 year ago

  • % Done changed from 100 to 0

#9 Updated by Marc-Andre Lafortune about 1 year 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
    aliasmethod :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#privatemethoddefined? and Module#privateinstancemethods 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.instancemethods.groupby{|x| String.instancemethod(x)}.map(&:last).reject(&:one?)
    # => [[:==, :===], [:[], :slice], [:length, :size], [:succ, :next], [:succ!, :next!], [:to
    s, :tostr], [:concat, :<<],
    [:intern, :to
    sym], [:kindof?, :isa?], [:send, :send], [:objectid, :id], [:toenum, :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

#10 Updated by Marc-Andre Lafortune about 1 year 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?

#11 Updated by Ilya Vorontsov about 1 year 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.

#12 Updated by Hiroshi SHIBATA 3 months ago

  • Target version changed from 2.1.0 to current: 2.2.0

Also available in: Atom PDF