self.include – 包括来自 Ruby 模块的类方法



我读了这篇文章:Ruby 模块 - 包含做结束块 - 但仍然不知道何时会在模块中使用self.included do ... end块。

这篇文章说,当你包含模块时,块中的代码将运行,但如果模块的唯一目的是被包含,那有什么意义呢?无论如何,该代码不需要运行吗?该块不需要在那里才能运行该代码,对吗?

以下两者之间有什么区别:

module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
module ClassMethods
...
end
end

与。

module M
def self.some_class_method
...
end
scope :disabled, -> { where(disabled: true) }
end

这两个例子有什么区别?

第一个代码块将ClassMethods中的类方法添加到包含类中,并对其调用scope方法。第二个不执行这些操作,并且将导致NoMethodError,因为模块没有scope类方法。 包含模块后,self.some_class_method将不再在包含类上可用。

有关模块包含如何在 Ruby 中工作的完整故事,请阅读我的答案:
从 Ruby 中的模块继承类方法/混合

如果模块的唯一目的是包含模块,那么self.included的意义何在?

包含不是模块的唯一目的。它们还用于其他事情,例如命名空间或简单地存储各种类方法,然后可以在模块本身上调用这些方法。

为什么 Ruby 不自动包含类方法?

从理论上讲,Ruby可以自动将模块中定义的所有类方法添加到包含类中,但实际上这将是一个坏主意,因为您将无法再选择是否要包含类方法 — 所有类方法每次都会被包含,无论它们是否打算被包含。请考虑以下示例:

module M
def self.class_method
"foo"
end
def self.configure_module
# add configuration for this module
end
end
class C
include M
end

在这里,configure_module方法显然不应该添加到C,因为它的目的是设置模块对象的配置。但是,如果我们对类方法具有自动包含功能,您将无法阻止它被包含。

但是所有实例方法都已包含在内!那怎么行呢?

模块中的实例方法只有在包含在类中时才真正有用,因为模块不能有实例,只有类可以。因此,在模块中,每个实例方法都应包含在某个地方才能工作。

模块上的"class"方法是不同的,因为它可以在模块本身上调用,因此无论它是否也添加到包含类中,都可以很好地使用它。这就是为什么您最好在那里有选择的原因。

module M
# self.included is the hook which automatically runs when this module is included
def self.included(base)
puts 'this will be printed when this module will be included inside a class'
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
def print_object_class
self.class.name # here self will be the object of class which includes this module
end
module ClassMethods
def print_class_name
self.name # Here self would be the class which is including this module
end
end
end

我尝试修改上述module以帮助您了解模块(关注点)如何帮助代码可重用性

self.included是一个钩子,当模块包含在类中时会自动运行。

module ClassMethods中声明的任何方法都将成为包含此模块的类的类方法

module ClassMethods之外声明的任何方法都将成为类(包括此模块)的实例方法

对于 Ex,假设有一个类 Product 并且您已将模块包含在其中

class Product < ActiveRecord::Base
include M
puts 'after including M'
end

如果您在 rails 控制台中尝试此示例,您会注意到,一旦模块M包含在模块运行的class Product钩子中,并且

this will be printed when this module will be included inside a class这是打印在控制台上的

之后after including M这将打印在控制台上。

您也可以尝试以下命令

Product.disabled # a scope with name 'disabled' is avaialble because of including the module M
Product.print_class_name # Outputs => 'Product' This method is available to class with the help of module M
Product.new.print_object_class #Outputs => 'Product'

它还提供可重用性,将此模块 M 包含在任何类中,并且该类可以访问模块中描述的所有方法。

而你的第二个例子仅仅是基本模块的例子

module N
def self.abc
puts 'basic module'
end
end

现在abc模块中定义的方法只能使用此模块访问

N.abc # outputs 'basic module'

class Product < ActiveRecord::Base
include  N
end

产品.abc #raises 异常,在类产品上找不到方法 Product.new.abc #raises 异常,在类 Product 的对象上找不到方法

我希望这可以帮助您更好地理解模块的概念。 如果您仍然有任何疑问,请告诉我。

最新更新