Im new in Ruby. I have seen that Modules in Ruby are used for namespacing or for mixin.
I would like to use a module for namespacing. Module will include class definitions.
This has been my attempt.
lib/HtmlBody.rb
module HtmlBody
require_relative './html_body/HeadingTags'
require_relative './html_body/AnchorTags'
require_relative './html_body/ImgTags'
end
lib/html_body/HeadingTags.rb
class HeadingTags
...
end
And from another file, I would require the module lib/HtmlBody.
require_relative 'lib/HtmlBody'
HtmlBody::HeadingTags.new
This will return an error. :
1: from (irb):9:in `rescue in irb_binding'
NameError (uninitialized constant HtmlBody::HeadingTags)
Im not sure what the issue is. I understand that it says uninitialized but Im not sure why. It seems it is looking for a constant instead of reading the class?
How are you supposed to include classes located in separated files inside a module?
The is something that Im missing in Ruby and the require/require_relative probably.
Im not sure what the issue is. I understand that it says uninitialized but Im not sure why. It seems it is looking for a constant instead of reading the class?
It is not clear to me what you mean by "reading the class". Yes, Ruby is looking for a constant. Variable names that begin with a capital letter are constants, ergo, HtmlBody
is a constant, HeadingTags
is a constant, and HtmlBody::HeadingTags
is the constant HeadingTags
located in a class or module that is referenced by the constant HtmlBody
.
How are you supposed to include classes located in separated files inside a module?
You namespace a class inside a module by defining the class inside the module. If you are sure that the module already exists, you can define the class like this:
class HtmlBody::HeadingTags
# …
end
However, if HtmlBody
is not defined (or is not a class or module), this will fail.
module HtmlBody
class HeadingTags
# …
end
end
This will guarantee that module HtmlBody
will be created if it doesn't exist (and simply re-opened if it already exists).
There is also a slight difference in constant lookup rules between the two, which is however not relevant to your question (but be aware of it).
The is something that Im missing in Ruby and the require/require_relative probably.
Indeed, your question stems from a fundamental misunderstanding of what Kerne#load
/ Kernel#require
/ Kernel#require_relative
does.
Here is the very complicated, detailed, in-depth explanation of all the incredibly convoluted stuff that those three methods do. Brace yourself! Are you ready? Here we go:
They run the file.
Wait … that's it? Yes, that's it! That's all there is to it. They run the file.
So, what happens when you run a file that looks like this:
class HeadingTags # … end
It defines a class named HeadingTags
in the top-level namespace, right?
Okay, so what happens when we now do this:
require_relative './html_body/HeadingTags'
Well, we said that require_relative
simply runs the file. And we said that running that file defines a class named HeadingTags
in the top-level namespace. Therefore, this will obviously define a class named HeadingTags
in the top-level namespace.
Now, looking at your code: what happens, when we do this:
module HtmlBody require_relative './html_body/HeadingTags' end
Again, we said that require_relative
simply runs the file. Nothing more. Nothing less. Just run the file. And what did we say running that file does? It defines a class named HeadingTags
in the top-level namespace.
So, what will calling require_relative
from within the module definition of HtmlBody
do? It will define a class named HeadingTags
in the top-level namespace. Because require_relative
simply runs the file, and thus the result will be exactly the same as running the file, and the result of running file is that it defines the class in the top-level namespace.
So, how do you actually achieve what you are trying to do? Well, if you want to define a class inside a module, you have to … define the class inside the module!
lib/html_body.rb
require_relative 'html_body/heading_tags'
require_relative 'html_body/anchor_tags'
require_relative 'html_body/img_tags'
module HtmlBody; end
lib/html_body/heading_tags.rb
module HtmlBody
class HeadingTags
# …
end
end
lib/html_body/anchor_tags.rb
module HtmlBody
class AnchorTags
# …
end
end
lib/html_body/img_tags.rb
module HtmlBody
class ImgTags
# …
end
end
main.rb
require_relative 'lib/html_body'
HtmlBody::HeadingTags.new