While watching this video I came across an interesting question posed by the presenter, Dave Thomas. He is talking about the syntax we see all the time in Ruby class method definitions:
class Foo
class << self
def bar
puts "inside class method"
end
def self.baz
puts "inside anonymous superclass method"
end
end
end
Of course, we can access Foo.bar, but how does one go about baz? Dave Thomas talks about the class << self syntax inserting an anonymous superclass into the hierarchy. I tried the following:
Calling Foo.superclass.bazdoesn't work because Foo.superclass is just Object.
I poked around the available methods of the ancestry hierarchy to no avail.
Test.class_eval 'self.self.baz'...now things are getting a little ridiculous.
Thoughts?
The problem is presented around 44:23 in the video.
At the end of the video, we are offered several answers to this question.
First, something pretty ugly. You can reopen the class:
class Foo
class << self
baz
end
end
And another way. Since class definitions are executed, you can return the inner self into a variable:
meta = class Foo
class << self
def self.baz
puts "inside anonymous superclass method"
end
self # return self here
end
end
meta.baz
Or, most elegantly you can open Class and add a method to it:
class Class
def meta
class << self
self
end
end
end
# class Foo ...
Foo.meta.baz
which turns out to be just a reimplementation of Object#singleton_class.
Related
class_eval and instance_eval are quite predictable in such cases like defining methods. I also understand the difference between class's instance and class's singleton (aka eigenclass).
BUT
I cannot figure out the only thing like following:
Let's say, for some strage purposes, we want make existing class to be singleton.
class A; end
class B; end
A.class_eval do
private :new
end
B.instance_eval do
private :new
end
in both cases got
NameError: undefined method 'new' for class
Did you mean? new
yes, I mean exactly this method.
Moreover, these two variants give the same result, like self points at class object in both cases
A.class_eval do
class << self
private :new
end
end
A.new
=> NoMethodError: private method 'new' called for A:Class
B.instance_eval do
class << self
private :new
end
end
B.new
=> NoMethodError: private method 'new' called for B:Class
How come? Can anybody shed the light on this?
Lets take a peek into what self is here:
class A
puts self.inspect
class << self
puts self.inspect
end
end
A.class_eval {
puts self.inspect
class << self
puts self.inspect
end
}
A.instance_eval{
puts self.inspect
class << self
puts self.inspect
end
}
We get the following output:
A
#<Class:A>
A
#<Class:A>
A
#<Class:A>
The class_eval method is defined for modules (and thus classes) and evaluates within the context of the module (class). The instance_eval method evaluates within the context of a BasicObject. It seems that in these cases the two (three actually) are the same thing.
However, I know for a fact that if methods are created inside the eval block that class_eval creates instance methods and instance_eval creates class methods. There is already an excellent posting for that observation:
This question already has answers here:
class << self vs self.method with Ruby: what's better?
(6 answers)
Closed 8 years ago.
My understanding is that:
class Example
class << self
def my_method; end
end
end
is equivalent to:
class Example
def self.my_method; end
end
but is that correct?
They are equivalent, but choose the latter for clarity reasons. When you have a class that is many lines long you may miss the class method definition when using class << self.
Another good reason to use class << self is when you need accessors on the class level:
class Foo
class << self
attr_accessor :bar
end
end
Beware that this is often not what you want as it is not thread safe. But that's a design problem. If you need it, you need it.
In the case of class << self all methods defined below will be class methods up until the class << self is closed. For a class method at a singular level or multiple ones if you wish you can define the method as self.foo.
class Test
def self.foo
end
def bar
end
end
class Test
class << self
def foo
end
end
def bar
end
end
In both of these cases you will end up with a class method "foo" and an instance method "bar". Both ways accomplish the same thing.
I understand the regular method lookup path i.e. class, superclass/module, all the way up to BasicObject. I thought it was true for singleton version of the chain also but doesn't seem the case when you mixin a module in the meta-chain. I'd appreciate if someone can explain why in the following example Automobile module's banner method is called instead of its singleton version when I have included this module in Vehicle's eigenclass.
module Automobile
def banner
"I am a regular method of Automobile"
end
class << self
def banner
"I am a class method of Automobile"
end
end
end
class Vehicle
def banner
"I am an instance method of Vehicle"
end
class << self
include Automobile
def banner
puts "I am a class method of Vehicle"
super
end
end
end
class Car < Vehicle
def banner
"I am an instance method of Car"
end
class << self
def banner
puts "I am a class method of Car"
super
end
end
end
puts Car.banner
# I am a class method of Car
# I am a class method of Vehicle
# I am a regular method of Automobile
First of all, include does not include eigenclass methods as you might expect. Consider:
module Foo
class << self
def do_something
puts "Foo's eigenclass method does something"
end
end
end
module Bar
include Foo
end
puts Bar.do_something
# undefined method `do_something' for Bar:Module (NoMethodError)
Note that this is consistent with the behavior of classically defined class methods:
module Foo
def self.do_something
puts "Foo's class method does something"
end
end
module Bar
include Foo
end
puts Bar.do_something
# undefined method `do_something' for Bar:Module (NoMethodError)
A common idiom is to define the class methods in a submodule and then trigger a call to extend when the module is included:
module Foo
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def do_something
puts "Foo::ClassMethod's instance method does something"
end
end
end
module Bar
include Foo
end
puts Bar.do_something
# Foo::ClassMethod's instance method does something
The second thing to note is, that you are really including the instance methods of Automobile into the eigenclass of Vehicle, thus the instance methods of Automobile turn into (eigen)class methods of Vehicle.
Your Car class basically has nothing to do with all this. The only thing to note here is, that class inheritance also makes class methods available, whereas include does not. Example:
class Foo
def self.do_something
puts "Foo's class method does something"
end
end
class Bar < Foo
end
puts Bar.do_something
# "Foo's class method does something"
First of all, a class is an object, just like the other objects it also has its own superclass;
second of all, Eigenclass itself is a normal class, only anonymous and sorta invisible;
third, the eigenclass's superclass of the derived class is the eigenclass of the base class;
Fourth, include includes instance methods (not singleton methods) of the included module, make them instance methods of the receiver class object.
There're two parallel inheritance chains in your example
Car < Vehicle < ...
Car's eigenclass < Vehicle's eigenclass < Automobile < ...
Do the following test on irb:
class Object
def eigenclass
class << self
self
end
end
end
Car.ancestors # => [Car, Vehicle, Object, Kernel, BasicObject]
Car.eigenclass.ancestors # => [Automobile, Class, Module, Object, Kernel, BasicObject]
Vehicle.eigenclass.ancestors # => [Automobile, Class, Module, Object, Kernel, BasicObject]
Car.eigenclass.superclass.equal? Vehicle.eigenclass # => true
You see, Automobile is in the eigenclass inheritance chain. But regretably, the ancestor method doesn't return invisible eigenclasses, nonetheless they are actually in the second chain.
I'm trying to define a couple of modules to easily add in some instance and class methods to other classes, here's what I'm doing:
module Foo
module Bar
def speak
puts "hey there"
end
end
module Baz
extend Foo::Bar
def welcome
puts "welcome, this is an instance method"
end
end
end
class Talker
include Foo::Baz
end
Talker.new.welcome
Talker.speak
The output of this is:
welcome, this is an instance method
undefined method 'speak' for Talker.class (NoMethodError)
I was expecting Talker to have the 'speak' method since it includes Foo::Baz which itself extends Foo::Bar.
What am I missing?
You can try this:
module Baz
extend Foo::Bar
def self.included(base)
base.send :extend, Foo::Bar
end
def welcome
puts "welcome, this is an instance method"
end
end
This will auto-extend all classes in wich Baz is included.
PS:
extend Foo::Bar in module Baz was in original snippet, this code do not influence on method def self.included(base).
try this:
class Talker
extend Foo::Baz
end
since you want to call Talker.speak as a class method and not as an instance method (like Talker.new.speak) you have to include the Foo:Baz in a way that the class will take the methods itself.
One possibility is to use 'extend' (as above) the other is modifying it's eigenclass:
class Talker
class << self
include Foo::Baz
end
end
I'm greatly confused by Ruby's behavior when defining const_missing and other class methods inside a class << self definition as opposed to using the def self.foo syntax.
I was trying to do something like this:
class Foo
class << self
def foo
puts MISSING
end
def const_missing(name)
puts "#{name} missing"
end
end
end
Foo.foo
I mostly use the the class << self syntax to define class methods. However, it did not work as expected. const_missing is never called. The above results in a NameError.
Defining both methods like this works as expected:
def self.foo
puts MISSING
end
def self.const_missing(name)
puts "#{name} missing"
end
I thought that the class << self syntax is just another way to define class methods, but completely equivalent to def self.foo? I've tested the above with MRI 1.8.7, 1.9.2 and JRuby 1.5.6. So obviously I'm missing something here?
Any hint is greatly appreciated.
Thanks, Martin
class << self is not a shortcut to define class methods. This syntax (I don't know the exact naming) opens the eigenclass from a object (in your case, a class). With that you can define methods to the object (not instance methods). But when you call a constant into the eigenclass, you are calling a constant from the eigenclass, not from the class. In this case you have to define a class method on the eigenclass to the const_missing, two ways to do that:
class Test
class << self
def foo
p MISSING
end
# First way: (syntax sugar for the second way)
def self.const_missing(name)
name
end
# Second way:
class << self # eigenclass of the eigenclass of the class
def const_missing(name)
name
end
end
end
end
Test.foo #=> :MISSING