我是Ruby的新手。我已经看到Ruby中的模块用于命名空间或mixin。
我想使用一个用于命名空间的模块。模块将包含类定义。
这是我的尝试。
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
从另一个文件中,我需要模块 lib / HtmlBody 。
require_relative 'lib/HtmlBody'
HtmlBody::HeadingTags.new
这将返回错误。 :
1: from (irb):9:in `rescue in irb_binding'
NameError (uninitialized constant HtmlBody::HeadingTags)
我不确定是什么问题。我了解它说未初始化,但是我不确定为什么。似乎是在寻找常量而不是在阅读课?
您应该如何在模块内的单独文件中包含类?
这是我在Ruby和 require / require_relative 中可能缺少的东西。
答案 0 :(得分:5)
我不确定是什么问题。我了解它说未初始化,但是我不确定为什么。似乎是在寻找常量而不是在阅读课?
我不清楚您“读课”的意思。是的,Ruby正在寻找一个常量。以大写字母开头的变量名称是常量,ergo,HtmlBody
是常量,HeadingTags
是常量,HtmlBody::HeadingTags
是位于类中的常量HeadingTags
常量HtmlBody
引用的模块。
您应该如何在模块内的单独文件中包含类?
通过在模块内定义类,可以在模块内为类命名。如果确定该模块已经存在,则可以这样定义类:
class HtmlBody::HeadingTags
# …
end
但是,如果未定义HtmlBody
(或不是类或模块),则将失败。
module HtmlBody
class HeadingTags
# …
end
end
这将确保模块HtmlBody
如果不存在将被创建(如果已经存在,则只需重新打开即可)。
两者之间的常量查找规则也有细微差别,但这与您的问题无关(但请注意)。
这是我在Ruby和 require / require_relative 中可能缺少的东西。
实际上,您的问题源于对Kerne#load
/ Kernel#require
/ Kernel#require_relative
所做的基本误解。
这是对这三种方法所做的所有令人费解的工作的非常复杂,详细,深入的解释。振作起来!你准备好了吗?我们开始:
他们运行文件。
等等...就是这样吗?对,就是那样!这里的所有都是它的。他们运行文件。
因此,当您运行一个如下所示的文件时会发生什么:
class HeadingTags # … end
它在顶级名称空间中定义了一个名为HeadingTags
的类,对吧?
好的,那么当我们现在这样做时会发生什么:
require_relative './html_body/HeadingTags'
好吧,我们说require_relative
只是运行文件。我们说过,运行该文件会在顶级名称空间中定义一个名为HeadingTags
的类。因此,这显然将在顶级名称空间中定义一个名为HeadingTags
的类。
现在,查看您的代码:当我们这样做时会发生什么:
module HtmlBody require_relative './html_body/HeadingTags' end
同样,我们说require_relative
只是运行文件。而已。没什么。只需运行文件。我们说运行该文件做什么呢?它在顶级名称空间中定义了一个名为HeadingTags
的类。
那么,在require_relative
的模块定义中调用HtmlBody
会做什么呢?它将在顶级名称空间中定义一个名为HeadingTags
的类。由于require_relative
仅运行文件,因此结果将与运行文件完全相同,并且运行文件的结果是它在顶级名称空间中定义了该类。
那么,您实际上如何实现您想要做的事情?好吧,如果要在模块内部定义一个类,则必须…在模块内部定义该类!
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
答案 1 :(得分:3)
您收到错误Uninitialized Constant
,因为在红宝石中,HeadingTags
和HtmlBody::HeadingTags
是两个不同的常量。 Ruby在这里没有考虑文件路径。要实现所需的功能,您需要明确声明HeadingTags
属于HtmlBody
,如下所示:
htmlbody.rb
module HtmlBody; end
htmlbody / headingtags.rb
module HtmlBody
class HeadingTags; end
end
或者class HtmlBody::HeadingTags; end
。
但是,如果要在模块下动态定义常量,则可以查看const_set
方法。
答案 2 :(得分:1)
Jorg的答案更为详尽,并详细说明了原因。我会顺从他的意见,但我的回答要简洁。 https://stackoverflow.com/a/58034705/1937435
您可以使用eval完成您想做的事情,但是不建议这样做。您在此处寻找的功能是通过包含和扩展来实现的。
include
将在实例级别分配模块的方法。 extend
将在类级别分配模块的方法。
module HtmlBody; end
module HeadingInstanceMethods
def h1
puts "I am h1"
end
end
module HeadingClassMethods
def valid_headers
["h1", "h2"]
end
end
module HtmlBody
class HeadingTags
include HeadingInstanceMethods
extend HeadingClassMethods
end
end
HtmlBody::HeadingTags.valid_headers # => ["h1", "h2"]
HtmlBody::HeadingTags.new.h1 # => I am h1
将它们拼接成单独的文件时,只需在文件顶部(不在名称空间中)执行通常的require
或require_relative
并相应地调用它们即可。
答案 3 :(得分:0)
我并不是说这是个好主意,但红宝石总是愿意给你足够的绳索以将自己射向脚,所以...
仅出于娱乐目的,您可以将这些功能(以非常通用的假设)结合在一起:
module HtmlBody
def self.include_in_scope(constant_name,path)
require_relative path
self.const_set(constant_name.to_s, Object.send(:remove_const, constant_name.to_s))
end
end
klass_list = {HeadingTags: './html_body/HeadingTags',
AnchorTags: './html_body/AnchorTags'
ImgTags: './html_body/ImgTags'}
klass_list.each do |name,path|
HtmlBody.include_in_scope(name, path)
end
现在,所有类都在HtmlBody
下命名,例如HtmlBody::HeadingTags
,但不包含在顶级范围内,例如::HeadingTags
将引发NameError
。