I have two classes A, and B. Class B overrides the foo method of class A. Class B has a bar method where I want to call the foo method of the super class. What is the syntax for such a call?
class A
def foo
"hello"
end
end
class B < A
def foo
super + " world"
end
def bar
# how to call the `foo` method of the super class?
# something similar to
super.foo
end
end
For class methods I can call the methods up the inheritance chain by explicitly prefixing the class name. I wonder if there is a similar idiom for instance methods.
class P
def self.x
"x"
end
end
class Q < P
def self.x
super + " x"
end
def self.y
P.x
end
end
Edit
My use case is general. For a specific case I know I can use alias technique. This is a common feature in Java or C++, so I am curious to know if it is possible to do this without adding extra code.
In Ruby 2.2, you can use Method#super_method now
For example:
class B < A
def foo
super + " world"
end
def bar
method(:foo).super_method.call
end
end
Ref: https://bugs.ruby-lang.org/issues/9781#change-48164 and https://www.ruby-forum.com/topic/5356938
You can do:
def bar
self.class.superclass.instance_method(:foo).bind(self).call
end
In this particular case you can just alias :bar :foo before def foo in class B to rename the old foo to bar, but of course you can alias to any name you like and call it from that. This question has some alternative ways to do it further down the inheritance tree.
You can alias old_foo foo before redefining it to keep the old implementation around under a new name. (Technically it is possible to take a superclass's implementation and bind it to an instance of a subclass, but it's hacky, not at all idiomatic and probably pretty slow in most implementation to boot.)
Based on #Sony's answer.
In case when you want to call the method method on some my_object and it's already overriden somewhere several classes higher (like for the Net::HTTPRequest#method), instead of doing .superclass.superclass.superclass use the:
Object.instance_method(:method).bind(my_object)
Like this:
p Object.instance_method(:method).bind(request).call(:basic_auth).source_location
Related
From what I understand about self, it refers to the current instance of the class.
Isn't this the default behaviour at all times anyways? For example, isn't
self.var_one = method(args)
equivalent to
var_one = method(args)
If so, what is the use of self?
There are several important uses, most of which are basically to disambiguate between instance methods, class methods, and variables.
First, this is the best way to define class methods:
class Foo
def self.bar
"class method bar"
end
def bar
"instance method bar"
end
end
Foo.bar #returns "class method bar"
foo = Foo.new
foo.bar #returns "instance method bar"
Also, within instance methods self refers to the instance, within class methods it refers to the class, and it can always be used to distinguish from local variables.
class Bar
def self.foo
"foo!"
end
def baz
"baz!"
end
def self.success
foo #looks for variable foo, doesn't find one, looks for class method foo, finds it, returns "foo!"
end
def self.fail
baz #looks for variable baz, doesn't find one, looks for class method baz, doesn't find one, raises exception
end
def instance_success
baz #looks for variable baz, doesn't find one, looks for instance method baz, finds it, returns "baz!"
end
def instance_fail
foo #looks for variable foo, doesn't find one, looks for instance method foo, doesn't find one, raises exception
end
def local_variable
baz = "is my favorite method"
baz #looks for variable baz, finds it, returns "is my favorite method"
end
def disambiguate
baz = " is my favorite method"
self.baz + baz #looks for instance method baz, finds it, looks for local variable baz, finds it, returns "baz! is my favorite method"
end
end
So, in the end, you can avoid using self in many cases, but it's often helpful to use it to make sure that you don't inadvertently create naming conflicts later on. Sometimes those can create bugs that are very hard to find. In the end it's often a matter of personal style.
As noted in the comments, one more really important thing:
In a class, if you have a method like this:
def bar=(string)
...
end
And in another method you call:
def other_method
bar = "abcd"
end
It isn't going to call your bar= method, it's going to create a local variable bar. So, in this case you use self to tell Ruby not to create a local variable:
def other_method
self.bar = "abcd"
end
The same thing applies if you want to take an argument with the name of a method:
def example
...
end
def other_thing(example)
self.example(example)
end
If you left off self Ruby would assume you meant the local variable with the same name.
So, in general, self in method names is used to distinguish between class and instance variables, and everywhere else you use it when Ruby needs help distinguishing between method calls and local variables or local variable assignment.
I hope that makes sense.
In most cases self.foo is indeed redundant because you can just write foo for the same effect, but in this case it is not and the self is required.
var_one = method(args) will create a local variable called var_one, it will not call any method or do anything else to self.
self.var_one = method(args) will call the method var_one= on self with the argument method(args).
Another case where the use of self is non-optional would be if you want to pass it as an argument to a method, i.e. some_method(self) - you can't do that without the self keyword.
One other use of self is to declare class methods (similar to static methods in Java).
class foo
def self.bar
#do class related stuff here
end
end
That being said, you could also have used def foo.bar instead for the method signature.
Here's an example:
def run miles
self.miles = miles
end
In this case self will help. In most cases self is redundant.
When using class inheritance, is there a way to order the methods? I want them ordered, because I am inheriting from a class that executes all public methods, in order.
For example:
class Foo
def action_two
puts "action 2"
end
end
class Bar < Foo
def action_one
puts "action 1"
end
end
Bar.instance_methods # => [:action_two, :action_one, ...]
...and I would like it to return [:action_one, :action_two, ...]
The documentation doesn't say anything about the order and I actually get them in the order you want when I use Ruby 2.1.2. If you want them to come out in a specific order then I think you'll have to do it yourself. You could use the include_super parameter to instance_methods to get just the current class's methods and then ask superclass for its methods:
class Bar < Foo
#...
def self.instance_methods
super(false) + superclass.instance_methods
end
end
If you're going to be dealing with BasicObject as a possible superclass then you'll want to add a superclass.nil? check before calling superclass.instance_methods.
If you're inheritance hierarchy is deep then you should be able to put this hackery into the lowest base class (Foo in this case) and let the subclasses pick it up through inheritance.
In order to do that, you have to define Bar#action_one before inheriting Foo into Bar. Since class inheritance is determined at the creation time of the class that inherits, you cannot do that as is.
A way to overcome this is to make Foo a module instead of a class, and include it into Bar after Bar#action_one is defined.
module Foo
def action_two
puts "action 2"
end
end
class Bar
def action_one
puts "action 1"
end
include Foo
end
Bar.instance_methods
# => [:action_one, :action_two, ...]
Bar.ancestors.each_with_object([]) { |a, o| o << a.instance_methods }.flatten.uniq
In Ruby, I have a DAO class, which is extended by a class that makes managing the connections easier, which is extended by a class that represents and manipulates data in a DB, which is further extended by another class. To use an animal metaphor it would look like this:
class Animal
...
end
class Mammal < Animal
...
end
class Feline < Mammal
...
end
class Cat < Feline
...
end
class Lion < Cat
...
end
...
In PHP, there is __destruct method that runs when you destroy/delete a class. And should that class extend another class, you simply add parent::__destruct() to the class's __destruct method like this:
public function __destruct() {
// Clean up code for this class here
...
// Execute clean up code for Parent class
parent::__destruct();
}
I could have a similar method for all the classes except Animal. Since it doesn't extend anything, the parent::__destruct(); line is no longer valid.
However, as I understand it, Ruby doesn't have a method like this for its objects. A finalizer can be set, but I decided to just put in a cleanup method I can call whenever I want to destroy/delete a class. That would take care of anything that needed doing prior to my setting the class to nil.
This raises a new problem though. If the method is always named cleanup and I call lion_instance.cleanup, I assume it calls the Lion#cleanup. How then to get it to call the cleanup in class Cat and then Feline and on down the chain?
Or is this a wrong approach and you have a better idea?
The Ruby idiom for this is to yield to a block which does work, and when the block returns, do cleanup. Ruby's built-in "File.open" does this:
File.open("/tmp/foo") do |file|
file.puts "foo"
end
When the block ends, the file is closed for you, without you having to do anything. This is an excellent idiom. Here's how you might implement something like that:
class Foo
def self.open(*args)
foo = new(*args)
yield foo
foo.close
end
def initialize
# do setup here
end
def close
# do teardown here
end
end
And to use it:
Foo.open do |foo|
# use foo
end
Foo#close will be caused automatically after the end
This will work with subclassing as well. That's because class methods are inherited just as are instance methods. Here's the superclass:
class Superclass
def self.open(*args)
o = new(*args)
yield o
o.close
end
def initialize
# common setup behavior
end
def close
# common cleanup behavior
end
end
and two derived classes:
class Foo < Superclass
def initialize
super
# do subclass specific setup here
end
def close
super
# do subclass specific teardown here
end
end
class Bar < Superclass
def initialize
super
# do subclass specific setup here
end
def close
super
# do subclass specific teardown here
end
end
to use:
Foo.open do |foo|
# use foo
end
Bar.open do |bar|
# use bar
end
If you really need to make sure that cleanup happens no matter what, then use an ensure clause in the class method:
def self.open(*args)
foo = new(*args)
begin
yield foo
ensure
foo.close
end
end
This way, cleanup happens even if there is an exception in the block.
You can use ObjectSpace.define_finalizer
Something like:
class Animal
def initialize
ObjectSpace.define_finalizer(self, proc { # your code })
end
end
Well since no one answered your question about the method moving its way up the inheritance chain...
class Cat
def rawr
puts "rawr"
end
end
class Kitty < Cat
def rawr
puts "meow"
super
end
end
Cat.new.rawr
"Rawr"
Kitty.new.rawr
"rawr"
"meow"
Within a method, you can access the superclass's method of the same name by calling super.
Here's a classic fizzbuzz in Ruby:
class PrimeChecker
def print_em
1.upto 100 do |fizzbuzz|
if (fizzbuzz % 2) == 0 && (fizzbuzz % 5) == 0
puts "fizzbuzz: " + fizzbuzz.to_s
elsif (fizzbuzz % 5) == 0
puts "fizz: "+fizzbuzz.to_s
elsif (fizzbuzz % 2) == 0
puts 'buzz: ' + fizzbuzz.to_s
else
puts "-" + fizzbuzz.to_s
end
end
end
end
PrimeChecker.print_em
When I execute this, I get this error:
undefined method 'print_em'.
I change the method to self.print_em and it works. Does this mean it's a class method (I think so)? Was the method "not found" before because I can only call such methods in a class on actual instances of the object? If I wanted it to be a instance method what is the syntax for that? I'm trying to understand Ruby, classes and methods better.
Class methods are just that: called on the class. Whereas instance methods are called on an instance of that class. An example is more useful:
class Foo
def self.bar
"This is a class method!"
end
def bar
"This is an instance method!"
end
end
Foo.bar # => "This is a class method!"
foo = Foo.new # This creates "foo" to be a new instance of Foo
foo.bar # => "This is an instance method!"
Note that "class methods" in Ruby are actually methods on the class object's singleton. This is a rather difficult concept to explain, and you can read more about it if you'd like.
It's not a class method as written; you need to run it with an instance of PrimeChecker:
pc = PrimeChecker.new
pc.print_em
Using self. turns it into a class method, runnable with the syntax you show.
It doesn't need to be a class method, it's just that that's how you're trying to execute it.
Q: When I run ruby.rb I get undefined method 'print_em'. I change the method to self.print_em and it works. Does this mean it's a class method (I think so).
A: Yes. class Bar; ... def self.foo defines a class method foo for class Bar.
Q: Was the method "not found" before because I can only call such methods in a class on actual instances of the object?
A: You were first defining it as an instance method. In that case, it is only available to instances of the class.
Q: If I wanted it to be a instance method what is the syntax for that?
A: The way you had it originally: class Bar; def foo defines instance method foo for class Bar
Yes, you are completely correct. Currently, the way you define it, you can evaluate the method with:
PrimeChecker.new.print_em
The reason def self.my_awesome_method defines it on the class side is because the stuff inside
class MyAwesomeClass
end
is being executed in the context of MyAwesomeClass. It's all Ruby code, as you can see! This enables you to do things like this:
class MyAwesomeClass
puts "Hello from innards of #{self}!" #=> Hello from the innards of MyAwesomeClass!
end
Method definitions will also only work if you call them after the definition location, for example:
class MyAwesomeClass
my_awesome_method # produces a nasty error
def self.my_awesome_method
puts "Hello world"
end
my_awesome_method # executes just fine
end
Hope this clears some things up.
Say I have class B derived from class A
Is it possible to invoke overrided method of A like this?
class A
def method1
end
def method2
end
end
class B < A
def method1
### invoke method2 of class A is what I want to do here
end
def method2
end
end
# not exactly duplicate to How do I call an overridden parent class method from a child class? , but we seem want to do the same thing.
I'm assuming here that B is supposed to inherit from A and you simply made a typo in your example code. If this is not the case, there is no way to do what you want.
Otherwise you can do what you want using reflection by binding A's method2 instance method to your current B object and calling it like this:
class A
def method1
end
def method2
end
end
class B < A
def method1
A.instance_method(:method2).bind(self).call
end
def method2
end
end
Note though that you shouldn't pull out the big black-magic-guns like this unless you really need to. In most cases redesigning your class hierarchy so that you don't need to do this is the better alternative.
You can create a synonym for parent method using alias statement and call it from the overriden method:
class A
def method1
puts '1'
end
def method2
puts '2'
end
end
class B < A
alias parent_method1 method1
alias parent_method2 method2
def method1
parent_method2
end
def method2
end
end
b = B.new
b.method1 # => 2
The answer of #sepp2k is technically correct, however I would like to explain why this technique is not appropriate in my opinion (so the question is technically interesting, but leads to the wrong goal):
Ruby does not allow to call super.method2 in the context of method1called in an instance of B, because it is just wrong to do it. Class inheritance should be used when your instances are specializations of the superclass. That includes that you normally only expand behavior, by calling super and doing something additionally before or after that call.
There are languages like Java and others, that allow to call super for another method, and that leads to something similar to spaghetti code, but the object-oriented way. No one understands when which methods are called, so try to avoid it.
So try to find the reason why you want to change the call, and fix that. If your method1 in A is wrong implemented in B, then you should not subclass is.