Ruby class, instance, module and all the frustration in-between
Programming in Ruby can be problematic when you start trying to dwell a bit beyond the common usages. In my opinion this is due to Ruby having a bit too many rules (ie. 4-5 different ways to inherit with different behaviors) and not being all-around concise (ie. some non-bang methods actually doing in-place replacement).
Below I try to bring some sanity to all this by hopefully providing some a-ha moments.
Methods come in two flavors: instance and class
Instance methods are the most commonly used. Class methods (aka static methods in other languages) are the ones with self
prepended. In that case, self
refers to the class itself and NOT an instance to it.
class MyClass
def self.class_do
print(10)
end
def instance_do
print(20)
end
end
And you call them like this
MyClass.class_do # -> 10
MyClass.new.instance_do # -> 20
Self can point to a class or an instance
In most programming languages we use self solely to refer to the instance of a class from inside the class. However in Ruby self can refer to more than that.
Below we have two methods that print the self
variable in different contexts:
class MyClass
def self.show_self # class context
print(self)
end
def show_self # instance context
print(self)
end
end
And you call them like this
MyClass.show_self # -> MyClass
MyClass.new.show_self # -> <MyClass:0x000000026edb90>
As you see, self can refer to more than the instance.
Modules have self as well
What’s the above behaviour in a module?? Apparently it’s exactly as with classes. But since a module can’t be instantiated by definition, we simply never see self as an instance reference.
module MyModule
def self.show_self
print(self)
end
def show_self
print(self)
end
end
MyModule.show_self # -> MyModule
MyModule.new.show_self # => ERROR
The last error is due to the module not being able to be instantiated than anything else.
Extend, include, inheritance!
All these ways to inherit behavior might make a new-comer confused. What you need to remember is that include
copies over instance methods while extend
copies over variable methods.
Let’s say we have the MyModule module with an instance and a class method.
module MyModule
def instance_do
print(10)
end
def self.class_do
print(20)
end
end
Let’s include the module in our class:
class MyClass
include MyModule
end
MyClass.instance_do # -> ERROR
MyClass.class_do # -> ERROR
MyClass.new.instance_do # -> 10
Nothing unexpected here. We inherit the instance methods as expected so we can use them with any class instances.
Let’s extend our class with the module:
class MyClass
extend MyModule
end
MyClass.instance_do # -> 10
MyClass.class_do # -> ERROR
MyClass.new.instance_do # -> ERROR
Now this is probably the thing that is most confusing. As we see, extend actually takes the instance methods from the module and translates them to class instances.
This makes sense to some degree since self
in the context of the module refers to the module itself. Confusing non-the-less.
Written with StackEdit.