Ruby Metaclass, Eigenclass, Singleton
In the previous post about Class
I’ve described the fact that it’s instance and now it’s time to describe how we define new functionality for such instances and instances which they could produce. Example:
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def full_name
"#{@first_name} #{@last_name}"
end
endPerson.instance_methods(false) # => [:full_name]person = Person.new("Bob", "Martin")
person.full_name # => Bob Martin
As we can see there is a definition of Class
instance Person
which describe functionality of instances it could produce. Method new
is part of Class
implementation which is responsible for creating new instance and after such instance is created the private method initialize
will be invoked on it. So, both methods initialize
and full_name
are called instance methods simply because they are available for instances produced by Person
, but in the same way method new
is instance method of Person
instance produced by Class
, such methods, available for Class
instances we call simply as “class methods”.
So, if we need more functionality for instances produced by Person
we could define it in place of creation Person
instance, but how we could add some functionality to Person
instance itself which is produced by Class
instance? Do we need to define it in place of creation Class
instance? but where is such a place? — The answer is that we don’t need to modify Class
instance to add functionality to the single instance produced by it, such as Person
, at list because we need to define some functionality only for Person
instance not for all instances produced by Class
instance. The solution is that every instance has one more instance behind it and it is linked to the initial one and could be used for such purpose:
class Person
def self.info
"Some description"
end
end# orclass Person
class << self
def info
"Some description"
end
end
end# ordef Person.info
"Some description"
end# orclass << Person
def info
"Some description"
end
endPerson.singleton_class.instance_methods(false) # => [:info]Person.info # => Some description
So, if we need some “class methods” for our Class
instances such as Person
instance then we use the second instance behind it which is called as Metaclass or Eigenclass or Singleton.
def Person.new; endPerson.new # => nil
As you can see we redefine new
method with our own implementation located in Metaclass. The next question could be — if every instance has some instance behind it then how it works when we try it on instances different from Class
instances?
bob = Person.new("Bob", "Martin")
ricky = Person.new("Ricky", "Martin")def bob.full_name
"Robert Cecil #{@last_name}"
end# orclass << bob
def full_name
"Robert Cecil #{@last_name}"
end
endbob.full_name # => Robert Cecil Martin
ricky.full_name # => Ricky Martin
So, we redefine bob
instance full_name
method definition with custom implementation placed in Metaclass which just highlight the way how it’s linked to initial instance, at the same time instance ricky
didn’t change behaviour simply because it has own Metaclass. While this works fine there is no much reason to use it and confuse everyone why instances produced by the same Person
instance have different behaviour, so remember that it’s solution to have different behaviour for instances produced by Class
instance such as Person
, File
, Dir
, etc. which are produced from the same Class
instance but needs different functionality.
If there is still a need to define some functionality which need to be available for all Class
instances or we need to change already existed Class
instance then we could simply “re-open” it, but there is no much sense in such monkey patching, which could bring a lot of pain:
class Class
def info
"#{self.name} instance produced by Class instance"
end
endclass Person
def full_name
"patched full_name" # original method need to be updated instead
end
endPerson.info # => Person instance produced by Class instance
Person.new(nil, nil).full_name # => patched full_name
Again there is some special cases with “unique” values such as symbols and numbers which doesn’t have additional instance behind it because there is no much sense to define specific functionality only for single symbol or number, at same time we have such ability for true, false, nil
values:
def true.info; enda = :something
def a.info; end
# => TypeError (can't define singleton)
From the message, it’s obvious that we try to define Metaclass but it was rejected, so Metaclasses are created automatically only for instances produced by Class
instance, while for other instances Metaclasses are created when they are needed.