[英]Ruby - How to include classes in a Module
我是 Ruby 的新手。 我已经看到 Ruby 中的模块用于命名空间或混合。
我想使用一个模块来命名空间。 模块将包括 class 定义。
这是我的尝试。
库/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)
我不确定问题是什么。 我知道它说未初始化,但我不知道为什么。 似乎它正在寻找一个常数而不是读取 class?
您应该如何在模块内包含位于单独文件中的类?
这是我在 Ruby 和require/require_relative中可能缺少的东西。
我不确定问题是什么。 我知道它说未初始化,但我不知道为什么。 似乎它正在寻找一个常数而不是读取 class?
我不清楚您所说的“阅读课程”是什么意思。 是的,Ruby 正在寻找一个常数。 以大写字母开头的变量名是常量,因此, HtmlBody
是常量, HeadingTags
是常量, HtmlBody::HeadingTags
是常量HeadingTags
位于常量HtmlBody
引用的 class 或模块中。
您应该如何在模块内包含位于单独文件中的类?
通过在模块内定义 class,您可以在模块内命名 class。 如果您确定该模块已经存在,您可以像这样定义 class:
class HtmlBody::HeadingTags
# …
end
但是,如果HtmlBody
未定义(或者不是 class 或模块),这将失败。
module HtmlBody
class HeadingTags
# …
end
end
这将保证HtmlBody
模块在不存在时会被创建(如果它已经存在则简单地重新打开)。
两者之间的持续查找规则也略有不同,但这与您的问题无关(但请注意)。
这是我在 Ruby 和require/require_relative中可能缺少的东西。
实际上,您的问题源于对Kerne#load
/ Kernel#require
/ Kernel#require_relative
所做的基本误解。
这是对这三种方法所做的所有令人难以置信的复杂事情的非常复杂、详细、深入的解释。 振作起来? 你准备好了吗:我们开始:
他们运行文件。
等等……就这样? 对,就是那样。 这里的所有都是它的。 他们运行文件。
那么,当您运行如下所示的文件时会发生什么:
class HeadingTags # … end
它在顶级命名空间中定义了一个名为HeadingTags
的 class ,对吧?
好的,那么当我们现在这样做时会发生什么:
require_relative './html_body/HeadingTags'
好吧,我们说过require_relative
只是运行文件。 我们说过,运行该文件会在顶级命名空间中定义一个名为HeadingTags
的 class。 因此,这显然会在顶级命名空间中定义一个名为HeadingTags
的 class。
现在,查看您的代码:当我们这样做时会发生什么:
module HtmlBody require_relative './html_body/HeadingTags' end
同样,我们说require_relative
只是运行文件。 而已。 一点也不差。 只需运行该文件。 我们说运行该文件有什么作用? 它在顶级命名空间中定义了一个名为HeadingTags
的 class。
那么,从HtmlBody
的模块定义中调用require_relative
会做什么呢? 它将在顶级命名空间中定义一个名为HeadingTags
的 class 。 因为require_relative
只是简单的运行文件,所以结果和运行文件是完全一样的,运行文件的结果是它在顶级命名空间中定义了 class。
那么,你如何真正实现你想要做的事情呢? 好吧,如果你想在模块内定义 class,你必须……在模块内定义 class!
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
主文件
require_relative 'lib/html_body'
HtmlBody::HeadingTags.new
您收到错误Uninitialized Constant
,因为在 ruby 中, HeadingTags
和HtmlBody::HeadingTags
是两个不同的常量。 Ruby 此处不考虑文件路径。 要实现您想要的,您需要明确声明HeadingTags
属于HtmlBody
,如下所示:
htmlbody.rb
module HtmlBody; end
htmlbody/headingtags.rb
module HtmlBody
class HeadingTags; end
end
或者class HtmlBody::HeadingTags; end
class HtmlBody::HeadingTags; end
。 但是,如果要在模块下动态定义常量,可以查看const_set
方法。
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
并相应地调用它们。
我并不是说这是一个好主意,但 ruby 总是愿意给你足够的绳子来射你自己的脚,所以......
只是为了好玩,您可以将这个功能一起破解(做出非常通用的假设):
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
但不包含在顶级 scope 例如::HeadingTags
将引发NameError
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.