Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Last active March 20, 2019 06:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/0ed2d4a5c4457084ab77a289c5aa99b9 to your computer and use it in GitHub Desktop.
Save JoshCheek/0ed2d4a5c4457084ab77a289c5aa99b9 to your computer and use it in GitHub Desktop.
Include and Extend in Ruby
# trying to be helpful for https://twitter.com/coorasse/status/1108047414961811456
require 'objspace'
# Helper methods
public
def real_class() ObjectSpace.internal_class_of self end
def real_super() ObjectSpace.internal_super_of self end
def references() ObjectSpace.reachable_objects_from self end
# A module we'll include
module MyMod
def hi
'there'
end
end
# ===== INCLUDE =====
class MySuperClass
end
class MySubClass < MySuperClass
# `include` will create a new "internal class" (ICLASS) and insert it into the inheritance hierarchy
self # => MySubClass
.real_super # => MySuperClass
include MyMod
self # => MySubClass
.real_super # => #<InternalObject:0x00007f9bfb939c90 T_ICLASS>
.real_super # => MySuperClass
# That internal class's methods pointer will point at MyMod's methods
self.real_super.references # => [MyMod, MySuperClass]
# Hence normal method lookup on an instance of MyClass will find MyMod's methods
new.hi # => "there"
MyMod.module_eval { def bye() "for now" end }
new.bye # => "for now"
end
class MySubClass2 < MySuperClass
# Ruby hides this mechanism from you, though
# https://github.com/ruby/ruby/blob/afa1505ca89bd350558f1f42b96f1eee781ef019/object.c#L2247-L2249
superclass # => MySuperClass
real_super # => MySuperClass
include MyMod
superclass # => MySuperClass
real_super # => #<InternalObject:0x00007f9bfb92e890 T_ICLASS>
end
class MySubClass3 < MySuperClass
# And when you look at your ancestors, it translates the internal class to the module
# https://github.com/ruby/ruby/blob/afa1505ca89bd350558f1f42b96f1eee781ef019/class.c#L1090-L1092
ancestors.take 2 # => [MySubClass3, MySuperClass]
include MyMod
ancestors.take 3 # => [MySubClass3, MyMod, MySuperClass]
end
# ===== EXTEND =====
# If you wanted to add MyMod to an object, you could open its singelton class and include it
s1 = "s1"
class << s1
include MyMod
end
s1.hi # => "there"
# The rest of the strings are fine
"s2".hi rescue $! # => #<NoMethodError: undefined method `hi' for "s2":String>
# But that's cumbersome, so they added `extend` to do it for you
s3 = "s3"
s3.extend(MyMod)
s3.hi # => "there"
# Ruby hides this from you, though
# https://github.com/ruby/ruby/blob/afa1505ca89bd350558f1f42b96f1eee781ef019/object.c#L256
s4 = "s4"
s4.class # => String
s4.real_class # => String
s4.extend MyMod
s4.class # => String
s4.real_class # => #<Class:#<String:0x00007f9bfc80ff80>>
.real_super # => #<InternalObject:0x00007f9bfc80fb98 T_ICLASS>
.real_super # => String
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment