Last active
March 20, 2019 06:43
-
-
Save JoshCheek/0ed2d4a5c4457084ab77a289c5aa99b9 to your computer and use it in GitHub Desktop.
Include and Extend in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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