Overwrite class method from another module - ruby

I want to modify the existing module A from a library:
module A
class << self
def foo
bar('Baz')
end
private
def bar(val)
val.upcase
end
end
end
A.foo
=> "BAZ"
module B
extend A
def self.bar(val)
val.downcase
end
end
B.foo # hoping for 'baz', but instead:
NoMethodError: undefined method `foo' for B:Module
Is there a way to re-use method .foo from A and only modify method .bar?

extend A doesn't work, because foo and bar are no instance methods of A, but of A's singleton class. To re-use those methods, you have to either create a copy of A as mudasobwa described, or you can use a refinement of A's singleton class like this:
module B
extend(refine(A.singleton_class) do
def bar(val)
val.downcase
end
end)
end
B.foo # => "baz"
You cannot use extend A.singleton_class as extend doesn't accept a class as argument. refine returns a module which is exactly what's needed.

Assuming you have A declared as above, the following code would do:
▶ B = A.clone
#⇒ B
▶ (class << B; self; end).send :define_method, :bar do |val|
val.downcase
end
▶ A.foo
#⇒ "BAZ"
▶ B.foo
#⇒ "baz"
There probably should be less tricky way, but I can’t yet figure it out.

Related

How to work through name collisions in ruby

Two modules Foo and Baa respectively define a method with the same name name, and I did include Foo and include Baa in a particular context.
When I call name, how can I disambiguate whether to call the name method of Foo or Baa?
Only the order of modules inclusion decides which one will get called. Can't have both with the same name - the latter will override the former.
Of course, you can do any tricks, just from the top of my head:
module A
def foo
:foo_from_A
end
end
module B
def foo
:foo_from_B
end
end
class C
def initialize(from)
#from = from
end
def foo
from.instance_method(__method__).bind(self).call
end
private
attr_reader :from
end
C.new(A).foo #=> :a_from_A
C.new(B).foo #=> :a_from_B
But that's no good for real life use cases :)
Technically, there is no name collision because the method foo is redefined.
In the following exemple, A.foo is redefined and is never called
module A
def foo
raise "I'm never called"
end
end
module B
def foo
puts :foo_from_B
end
end
class C
include A
include B
end
C.new.foo
# =>
# foo_from_B
If you write A and B module, you can use super to call previous definition of foo. As if it where an inherited method.
module A
def foo
puts :foo_from_A
end
end
module B
def foo
super
puts :foo_from_B
end
end
class C
include A
include B
end
C.new.foo
# =>
# foo_from_A
# foo_from_B
There are side effects and I would not use this but this is doing the trick :
module A
def foo
puts :foo_from_A
end
end
module B
def foo
puts :foo_from_B
end
end
class C
def self.include_with_suffix(m, suffix)
m.instance_methods.each do |method_name|
define_method("#{method_name}#{suffix}", m.instance_method(method_name))
end
end
include_with_suffix A, "_from_A"
include_with_suffix B, "_from_B"
end
c= C.new
c.foo_from_A
c.foo_from_B
begin
c.foo
rescue NoMethodError
puts "foo is not defined"
end
# =>
# foo_from_A
# foo_from_B
# foo is not defined
Provided none of the methods of Foo or Baa call name (which seems a reasonable assumption), one can simply create aliases.
module Foo
def name; "Foo#name"; end
end
module Baa
def name; "Baa#name"; end
end
class C
include Foo
alias :foo_name :name
include Baa
alias :baa_name :name
undef_method :name
end
c = C.new
c.foo_name
#=> "Foo#name"
c.baa_name
#=> "Baa#name"
C.instance_methods & [:foo_name, :baa_name, :name]
#=> [:foo_name, :baa_name]
The keyword alias is documented here. One may alternatively use the method #alias_method. See this blog for a comparison of the two.
Module#undef_method is not strictly necessary. It's just to ensure that an exception is raised if name is called.
You should definetely read about method lookups.
Anyway, I would do it this way:
module Foo
def name
:foo
end
end
module Bar
def name
:bar
end
end
class MyClass
include Foo
include Bar
def foo_name
Foo.instance_method(:name).bind(self).call
end
def bar_name
Bar.instance_method(:name).bind(self).call
end
#
# or even like this: obj.name(Foo)
#
def name(mod)
mod.instance_method(:name).bind(self).call
end
end
BTW if you are using Module#instance_method and UnboundMethod#bind you don't really need to include specific module. This code works:
Foo.instance_method(:name).bind('any object (e.g. string)').call

Behaviour of instance_eval

My understanding of instance_eval was that if I have module M then the following were equivalent:
module M
def foo
:foo
end
end
class C
class << self
include M
end
end
puts C.foo
equivalent to:
module M
def foo
:foo
end
end
class C
end
C.instance_eval do
include M
end
puts C.foo
However, the first example prints :foo and the second throws a NoMethodError? (Ruby 2.3.0)
In both cases above, if I had replaced:
include M
with:
def foo
:foo
end
ie directly defining the method rather than including a module then both cases would have resulted in a C.foo method being defined. Should I be surprised at this difference between include and defining the method directly?
Or does it ever even make sense to call include within the context of instance_eval? Should it only ever be called within a class_eval?
In each of these cases, what object are you calling include on? In your first example, you're calling include on C's singleton class:
class C
class << self
p self == C.singleton_class
include M
end
end
# => true
p C.foo
# => :foo
...so your include line is equivalent to C.singleton_class.include(M).
In your second example, however, you're calling include on C itself:
class C
end
C.instance_eval do
p self == C
include M
end
# => true
p C.foo
# => NoMethodError: undefined method `foo' for C:Class
p C.new.foo
# => :foo
...so you're doing the equivalent of C.include(M), which is the same as:
class C
p self == C
include M
end
# => true
p C.new.foo
# => :foo
What would work like you want would be to call instance_eval on C's singleton class:
class D
end
D.singleton_class.instance_eval do
p self == D.singleton_class
include M
end
# => true
p D.foo
# => :foo
Module#class_eval() is very different from Object#instance_eval(). The instance_eval() only changes self, while class_eval() changes both self and the current class.
Unlike in your example, you can alter class_instance vars using instance_eval though, because they are in the object scope as MyClass is a singleton instance of class Class.
class MyClass
#class_instance_var = 100
##class_var = 100
def self.disp
#class_instance_var
end
def self.class_var
##class_var
end
def some_inst_method
12
end
end
MyClass.instance_eval do
#class_instance_var = 500
def self.cls_method
##class_var = 200
'Class method added'
end
def inst_method
:inst
end
end
MyClass.disp
#=> 500
MyClass.cls_method
#=> 'Class method added'
MyClass.class_var
#=> 100
MyClass.new.inst_method
# undefined method `inst_method' for #<MyClass:0x0055d8e4baf320>
In simple language.
If you have a look in the upper class defn code as an interpreter, you notice that there are two scopes class scope and object scope. class vars and instance methods are accessible from object scope and does not fall under jurisdiction of instance_eval() so it skips such codes.
Why? because, as the name suggests, its supposed to alter the Class's instance(MyClass)'s properties not other object's properties like MyClass's any object's properties. Also, class variables don’t really belong to classes—they belong to class hierarchies.
If you want to open an object that is not a class, then you can
safely use instance_eval(). But, if you want to open a class definition and define methods with def or include some module, then class_eval() should be your pick.
By changing the current class, class_eval() effectively reopens the class, just like the class keyword does. And, this is what you are trying to achieve in this question.
MyClass.class_eval do
def inst_method
:inst
end
end
MyClass.new.inst_method
#=> :inst

