继承自 class 模块
Inheriting from class Module
我正在尝试了解 shrine gem 源代码,它是一个用于文件附件的工具包。您可以在 rails 中为您的模型定义上传器,如下所示:
class Picture < ApplicationRecord
include ImageUploader::Attachment.new(:image)
end
Attachment
的 class 定义可以在 link.
中找到
仅此而已:
class Attachment < Module
@shrine_class = ::Shrine
end
我的假设是,这允许您在包含中实例化 class,以便这些方法现在可以在您包含它的地方使用,类似于混合。 Module
是 ruby class 吗?这究竟是如何工作的?
编辑:
为清楚起见,ImageUploader
在我的应用中定义如下:
class ImageUploader < Shrine
plugin :remove_attachment
end
所以 ImageUploader::Attachment.new(:image)
正在使用 Shrine 中定义的 Attachment
class。
Modules 是一种将方法、classes 和常量组合在一起的方法。这是第一个样本分组 class
app/services/purchase_service.rb
module PurchaseService
class PurchaseRequest
def initialize
# init value
end
def request_item
# action
end
end
class PurchaseOrder
def initialize
# init value
end
def order_item
# action
end
end
end
在控制器文件中,您可以使用 Module_name::Class_name.new 调用 class,如下所示
@purchase_svc = PurchaseService::PurchaseRequest.new
@purchase_svc.request_item
@purchase_svc = PurchaseService::PurchaseOrder.new
@purchase_svc.order_item
但有时您想要将不会自然形成 class 的事物组合在一起。
这是分组的第二个示例,但不是 class 形式
module_collection.rb, (一个文件有两个模块栈和队列)
module Stacklike
def stack
@stack ||= []
end
def add_to_stack(obj)
@stack.push(obj)
end
def take_from_stack
@stack.pop
end
end
module Queuelike
#
end
现在,如果一个对象(例如货物)需要具有堆栈功能,那么我将包含该模块。
cargo.rb,
require './module_collection.rb'
include Stacklike
# as cargo needs stack
class
def initialize
stack
# this will call stack method inside module_collection.rb
end
end
Module
确实是一个Rubyclass。 class Module
的实例是 Ruby 模块。为了说明,这两种定义模块的方式是等价的:
module MyModule
# ...
end
# is equivalent to
MyModule = Module.new do
# ...
end
如果 Module
的实例是 Ruby 模块,这意味着 [=12] 的任何 subclass 的实例=]也是一个Ruby模块,包括Shrine::Attachment
。这是有道理的,因为我们知道我们只能 include
模块,所以 Shrine::Attachment
的实例必须是一个模块。
因为 Shrine 的 plugin system design, 这个:
class Attachment < Module
@shrine_class = ::Shrine
end
不是 Shrine::Attachment
的全部实现;实际实现在 Shrine::Plugins::Base::AttachmentMethods
模块中定义,该模块包含在 Shrine::Attachment
.
中
如果我们查看 Shrine::Attachment.new
的实现,我们可以看到它根据给定的属性名称动态定义自身的方法。例如,Shrine::Attachment.new(:image)
将生成一个定义了以下方法的模块:#image_attacher
、#image=
、#image
和 #image_url
。然后,这些方法将被添加到包含该 Shrine::Attachment
实例的模型中。
为什么我没有通过 Module.new
(如 Refile does)创建新模块的方法,而不是创建 [=12= 的整个子 class ]?嗯,主要有两个原因:
首先,这提供了更好的内省,因为您现在看到的不是模型的祖先列表中的 #<Module:0x007f8183d27ab0>
,而是指向其定义的实际 Shrine::Attachment
实例。你仍然可以 manually override #to_s
and #inspect
,但这个更好。
其次,由于 Shrine::Attachment
现在是 class,其他 Shrine 插件可以用更多行为扩展它。所以 remote_url plugin adds the #<attachment>_remote_url
accessor, data_uri 插件添加了 #<attachment>_data_uri
访问器等
注意:准备这个答案需要几个小时。同时 janko-m 已经回答得很好。
Rails 或 Shrine 的人已经将人们在 Ruby 中可以做什么的知识推向了一个远远超出阅读 Ruby 书籍所能想象的水平,而我读了十几本。
99% 的时间包含在表单中
include SomeModule
和 SomeModule
在单独的文件 some_module.rb
中定义,该文件通过 require 'some_module'
.
合并到当前源文件中
这个
include ImageUploader::Attachment.new(:image)
由于很多原因很棘手。
=== 内部 class ===
98% 的时间,class 是一个外部对象,主要包含 def 方法,一些包含和少量 class 实例变量。我没有写过大量的 Ruby 代码,但在特殊情况下只写过一次内部 class 。从外面看,它只能通过提供完整的
访问路径,例如 Shrine::Attachment
或 Shrine::Plugins::Base::AttacherMethods
.
我不知道一个 subclass "inherits" 内部 classes 这样就可以写
ImageUploader::Attachment
=== Module.new ===
如果您阅读了足够多的 Ruby 文档,您会发现 class 和模块之间的区别是我们无法实例化模块 1'000 次。模块仅用于围绕一个人的代码或(主要)在 class 中混合方法创建命名空间(更确切地说,include SomeModule 创建一个匿名 superclass 以便方法的搜索路径从 class 到 SomeModule,然后到 superclass(对象,如果没有明确定义))。
所以我可以发誓,Module没有新的方法,因为没有必要。但是有一个 return 是一个匿名模块。
嗯,话说回来,这里我们实例化了classImageUploader::Attachment
,不是模块,甚至Module.new
实例化了classModule
。
=== 包含表达式 ===
对于不使用常量而是表达式的 1% 的包含,表达式必须 return 一个模块。您对为什么 Attachment 继承自 Module 有了自己的答案。如果没有这样的继承,include 会抱怨。 运行 下面的代码,可以正常工作。但是如果你取消注释
# include ImageUploader::Attachment_O.new(:image)
在class图片中,有错误:
t.rb:28:in `include': wrong argument type Shrine::Attachment_O (expected Module) (TypeError)
from t.rb:28:in `<class:Picture>'
from t.rb:27:in `<main>'
文件 t.rb :
class Shrine
class Attachment_O
def initialize(parm=nil)
puts "creating an instance of #{self.class.name}"
end
end
class Attachment_M < Module
def initialize(parm=nil)
puts "creating an instance of #{self.class.name}"
end
end
end
print 'Attachment_O ancestors '; p Shrine::Attachment_O.ancestors
print 'Attachment_M ancestors '; p Shrine::Attachment_M.ancestors
class ImageUploader < Shrine
end
imupO = ImageUploader::Attachment_O.new
imupM = ImageUploader::Attachment_M.new
print 'imupO is a Module ? '; p imupO.is_a?(Module)
print 'imupM is a Module ? '; p imupM.is_a?(Module)
class Picture
# include ImageUploader::Attachment_O.new(:image)
include ImageUploader::Attachment_M.new(:image)
end
执行:
$ ruby -w t.rb
Attachment_O ancestors [Shrine::Attachment_O, Object, Kernel, BasicObject]
Attachment_M ancestors [Shrine::Attachment_M, Module, Object, Kernel, BasicObject]
creating an instance of Shrine::Attachment_O
creating an instance of Shrine::Attachment_M
imupO is a Module ? false
imupM is a Module ? true
creating an instance of Shrine::Attachment_M
This is all it is:
乍一看,附件的定义似乎很奇怪,因为它是空的。 shrine.rb我没有详细研究过,但是我看到了这个:
# Load a new plugin into the current class ...
def plugin(plugin, *args, &block)
...
self::Attachment.include(plugin::AttachmentMethods) if defined?(plugin::AttachmentMethods)
显然,Attachment 稍后会通过包含模块来填充方法,或者更准确地说,include
创建一个指向 Attachment 的匿名 superclass,它指向 AttachmentMethods,以便方法搜索机制在包含的模块中查找方法。
另请参阅 继承在 Ruby 中如何工作?
.
我正在尝试了解 shrine gem 源代码,它是一个用于文件附件的工具包。您可以在 rails 中为您的模型定义上传器,如下所示:
class Picture < ApplicationRecord
include ImageUploader::Attachment.new(:image)
end
Attachment
的 class 定义可以在 link.
仅此而已:
class Attachment < Module
@shrine_class = ::Shrine
end
我的假设是,这允许您在包含中实例化 class,以便这些方法现在可以在您包含它的地方使用,类似于混合。 Module
是 ruby class 吗?这究竟是如何工作的?
编辑:
为清楚起见,ImageUploader
在我的应用中定义如下:
class ImageUploader < Shrine
plugin :remove_attachment
end
所以 ImageUploader::Attachment.new(:image)
正在使用 Shrine 中定义的 Attachment
class。
Modules 是一种将方法、classes 和常量组合在一起的方法。这是第一个样本分组 class
app/services/purchase_service.rb
module PurchaseService
class PurchaseRequest
def initialize
# init value
end
def request_item
# action
end
end
class PurchaseOrder
def initialize
# init value
end
def order_item
# action
end
end
end
在控制器文件中,您可以使用 Module_name::Class_name.new 调用 class,如下所示
@purchase_svc = PurchaseService::PurchaseRequest.new
@purchase_svc.request_item
@purchase_svc = PurchaseService::PurchaseOrder.new
@purchase_svc.order_item
但有时您想要将不会自然形成 class 的事物组合在一起。 这是分组的第二个示例,但不是 class 形式
module_collection.rb, (一个文件有两个模块栈和队列)
module Stacklike
def stack
@stack ||= []
end
def add_to_stack(obj)
@stack.push(obj)
end
def take_from_stack
@stack.pop
end
end
module Queuelike
#
end
现在,如果一个对象(例如货物)需要具有堆栈功能,那么我将包含该模块。
cargo.rb,
require './module_collection.rb'
include Stacklike
# as cargo needs stack
class
def initialize
stack
# this will call stack method inside module_collection.rb
end
end
Module
确实是一个Rubyclass。 class Module
的实例是 Ruby 模块。为了说明,这两种定义模块的方式是等价的:
module MyModule
# ...
end
# is equivalent to
MyModule = Module.new do
# ...
end
如果 Module
的实例是 Ruby 模块,这意味着 [=12] 的任何 subclass 的实例=]也是一个Ruby模块,包括Shrine::Attachment
。这是有道理的,因为我们知道我们只能 include
模块,所以 Shrine::Attachment
的实例必须是一个模块。
因为 Shrine 的 plugin system design, 这个:
class Attachment < Module
@shrine_class = ::Shrine
end
不是 Shrine::Attachment
的全部实现;实际实现在 Shrine::Plugins::Base::AttachmentMethods
模块中定义,该模块包含在 Shrine::Attachment
.
如果我们查看 Shrine::Attachment.new
的实现,我们可以看到它根据给定的属性名称动态定义自身的方法。例如,Shrine::Attachment.new(:image)
将生成一个定义了以下方法的模块:#image_attacher
、#image=
、#image
和 #image_url
。然后,这些方法将被添加到包含该 Shrine::Attachment
实例的模型中。
为什么我没有通过 Module.new
(如 Refile does)创建新模块的方法,而不是创建 [=12= 的整个子 class ]?嗯,主要有两个原因:
首先,这提供了更好的内省,因为您现在看到的不是模型的祖先列表中的 #<Module:0x007f8183d27ab0>
,而是指向其定义的实际 Shrine::Attachment
实例。你仍然可以 manually override #to_s
and #inspect
,但这个更好。
其次,由于 Shrine::Attachment
现在是 class,其他 Shrine 插件可以用更多行为扩展它。所以 remote_url plugin adds the #<attachment>_remote_url
accessor, data_uri 插件添加了 #<attachment>_data_uri
访问器等
注意:准备这个答案需要几个小时。同时 janko-m 已经回答得很好。
Rails 或 Shrine 的人已经将人们在 Ruby 中可以做什么的知识推向了一个远远超出阅读 Ruby 书籍所能想象的水平,而我读了十几本。
99% 的时间包含在表单中
include SomeModule
和 SomeModule
在单独的文件 some_module.rb
中定义,该文件通过 require 'some_module'
.
这个
include ImageUploader::Attachment.new(:image)
由于很多原因很棘手。
=== 内部 class ===
98% 的时间,class 是一个外部对象,主要包含 def 方法,一些包含和少量 class 实例变量。我没有写过大量的 Ruby 代码,但在特殊情况下只写过一次内部 class 。从外面看,它只能通过提供完整的
访问路径,例如 Shrine::Attachment
或 Shrine::Plugins::Base::AttacherMethods
.
我不知道一个 subclass "inherits" 内部 classes 这样就可以写
ImageUploader::Attachment
=== Module.new ===
如果您阅读了足够多的 Ruby 文档,您会发现 class 和模块之间的区别是我们无法实例化模块 1'000 次。模块仅用于围绕一个人的代码或(主要)在 class 中混合方法创建命名空间(更确切地说,include SomeModule 创建一个匿名 superclass 以便方法的搜索路径从 class 到 SomeModule,然后到 superclass(对象,如果没有明确定义))。
所以我可以发誓,Module没有新的方法,因为没有必要。但是有一个 return 是一个匿名模块。
嗯,话说回来,这里我们实例化了classImageUploader::Attachment
,不是模块,甚至Module.new
实例化了classModule
。
=== 包含表达式 ===
对于不使用常量而是表达式的 1% 的包含,表达式必须 return 一个模块。您对为什么 Attachment 继承自 Module 有了自己的答案。如果没有这样的继承,include 会抱怨。 运行 下面的代码,可以正常工作。但是如果你取消注释
# include ImageUploader::Attachment_O.new(:image)
在class图片中,有错误:
t.rb:28:in `include': wrong argument type Shrine::Attachment_O (expected Module) (TypeError)
from t.rb:28:in `<class:Picture>'
from t.rb:27:in `<main>'
文件 t.rb :
class Shrine
class Attachment_O
def initialize(parm=nil)
puts "creating an instance of #{self.class.name}"
end
end
class Attachment_M < Module
def initialize(parm=nil)
puts "creating an instance of #{self.class.name}"
end
end
end
print 'Attachment_O ancestors '; p Shrine::Attachment_O.ancestors
print 'Attachment_M ancestors '; p Shrine::Attachment_M.ancestors
class ImageUploader < Shrine
end
imupO = ImageUploader::Attachment_O.new
imupM = ImageUploader::Attachment_M.new
print 'imupO is a Module ? '; p imupO.is_a?(Module)
print 'imupM is a Module ? '; p imupM.is_a?(Module)
class Picture
# include ImageUploader::Attachment_O.new(:image)
include ImageUploader::Attachment_M.new(:image)
end
执行:
$ ruby -w t.rb
Attachment_O ancestors [Shrine::Attachment_O, Object, Kernel, BasicObject]
Attachment_M ancestors [Shrine::Attachment_M, Module, Object, Kernel, BasicObject]
creating an instance of Shrine::Attachment_O
creating an instance of Shrine::Attachment_M
imupO is a Module ? false
imupM is a Module ? true
creating an instance of Shrine::Attachment_M
This is all it is:
乍一看,附件的定义似乎很奇怪,因为它是空的。 shrine.rb我没有详细研究过,但是我看到了这个:
# Load a new plugin into the current class ...
def plugin(plugin, *args, &block)
...
self::Attachment.include(plugin::AttachmentMethods) if defined?(plugin::AttachmentMethods)
显然,Attachment 稍后会通过包含模块来填充方法,或者更准确地说,include
创建一个指向 Attachment 的匿名 superclass,它指向 AttachmentMethods,以便方法搜索机制在包含的模块中查找方法。
另请参阅 继承在 Ruby 中如何工作?
.