繁体   English   中英

Ruby - 如何在模块中包含类

[英]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 中, HeadingTagsHtmlBody::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

当您将它们拼接到单独的文件中时,只需在文件顶部(不在命名空间中)执行通常的requirerequire_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM