Module引入

类引入

ruby 中module用于替代多继承的功能,是一个可以包含常量、方法、类定义以及其他模块的集合。

module除了不可以new实例,以及没有Class类中一些特殊的方法和class没什么区别。

其定义语法如下:

module ModuleA
  constA

  def method1
    ...do something
  end
end

Mix-in 可以将多个模块混合到类中,即通过include 在类中引入模块,以获得模块的方法、常量等。

class ClassA
  include ModuleA
  include ModuleB
  ...
end

class ClassB
  include ModuleA
  include ModuleB
  ...
end

每个模块具有单独的命名空间,以保证模块下的常量和方法互不影响。

Const

在类中引入模块后,其中的常量都被引入到了所在类中,当然也可以直接通过模型的命名空间直接访问模型的常量。

Method

在类中引入模块后,其中的方法可以被该类的实例调用。

同时也可以在模块中将方法定义为模块方法。

NameSpace

在引入多个模块时如果常量出现同名问题可以指定具体的命名空间以使用正确的常量。

否则 ruby 将使用后引入的常量,且多次引入不会重新引入。

且在模块中的非模块方法(可以直接调用的,模块的自方法)同名时,引入模块的类实例没法精准指定具体方法,所以一定要注意。

include & extend

  • include添加一个模块的方法到实例中

  • extend添加一个模块的方法到类中

ruby中使用include也可以增加实例方法,因为include有一个self.included的钩子函数,可以用它来修改类中对于模块的引入。

extend也有一个叫self.extended的方法,作用和include中的self.included类似。

同时included方法可以用作文件夹在时的一些初始化操作:


尤其值得注意的是,self其实就是调用的该类的一个单例类,可以从下面的例子看到:

参考资料:《Ruby中include和extend的比较》

module引入

不同于类,module本身不能实例对象,所以module通过extend引入别的module时,其中打方法会被添加到自身的metaclass中,并且可以通过module名直接调用:

Base通过include引入到方法则会在Base被其他class通过include引入时添加到class到实例方法中:


此时在KLASS_INself方法和实例的方法中都不能找到hello_exm方法:

可以先看一个例子:

可以看到在Base中没有找到SelfM中的方法。self的方法其实存放在metaclass中。而ruby中的引入不会将metaclass中的方法引入。

所以在上面的例子中ExM中的方法已经被添加到了Basemetaclass中,所以ExM中的方法在KLASS_INmetaclass和实例中都找不到对应方法。


如果Baseextend到其他到类,同样ExM中的方法不会被引入到该类中,而InM中到方法会被添加到该类的metaclass中:


还有一点值得注意的是,Module类中提供了一个名为module_function(*args)的方法。其可以将指定的方法添加到当前的module中,并且同时在该module被其他类引入时,将方法添加到类的实例中。

在缺省情况下,module_function调用之后会作用于其后添加的所有方法,类似private

Rails引入

concern

Rails中的ActiveSupport::Concern通过其中的append_features(base)方法:

使得引入了ActiveSupport::Concernmodule可以在其被其他类include引入时,将其中名为ClassMethodsmodule中的方法添加到对应类的metaclass中。

而其中的其他方法,或者在该module中通过include引入的其他module中的方法可以被引入到对应类的实例中。

autoload_path

通过命令行调用rails runner 'puts ActiveSupport::Dependencies.autoload_paths'可以看到Rails配置的autoload_paths

这些路径下的module将会被Rails自动加载,这使得我们在使用对应的module时不用添加表示文件结构的命名空间

例如不使用autoload_path的情况下,在app/modules下添加共用的module:

soft_delete.rb文件中module,必须添加命名空间Commons

在其他Record引入时也需要添加命名空间Commons

我们可以在项目根目录的application.rb中指定Rails自动将app/modules/commons加载,这使得我们可以省去在引入时使用命名空间:

app/modules/commons/soft_delete.rb文件的module修改为module SoftDeleteRecord的引入则可以修改为:

注:autoload_paths 在初始化过程中计算并缓存。目录结构发生变化时,要重启服务器。spring可能会缓存autoload_paths,即使是重启了服务,修改目录后需要暂时关闭spring

最后更新于

这有帮助吗?