Skip to content

Instantly share code, notes, and snippets.

@blaix
Last active November 21, 2022 09:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blaix/784cf15cbafb530243d489cd517d5edd to your computer and use it in GitHub Desktop.
Save blaix/784cf15cbafb530243d489cd517d5edd to your computer and use it in GitHub Desktop.
How to understand `extend self` and `class << self` in ruby.
# Everything in ruby is an object which is an instance of a class. Even classes
# themselves are objects that are instances of the class `Class`.
class Dog
end
puts Dog.class # => Class
# When you use the class keyword, you are opening a class to define instance
# methods for any instance of the class:
class Dog
def bark
puts "bark!"
end
end
dog1 = Dog.new
dog2 = Dog.new
dog1.bark # => "bark!"
dog2.bark # => "bark!"
# Every object in ruby also has an "invisible" singleton class (a metaclass):
puts Dog.singleton_class # => #<Class:Dog>
puts Dog.new.singleton_class # => #<Class:#<Dog:0x0123...>>
puts Dog.new.singleton_class # => #<Class:#<Dog:0x0456...>>
# When you say `def <some object>.<some method>`, you are defining an instance
# method on that object's singleton class:
def dog1.bark
puts "redefined bark!"
end
# and when you call a method in ruby, first it checks the object's singleton
# class, and THEN it checks object's class:
dog1.bark # => redefined bark!
dog2.bark # => bark!
# Since classes are objects, the same applies:
def Dog.eat
puts "yum!"
end
# We defined an instance method on the class object's singleton class (wee),
# and method lookup works the same way. There's nothing special about "class"
# methods in ruby!
Dog.eat # => "yum!"
# And there's always an implicit `self` object:
puts self # => main
# When you open a class, self changes to the class object:
class Dog
puts self # => Dog
end
# Which is why you can define "class methods" like this:
class Dog
# Remember `def <object>.<method>` syntax defines an instance method on the
# object's singleton class. Since here `self` is `Dog`, we define a class
# method by putting an instance method on the Dog's singleton class:
def self.eat_again
puts "yummy!"
end
end
Dog.eat_again # => yummy!
# Another way to define methods in ruby is with modules:
module Pisser
def piss
puts "pisss"
end
end
# Classes have a method called `include` which takes a module ands adds the
# module methods to the class as instance methods:
Dog.include(Pisser)
dog1.piss # => pisss
dog2.piss # => pisss
# Classes also have a method called `extend` which is described as adding "class
# methods" to the class, but to understand `extend self`, you need to understand
# that really `extend` is adding _instance_ methods to the class' _singleton_
# class. Class methods are a ruby myth!
Dog.extend(Pisser)
Dog.piss # => pisss
# Also remember that using `class Whatever` is just opening the Whatever class,
# and `self` becomes the class, which means this is the same as calling
# `Dog.extend(Pisser)`:
class Dog
extend Pisser # => here the implicit `self` is `Dog`
end
# The same thing applies to modules....
module AnotherPisser
extend Pisser
end
AnotherPisser.piss # => pisss
# Which means we can define modules to namespace "functions" (that are really
# just instance methods on the module's singleton class):
module Util
extend self # => the same as Util.extend(Util)
# Since Util extends itself, these methods will exist on Util's singleton
# class, making them callable directly on Util
def hello
puts "hello"
end
end
Util.hello # => hello
# Another way to define methods on an object's singleton class is with the
# `class << <object>` syntax. For example:
class << dog1
def pet
puts "tail wag!"
end
end
dog1.pet # => tail wag!
# Which means...
class Dog
class << self
def zen
puts "ruby is radical!"
end
end
end
# Inside the Dog class, `self` is the Dog class object, so `class << self` opens
# the Dog class object's singleton class to define methods, and ruby checks the
# singleton class first on method lookups:
Dog.zen # => ruby is radical!
# The easy way to think about it is `extend self` is a convenient way to define
# "module methods" and `class << self` is a convenient way to define "class
# methods", but understanding the object model and method lookup shows there
# is nothing special going on. It's all just instance methods on objects.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment