Feature #7836
closedNeed a way to get Method and UnboundMethod objects to methods overridden by prepended modules
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
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.
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:
- the method that
A.new.hello
will execute, or - 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
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?
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
?
Updated by marcandre (Marc-Andre Lafortune) almost 7 years ago
- Status changed from Open to Closed