Calling super from module method - ruby

I'm trying to override a method located in a Gem in Ruby/Rails, and I'm struggling with some problems.
My goal is to execute custom code when a method from the Gem is called, but also to keep executing the original code.
I tried to abstract the code into the following script:
module Foo
class << self
def foobar
puts "foo"
end
end
end
module Foo
class << self
def foobar
puts "bar"
super
end
end
end
Foo.foobar
Executing this script gives me this error:
in `foobar': super: no superclass method `foobar' for Foo:Module (NoMethodError)
How should I write the overriding method so I can call super with this exception being raised?
PS: The overriding works just fine if I remove the super, but then the original method isn't called and I don't want that.

You can do what you want like this:
module Foo
class << self
alias_method :original_foobar, :foobar
def foobar
puts "bar"
original_foobar
end
end
end

Calling super looks for the next method in the method lookup chain. The error is telling you exactly what you are doing here: there is foobar method in the method lookup chain for Foo, since it is not inheriting from anything. The code you show in your example is just a redefinition of the Foo module, so having the first Foo does nothing.

Related

Why can't Ruby find a method declared right above?

I have a simple file called helper.rb that looks like this:
module MyHelper
def initialize_helper
puts "Initialized"
end
initialize_helper()
end
And another simple file like this:
require_relative 'helper.rb'
include MyHelper
puts "Done"
But when I run this second file, it results in this error:
helper.rb:6:in `<module:MyHelper>': undefined method `initialize_helper' for MyHelper:Module (NoMethodError)
Why can't Ruby find this initializeHelper method defined directly above where I'm calling it???
Try
def self.initialize_helper
puts "Initialized"
end
Without the self., you're declaring an instance method intended to be called on objects, not the module itself. So, for instance, your original code is intended to be used like
module MyHelper
def initialize_helper
puts "Initialized"
end
end
class Foo
include MyHelper
end
Foo.new.initialize_helper
But if you want to call it on the module, you need to have self. in front of it to make it a method on the module itself.

Call a function and method with the same name in Ruby

Forgive me for my english.
I am a php programmer and now i want to learn Ruby.
In php if you want to call function "foo" within a class, you simply call foo(), and if you want to call method "foo" you call this->foo().
The question is, is it possible to call function and method with the same name in Ruby?
For example:
def foo
puts "In foo function"
end
class A
def call_foo
foo
#How can i call foo function, not a method?
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo method"
There is no such thing as a function in Ruby, only methods.
If you define a method at the top-level it is an instance method of Object.
If you define a class without a superclass, it's superclass is Object.
So, your A#foo simply overrides Object#foo. And if it overrides Object#foo, it should respect its contract. You should never need to call Object#foo on an A, if A#foo implements Object#foo's contract correctly (and it should, otherwise it would be a violation of the Liskov Substitution Principle). If you want to reuse Object#foo's implementation within A#foo, you can defer to the superclass implementation using super.
Note: what you want is possible using reflection, but the correct solution would be to fix your design:
def foo
puts "In foo function"
end
class A
def call_foo
self.class.superclass.public_instance_method(:foo).bind(self).()
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo function"
The foo method outside of your class definition is bound to Object, which is an instance of Class. So to call your method you can do:
> Object.foo­
=> "In foo function"
But as it was pointed out before, you should rather declare this method in an appropiate class.
You can also declare this method as class method with:
class A
def self.foo
puts "In foo class method"
end
end
Now you can call it without creating an A instance:
> A.foo
=> puts "In foo class method"

Why is call to super failing here

module A
def foo
if super.respond_to? :foo
puts 'super responded to :foo'
end
end
end
class Lab
include A
end
puts Lab.ancestors.inspect #=> Lab, A, Object, Kernel, BasicObject]
Lab.new.foo
foo': super: no superclass methodfoo' for # (NoMethodError)
I was assuming that in this case the call to super would go to Object and then to BasicObject and finally false would be returned.
Why I'm getting no superclass method foo ?
I'm using ruby 1.9.3 .
Super calls the method of the same name on a parent class.
if super.respond_to? foo
That line will be calling foo on any parent class in the hierachy.
EDIT:
You probably want to do something like
self.ancestors.select{|a| a.respond_to? :foo}.size > 0
as the check.
Your code doesn't really make sense: you guard the call to super with a condition, but you are calling super in that condition anyway. In other words: you are calling super in order to determine whether it is safe to call super. Therefore, you will get an error if there is no method called foo in the ancestry chain.

execute a method in a ruby file

I have this dummy ruby class file (Bar.rb):
class Bar
foo() # execute foo()
def foo()
puts "Hello world, "
end
end
And I ran the file with:
$ ruby Bar.rb
I was expecting to see "Hello, world" in the command, but got this error:
undefined local variable or method `foo' for Bar:Class (NameError)
from bar.rb:3:in `<main>'
So how do I execute a method? Does Ruby have any main method (as in Java or C/C++)?
A couple of reasons this doesn't work as you have written it.
The method foo hasn't been declared before you attempt to call it.
The method foo, as you have declared it, is an instance method. You're not invoking it on an instance of the class.
This would work:
class Bar
def self.foo
end
foo
end
As others have said, though, you probably don't need to wrap this in a class.
You don't need any class, you can call any methods you like right from the file itself:
bar.rb:
puts "Hello, world!"
If you want to stick with your code, you are calling foo before its declaration, which obviously doesn't work.
First you define the method, then you call it.
No need for a main procedure, the first code outside a class or method will be executed first. The script itself is the main. I'm sure there are better definitions for this, but i'm sure you will underdstand.
def foo()
puts "Hello world, "
end
foo() # execute
Also no need to put this in a class, then you would have to initiate her first.
class Bar
def foo()
puts "Hello world, "
end
end
bar = Bar.new
bar.foo

When an instance method calls a class method, I have to use self.class_method?

I'm getting an error so I guess I have to reference a class method from inside of an instance method with self.class_method_name, but why is that?
Shouldn't it resolve this by itself? Confused.
def self.blah(string)
..
end
def some_method()
some_thing = blah("hello")
end
If you have
# This won't work
class Foo
def self.blah(string)
puts "self.blah called with a string of #{string}"
end
def some_method
# This won't work
self.blah("hello")
end
end
foo = Foo.new
foo.some_method
It won't work, because it'll look for the instance method Foo#blah. Instead, you're looking for Foo.bar.
To make some_method call Foo.bar, you have to make some_method refer to the Foo class, and then call blah on it.
class Foo
def self.blah(string)
puts "self.blah called with a string of #{string}"
end
def some_method
# This will work
self.class.blah("hello")
end
end
foo = Foo.new
foo.some_method
The reason you have def self.blah to define the method, but self.class.blah to call the method, is that in the former, self refers to the Foo class, while in the latter, self refers to the foo object, so you need self.class to refer to the Foo class.
It may be easier to think of self as part of the method name, that way it's clear that you never defined a blah method, you defined only a self.blah method. (To clarify: the previous sentence shouldn't be thought of too much, so please don't read into it, as it's not how things are actually working, just a sort of "layman's terms" attempt at describing why it doesn't work.)
Also, what if you had defined a blah instance method in addition to the class method? If calling blah was enough to access the class method, how would you call the instance method?
Finally, there really isn't any such thing as a class method in Ruby, "class methods" are really methods of the singleton class.

Resources