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.
Related
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"
class MyClass
def method_missing(id,*args,&block)
return A if something
return B if something_else
super
end
end
Here if neither A nor B is returned, super is called, which will raise a NoMethodError. I suppose here the super is the superclass of MyClass. However, how is NoMethodError raised since here we are solely calling super without calling the missing method along with it?
Your supposition is wrong. super is not the superclass of MyClass. It is a call of a method with the same name on the superclass of MyClass.
In the the following tea timer code, there's a 'start' method inside SleepTimer, which calls 'notify.'
def start
sleep minutes * 60
notifier.notify("Tea is ready!")
end
If you look at the code below, you'll see that there's a notify method in the class StdioUi as well as a notify method in module UiWithBeep. The start method shown above calls the notify method in module UiWithBeep, which then, via 'super,' calls the notify method in class StdioUi. (The effect is that "BEEP!" is heard before "Tea is ready".) However, I don't understand why notifier.notify calls the notify method in module UiWithBeep rather than in class StdioUi.
First Question: how does it know to go to the one 'notify' over the other.
SecondQuestion And, although I understand super, what establishes the relationship so that notify in class StdioUi is 'super' to the other notify. Can you please explain
Tea Timer
class TeaClock
attr_accessor :timer
attr_accessor :ui
def initialize(minutes)
self.ui = StdioUi.new
self.timer = SleepTimer.new(minutes, ui)
init_plugins
end
def init_plugins
puts "init plugins"
#plugins = []
::Plugins.constants.each do |name|
#plugins << ::Plugins.const_get(name).new(self)
end
end
def start
timer.start
end
end
class StdioUi
def notify(text)
puts text
end
end
SleepTimer = Struct.new(:minutes, :notifier) do
def start
sleep minutes * 60
notifier.notify("Tea is ready!")
end
end
module Plugins
class Beep
def initialize(tea_clock)
tea_clock.ui.extend(UiWithBeep)
end
module UiWithBeep
def notify(*) #gets called by notifier.notify("Tea is ready")
puts "BEEP!"
super #calls notify in class StdioUi
end
end
end
end
t = TeaClock.new(0.01).start
The Book: I keep recommending this excellent book, Metaprogramming Ruby. I was consulting it while composing this answer.
So, here you extend an object with a module. In Ruby it's called Object Extension. In simple cases it all works as expected, like this one:
module Foo
def hello
puts "foo"
end
end
class Bar
end
bar = Bar.new
bar.extend Foo
bar.hello
# >> foo
Things get complicated when there are class' own methods involved. Here's a simplified version of your snippet that exhibits the same behaviour.
module Foo
def hello
puts "foo"
super
end
end
class Bar
def hello
puts 'bar'
end
end
bar = Bar.new
bar.extend Foo
bar.hello
# >> foo
# >> bar
When you call a method in ruby, the interpreter has to first find a method to call. This is called Method Lookup. Now, when you define an instance method, in reality it's a method on class object, not that instance. So, method lookup goes like this for first snippet:
1) bar instance => method hello not found here
2) Bar class => method hello found
When you extend an object, however, methods are injected into instance's eigenclass. It's a special "hidden" class, unique for each instance. And in reality method lookup goes through it first. First snippet again:
1) bar instance => method hello not found here
2) bar's eigenclass => method hello not found here
3) Bar class => method hello found
Now it should be clear why Foo.hello is called instead of Bar.hello: because it appears earlier in the method lookup process!
1) bar instance => method hello not found here
2) bar's eigenclass => method hello found
I may have made a few mistakes but this is roughly what happens.
each class has a property called ancestors that represents the inheritance chain. ruby walks through the list of inherited behavior and looks for matching methods. if it finds one, it calls it with the given parameters. if you call super it looks for the next match.
1.9.3-p194 :003 > String.class.ancestors
=> [Class, Mocha::ClassMethods, Module, NewRelic::Agent::MethodTracer::InstanceMethods, NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped, NewRelic::Agent::MethodTracer::ClassMethods, NewRelic::Agent::MethodTracer::ClassMethods::AddMethodTracer, Mocha::ModuleMethods, ActiveSupport::Dependencies::ModuleConstMissing, Object, FactoryGirl::Syntax::Vintage, Metaclass::ObjectMethods, Mocha::ObjectMethods, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Dependencies::Loadable, FriendlyId::ObjectUtils, Kernel, BasicObject]
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.
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.