Ruby-如何在模块中包含类

Ruby-如何在模块中包含类,ruby,Ruby,我是红宝石新手。我已经看到Ruby中的模块用于名称空间或mixin 我想使用一个用于名称空间的模块。模块将包括类定义 这是我的尝试 lib/HtmlBody.rb lib/html\u body/HeadingTags.rb 从另一个文件中,我需要模块lib/HtmlBody 这将返回一个错误: 1: from (irb):9:in `rescue in irb_binding' NameError (uninitialized constant HtmlBody::HeadingTags)

我是红宝石新手。我已经看到Ruby中的模块用于名称空间或mixin

我想使用一个用于名称空间的模块。模块将包括类定义

这是我的尝试

lib/HtmlBody.rb

lib/html\u body/HeadingTags.rb

从另一个文件中,我需要模块lib/HtmlBody

这将返回一个错误:

1: from (irb):9:in `rescue in irb_binding'
NameError (uninitialized constant HtmlBody::HeadingTags)
我不确定是什么问题。我知道它说的是未初始化,但我不知道为什么。它似乎是在寻找一个常数,而不是阅读课堂

您应该如何在模块中包含位于独立文件中的类


我在Ruby中缺少的是require/require\u相对的元素。

Jorg的回答更为详尽,并且解释了为什么。我会听从他的,但请留下我简洁的答案

你可以用eval完成你想做的事情,但不推荐这样做。您在这里寻找的功能是通过include和extend实现的

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\u relative,而不是在名称空间中,并相应地调用它们。

您将得到错误Uninitialized常量,因为在ruby中,HeadingTags和HtmlBody::HeadingTags是两个不同的常量。Ruby在这里不考虑文件路径。为了实现您想要的,您需要显式地将HeadingTags声明为属于HtmlBody,如下所示:

class HtmlBody::HeadingTags
  # …
end
htmlbody.rb

htmlbody/headingtags.rb

或者,类HtmlBody::HeadingTags;终止 但是,如果要在模块下动态定义常量,可以查看const_set方法

我不是说这是个好主意,但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,但不包括在顶级范围内,例如:::HeadingTags将引发NameError

我不确定是什么问题。我知道它说的是未初始化,但我不知道为什么。它似乎是在寻找一个常数,而不是阅读课堂

我不清楚你读这堂课是什么意思。是的,Ruby正在寻找一个常数。以大写字母开头的变量名是常量,因此,HtmlBody是常量,HeadingTags是常量,HtmlBody::HeadingTags是常量HtmlBody引用的类或模块中的常量HeadingTags

您应该如何在模块中包含位于独立文件中的类

通过在模块内定义类,可以在模块内为类命名名称空间。如果确定该模块已存在,则可以按如下方式定义该类:

class HtmlBody::HeadingTags
  # …
end
但是,如果HtmlBody未定义或不是类或模块,则此操作将失败

module HtmlBody
  class HeadingTags
    # …
  end
end
这将保证模块HtmlBody在不存在时被创建,如果已经存在则被重新打开

这两种方法在常量查找规则上也有细微差别,但这与您的问题无关,但请注意

我在Ruby中缺少的是,require/require\u可能是相对的

事实上,你的问题源于对//的基本误解

下面是对这三种方法所做的所有令人难以置信的复杂事情的非常复杂、详细、深入的解释。振作起来!你准备好了吗?我们开始:

他们运行文件

等等……就这样?是的,就是这样!就这些。他们运行文件

那么,当您运行一个如下所示的文件时会发生什么情况:

class HtmlBody::HeadingTags
  # …
end
它在顶级命名空间中定义了一个名为HeadingTags的类,对吗

好的,那么当我们现在这样做时会发生什么:

我们说过require_relative只是运行文件。我们说过,运行该文件在顶级名称空间中定义了一个名为HeadingTags的类。因此,这显然将在顶级名称空间中定义一个名为HeadingTags的类

现在,看看您的代码:当我们这样做时会发生什么:

同样,我们说过require_relative只运行文件。没别的了。同样如此。只需运行文件。我们说运行该文件有什么作用?它在顶级命名空间中定义了一个名为HeadingTags的类

那么,在HtmlBody的模块定义中调用需要_relative做什么呢?它将在顶级命名空间中定义一个名为HeadingTags的类。因为require_relative只运行文件,因此结果将与运行文件完全相同,运行文件的结果是它定义了类 在顶级命名空间中

那么,你是如何真正实现你想做的事情的呢?如果你想在模块内定义一个类,你必须在模块内定义这个类

lib/html_body.rb

lib/html\u body/heading\u tags.rb

lib/html\u body/anchor\u tags.rb

lib/html\u body/img\u tags.rb

main.rb


有什么理由不在ruby文件中定义HtmlBody::HeadingTags吗?如果每个类都有10个方法,将它们全部放在同一个文件中会使模块进入数百行。为了可读性,我更喜欢将它们保存在单独的文件中。您可以在单独的文件类中定义它们,如果它们是类级方法,则使用extend;如果它们是实例方法,则使用include。你可以用eval做你想做的事,但不推荐。谢谢,你给了我答案。看起来很奇怪!我不确定Ruby是否提供了模块供我使用。我并不反对这个答案,但我确实认为这不是一个很好的答案,原因有三:1它对于OP试图完成的任务来说过于复杂。它实际上解决了一个与OP试图解决的问题不同的问题。3它没有解决OP困惑的根源;事实上,鉴于OP在这个答案下的评论,它似乎增加了混乱。@JörgWMittag我很乐意听从你对我仓促的书面回答:
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
class HtmlBody::HeadingTags
  # …
end
module HtmlBody
  class HeadingTags
    # …
  end
end
class HeadingTags
  # …
end
require_relative './html_body/HeadingTags'
module HtmlBody
  require_relative './html_body/HeadingTags'
end
require_relative 'html_body/heading_tags'
require_relative 'html_body/anchor_tags'
require_relative 'html_body/img_tags'

module HtmlBody; end
module HtmlBody
  class HeadingTags
    # …
  end
end
module HtmlBody
  class AnchorTags
    # …
  end
end
module HtmlBody
  class ImgTags
    # …
  end
end
require_relative 'lib/html_body'

HtmlBody::HeadingTags.new