How to create an instance with part of the methods of a class in Ruby?

I have class A with methods X and Y. Now I want to create an instance but only want it to have method X from class A.
How should I do it? Should it be by deleting method Y for the instance when creating it? Your help is appreciated!
You should not do this. You should instead share the problem you're solving and find a better pattern for solving it.
An example for solving this problem a little differently:
class A
def x; end
end
module Foo
def y; end
end
instance_with_y = A.new
instance_with_y.send :include, Foo
instance_with_y.respond_to? :y #=> true
Here is one way to solve the problem :
class X
def a
11
end
def b
12
end
end
ob1 = X.new
ob1.b # => 12
ob1.singleton_class.class_eval { undef b }
ob1.b
# undefined method `b' for #<X:0x9966e60> (NoMethodError)
or, you could write as ( above and below both are same ) :
class << ob1
undef b
end
ob1.b
# undefined method `b' for #<X:0x93a3b54> (NoMethodError)
It's possible to do what you want with ruby, as ruby can be very malleable like that, but there are much better ways. What you want to achieve seems like a really bad idea.
The problem you just described a problem inheritance is designed to solve. So really, you have two classes. Class A and also class B which inherits from class A.
class A
def foo
'foo'
end
end
# B inherits all functionality from A, plus adds it's own
class B < A
def bar
'bar'
end
end
# an instance of A only has the method "foo"
a = A.new
a.foo #=> 'foo'
a.bar #=> NoMethodError undefined method `bar' for #<A:0x007fdf549dee88>
# an instance of B has the methods "foo" and "bar"
b = B.new
b.foo #=> 'foo'
b.bar #=> 'bar'

How do I hide a method from a superclass?

