Skip to content

Instantly share code, notes, and snippets.

@yoelblum
Last active October 18, 2023 01:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yoelblum/12b6a648d0ff7df4e3c0e95a55e8be1d to your computer and use it in GitHub Desktop.
Save yoelblum/12b6a648d0ff7df4e3c0e95a55e8be1d to your computer and use it in GitHub Desktop.
Ruby base class VS including modules (mixins)
# Ruby's inheritance mechanisms are a bit unique and have some subtelties
# There are two main ways to inherit: Direct Inheritance (with a "Base" class) and "Mixins" (including modules)
# "Direct inheritance" (B is the base class of A)
class B
X = 30
def self.static_method
"Hello from static method"
end
def inst_method
"Hello from instance method"
end
end
class A < B
end
puts A.static_method # Hello from static method
puts A::X # 30
puts A.new.inst_method # Hello from instance method
# Pretty much what you'd expect coming from other programming languages: static methods are inherited,
# instance methods are inherited and constants are inherited
# Important: You can only inherit from one class in this way!
# Way #2 is Mixins (including modules)
module Includable
X = 30
def self.static_method
"Hello from static method"
end
def inst_method
"Hello from instance method"
end
end
class C
include Includable
end
puts C.static_method # NoMethodError (undefined method `static_method' for C:Class)
puts C.new.inst_method # Hello from instance method
puts C::X # 30
# So with mixins instance methods are inherited, constants are inherited but static methods are not!
# ActiveSupport Concerns are a popular way to mixin "fully inherited" modules (instance methods, static_methods and constants!)
# https://api.rubyonrails.org/classes/ActiveSupport/Concern.html
# Ruby Inheritance Order of Precedence
# So who gets precedence, a mixin or a base class?
class E
X = 30
def say_hello
"Hello from E"
end
end
module Mixin
X = 40
def say_hello
"Hello from Mixin"
end
end
class F < E
include Mixin
end
puts F::X # 40
puts F.new.say_hello # Hello from Mixin
# It may be a bit surprising but the Mixin gets precedence over the base case
# If you're having a hard time remembering this, you can always use the ancestors method
puts F.ancestors # [F, Mixin, E, Object, Kernel, BasicObject]
# The order of precedence is just what you see in ancestors: First F, the class itself (obviously!). Then perhaps surprisingly
# all mixins (included modules) which is "Mixin" in our example. then the base class E. Later it's ruby's Object, Kernel and
# BasicObject
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment