我正在尝试了解shrine gem源代码,它是文件附件的工具包。您可以在轨道中定义模型上的上传器,如下所示:
class Picture < ApplicationRecord
include ImageUploader::Attachment.new(:image)
end
可以在此link.
找到Attachment
的类定义
这就是全部:
class Attachment < Module
@shrine_class = ::Shrine
end
我的假设是,这允许您在include中实例化类,以便现在可以使用包含它的方法,类似于mixin。 Module
是红宝石类吗?这究竟是如何工作的?
编辑:
为清楚起见,ImageUploader
在我的应用中定义如下:
class ImageUploader < Shrine
plugin :remove_attachment
end
所以ImageUploader::Attachment.new(:image)
正在使用Shrine中定义的Attachment
类。
答案 0 :(得分:2)
Module
确实是一个Ruby类。类Module
的实例是Ruby模块。为了说明,这两种定义模块的方法是等效的:
module MyModule
# ...
end
# is equivalent to
MyModule = Module.new do
# ...
end
如果Module
的实例是Ruby模块,则意味着Module
的任何子类的实例也是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),而不是创建Module
的整个子类?那么,有两个主要原因:
首先,这样可以提供更好的内省,因为您不会在模型的祖先列表中看到#<Module:0x007f8183d27ab0>
,而是看到指向其定义的实际Shrine::Attachment
实例。你仍然可以manually override #to_s
and #inspect
,但这更好。
其次,由于Shrine::Attachment
现在是一个类,因此其他Shrine插件可以使用更多行为来扩展它。因此,remote_url插件会添加#<attachment>_remote_url
访问者,data_uri插件会添加#<attachment>_data_uri
访问者等。
答案 1 :(得分:1)
Modules是一种将方法,类和常量分组在一起的方法。这是第一个分组类的样本
应用程序/服务/ 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
在constroller文件中,您可以使用 Module_name :: Class_name.new 调用类,如下所示
@purchase_svc = PurchaseService::PurchaseRequest.new
@purchase_svc.request_item
@purchase_svc = PurchaseService::PurchaseOrder.new
@purchase_svc.order_item
但有时您希望将不自然形成类的内容组合在一起。 这是分组的第二个例子,但不是类形式
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
答案 2 :(得分:1)
注意:准备这个答案需要几个小时。与此同时,janko-m回答得很好。
Rails或Shrine的人们通过阅读Ruby书籍,将人们在Ruby中可以做的事情的知识推向远远超出人们想象的水平,而且我已经阅读了十几本。
99%的时间包括
形式include SomeModule
和SomeModule
在单独的文件some_module.rb
中定义,该文件与require 'some_module'
合并到当前源文件中。
此
include ImageUploader::Attachment.new(:image)
由于很多原因,很棘手。
=== inner class ===
98%的时候,一个类是一个外部对象,主要包含def方法,一些包括一些类实例变量。我没有写过大量的Ruby代码,但在特殊情况下只有一次是内部类。从外面看,它只能通过充分使用
访问路径,例如Shrine::Attachment
或Shrine::Plugins::Base::AttacherMethods
。
我不知道子类&#34;继承&#34;内部类,以便人们可以写
ImageUploader::Attachment
=== Module.new ===
如果您阅读了足够的Ruby文档,那么您会发现类和模块之间的差异是我们无法实现模块的1到3000倍。模块仅用于围绕一个代码或(主要)mixin方法创建一个命名空间(更准确地说,包括SomeModule创建一个匿名超类,以便方法的搜索路径从类到SomeModule,然后到超类(如果没有明确定义,则为对象))。
因此,我会在折磨下宣誓没有新的方法,因为没有必要。但有一个,它返回一个匿名模块。
好吧,说过这里,我们实例化了类ImageUploader::Attachment
,而不是模块,甚至Module.new
实例化了类Module
。
=== include expression ===
对于不使用常量但表达式的1%包含,表达式必须返回一个模块。而且你有答案为什么Attachment继承自Module。 Whitout这样的继承,包括会抱怨。运行以下代码,它的工作原理。但如果你取消注释
# include ImageUploader::Attachment_O.new(:image)
在类图片中,有一个错误:
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>'
file 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
这就是全部:
乍一看,附件的定义似乎很奇怪,因为它是空的。我还没有详细研究过shrine.rb,但我已经看过了:
# Load a new plugin into the current class ...
def plugin(plugin, *args, &block)
...
self::Attachment.include(plugin::AttachmentMethods) if defined?(plugin::AttachmentMethods)
显然,附件后来通过包含一个模块来填充方法,或者更确切地说,include
为Attachment创建一个匿名超类,它指向AttachmentMethods,以便方法搜索机制在包含的模块中查找方法。
另请参阅How does Inheritance work in Ruby?
。