I have a method in a superclass from a third-party gem which I want to hide. I would prefer if it is impossible to call this method at all, so not just override it and leave the body empty.
I believe this may be what you're looking for:
undef_method :foo
This will prevent any calls to the method foo.
In contrast, this will not achieve the same effect:
remove_method :foo
That will remove the method from the child, but will still pass through up the inheritance chain.
Docs: undef_method and remove_method
Use the undef keyword.
class A
def foo
5
end
end
class B < A
undef foo
end
A.new.foo #=> 5
B.new.foo #=> NameError: undefined local variable or method `foo'
That is wrong OOP that you're trying to do there. I suggest you use composition instead of inheritance.
require 'forwardable'
class SomeBaseClass
def foo
puts 'foo'
end
def bar
puts 'bar'
end
def quux
puts 'quux'
end
end
class MyClass
def initialize
#base = SomeBaseClass.new
end
extend Forwardable
def_delegators :#base, :foo, :bar # but not quux
end
mc = MyClass.new
mc.foo
mc.bar
mc.quux
# >> foo
# >> bar
# ~> -:32:in `<main>': undefined method `quux' for #<MyClass:0x007febcc155210> (NoMethodError)

"Personal" method in ruby

I'm looking for a way of making a method "personal" - note NOT PRIVATE to a class
here is an example - by "personal" I mean the behaviour of method "foo"
class A
def foo
"foo"
end
end
class B < A
def foo
"bar"
end
end
class C < B
end
a=A.new; b=B.new;c=C.new
I'm looking for a way of producing the following behaviour
a.foo #=> "foo"
b.foo #=> "bar"
c.foo #=> "foo" (ultimate base class method called)
Instead of creating 'personal' methods, change your inheritance structure.
It appears that you want the C class to have only some of the same functionality of the B class while not making changes to the A class.
class A
def foo
"foo"
end
end
class BnC < A
end
class B < BnC
def foo
"bar"
end
end
class C < BnC
end
a=A.new; b=B.new;c=C.new
There's no standard way of doing this. It circumvents how inheritance works. You could implement B's method to do the logic like this:
def foo
instance_of?(B) ? "bar" : super
end
And you could of course define a method on Class that would do this for you similar to public and private.
class Class
def personal(*syms)
special_class = self
syms.each do |sym|
orig = instance_method(sym)
define_method(sym) {|*args| instance_of?(special_class) ? orig.bind(self).call(*args) : super}
end
end
end
Then you can personal :foo in B just like you'd private :foo.
(This isn't at all optimized and I didn't implement the zero-argument behavior that public and private have because frankly it's a huge PITA to do right and even then it's a hack.)
Seems like it could be confusing, but here's one option:
class A
def foo
"foo"
end
end
class B < A
def initialize #when constructing, add the new foo method to each instance
def self.foo
"bar"
end
end
end
class C < B
def initialize #when constructing, do nothing
end
end
More generally, using a similar approach, you can always add a method to a given instance, which of course has no effect on inherited classes or indeed on other instances of the same class.
If you give us specifics of what you're ultimately trying to accomplish we can probably be more helpful.
Answering this is a bit tricky since I don't really see what you want to accomplish in practice, but you could try something like
class C < B
def foo
self.class.ancestors[-3].instance_method(:foo).bind(self).call
end
end
(The ancestors[-3] assumes that A inherits from Object and Kernel and your intent was to access the method from the topmost non-builtin class. Of course you could substitute self.class.ancestors[-3] with just A, or figure out the class from the Array ancestors yourself, etc.)
In practice it would be simpler to alias the original in class B if you can modify it (i.e. alias :foo_from_A :foo in class B < A before the new def foo, then you can call foo_from_A in C). Or just redefine what you want in C. Or design the whole class hierarchy differently.
You can write a shortcut function to handle personalizing methods.
def personalize(*methodNames)
old_init = instance_method(:initialize)
klass = self
modul = Module.new {
methodNames.each { |m|
define_method(m, klass.instance_method(m)) if klass.method_defined?(m)
}
}
methodNames.each { |m|
remove_method(m) if method_defined?(m)
}
define_method(:initialize) { |*args|
# I don't like having to check instance_of?, but this is the only way I
# could thing of preventing the extension of child classes. At least it only
# has to happen once, during initialization.
extend modul if instance_of?(klass)
old_init.bind(self).call(*args)
}
self
end
class A
def foo
"foo"
end
end
class B < A
def foo
"bar"
end
def bam
'bug-AWWK!'
end
personalize :foo, :bam, :nometh
end
class C < B
end
a=A.new; b=B.new; c=C.new
a.foo #=> "foo"
b.foo #=> "bar"
b.bam #=> "bug-AWWK!"
c.foo #=> "foo"
C.instance_method(:foo) # => #<UnboundMethod: C(A)#foo>
c.bam #throws NoMethodError
Sometimes you don't really want an "is a" (inheritance) relationship. Sometimes what you want is "quacks like a." Sharing code among "quacks like a" classes is easily done by using modules to "mix in" methods:
#!/usr/bin/ruby1.8
module BasicFoo
def foo
"foo"
end
end
class A
include BasicFoo
end
class B
def foo
"bar"
end
end
class C
include BasicFoo
end
p A.new.foo # => "foo"
p B.new.foo # => "bar"
p C.new.foo # => "foo"

Resources