Reputation: 447
I'm reading 'Metaprogramming Ruby' right now and writing some code at the same time to clarify the concepts. I've read that when you include/prepend a single module multiple times, all further inclusions won't change the place of said module in the ancestry chain.
I wrote some code that works in a way that I did not expect - what actually happens there?
module GreatGrandfather; end
module Grandfather
include GreatGrandfather
end
module Father
include Grandfather
prepend GreatGrandfather
end
module Son
include Father
end
Son.ancestors # => [Son, Father, Grandfather, GreatGrandfather]
I've assumed that when I run Son.ancestors
, Son would include Father, Father would include Grandfather and prepend GreatGrandfather and the ancestry tree would be set to [Son, GreatGrandfather, Father, Grandfather]
. Obviously that didn't happen.
Once Son includes Father, it starts to look in the Father module and finds include Grandfather
and prepend GratGrandfather
. Does it actually 'go into' Grandfather
where it includes GreatGrandfather, and only then executes the prepend GreatGrandfather
line (and ignores it because it already exists in the ancestry)?
Truth be told, I doubt I will get much use out of it, but it won't hurt to know how exactly the modules 'chain' each other.
@edit - I've played around with it a bit more, and it doesn't seem that my intuition is right in either case. I've included a picture of the two ways I thought it could go, instruction after instruction, about creating the inheritance hierarchy - the one that appears to occur in the picture goes against the original example given, so neither #1 or #2 can be happening.
Modified example code (only GreatGrandfather changed)
module GreatGrandfather
include Grandfather
end
module Grandfather
include GreatGrandfather
end
module Father
prepend GreatGrandfather
include Grandfather
end
module Son
include Father
end
Son.ancestors # => Son, GreatGrandfather, Father, Grandfather
in summary - I still have no idea how it happens
Upvotes: 2
Views: 893
Reputation: 317
Ruby’s default implementation is to overlay the constants, methods, and module variables of this module to mod if this module has not already been added to mod or one of its ancestors.
But you already add GreatGrandfather to Father through Grandfather.
This way it would work as you expect:
module GreatGrandfather; end
module Grandfather
include GreatGrandfather
end
module Father
prepend GreatGrandfather
include Grandfather
end
module Son
include Father
end
p Son.ancestors # => [Son, GreatGrandfather, Father, Grandfather]
Update
1.You cant modify example like this:
module GreatGrandfather
include Grandfather
end
module Grandfather
include GreatGrandfather
end
cause when you define GreatGrandfather Grandfather is not defined.
2.This is, what happens when you add modules to another modules. Comments illustrate, what happens with module hierarchy in time:
module GreatGrandfather
# GG
end
module Grandfather
# GG
# G
include GreatGrandfather
# GG
# G -> GG
end
module Father
# GG
# G -> GG
# F
prepend GreatGrandfather
# GG
# G -> GG
# GG -> F
include Grandfather
# Don't change the position of GG in Father hierarchy, cause it is already in ancestors
# GG
# G -> GG
# GG -> F -> G
end
module Son
# GG
# G -> GG
# GG -> F -> G
# S
include Father
# GG
# G -> GG
# GG -> F -> G
# S -> GG -> F -> G
end
Upvotes: 1