我正在查看此示例:
class SQLObject
def self.columns
return @columns if @columns
columns = DBConnection.execute2(<<-SQL).first
SELECT
"#{table_name}".*
FROM
"#{table_name}"
LIMIT
0
SQL
columns.map!(&:to_sym)
@columns = columns
end
def self.table_name
@table_name ||= self.name.underscore.pluralize
end
def insert
column_symbols = self.class.columns.drop(1)
column_names = column_symbols.map(&:to_s).join(", ")
question_marks = (['?'] * column_symbols.count).join(", ")
DBConnection.execute(<<-SQL, *attribute_values)
INSERT INTO
#{self.class.table_name} (#{column_names})
VALUES
(#{question_marks})
SQL
self.id = DBConnection.last_insert_row_id
end
end
我很困惑为什么可以在“ self.columns”方法中调用table_name方法,就像它是实例方法一样。 “ table_name”方法不是类方法吗?因此,在“ self.columns”方法中也不应将其称为“ self.class.table_name”吗?
答案 0 :(得分:1)
在抽象类中,self
引用正确的类,而不是对象。这就是为什么您无需显式告知self
答案 1 :(得分:0)
简短的回答:self
是Ruby中的隐式接收者。如果您将其保留,则邮件将发送到self
。换句话说,在消息发送中不必说self.foo
,因为foo
总是 与{{1} }。
长答案:Ruby中没有“ 类方法”之类的东西。实际上,所谓的“ 类方法”不过是在对象上定义的 singleton方法而已,而对象恰好是类self.foo
的实例。 / p>
实际上,Ruby中也没有 singleton方法。 singleton方法实际上只不过是在对象的 singleton类上定义的常规的 instance方法。
[注意:这并不是说您不应使用这些术语。说“类方法”显然比“类Class
对象的单例类的实例方法”更简单,并且传达意图更好。另外,请注意,存在诸如Object#singleton_methods
之类的方法。这些术语显然存在于Ruby Community 中,但重要的是要理解,这些概念 并不存在于Ruby Language 中。它们是一种通讯工具,不是真正的Ruby概念。]
单例类是与单个对象关联的类。每个对象都有一个 one 单例类,并且该对象是其Singleton类的 only 实例(“单例实例”,因此是名称)。
实际上,对象总是的继承层次结构始于其单例类,即对象的 class指针始终指向其单例类,而单例然后,该类的 superclass指针指向创建此对象的类。您只是看不到继承链中的单例类,因为诸如Object#class
之类的方法在计算继承链时会“跳过”单例类。但是,它总是在方法查找期间使用。
实际上,方法查找实际上非常简单(唯一的例外是Module#prepend
,在此解释中我将忽略它):
method_missing(original_message_name, ...)
重新启动算法,除非消息名称已经为Class
,在这种情况下,请提出一个NoMethodError
。所以,这全都归结为两个问题:
使用method_missing
定义方法时,Ruby将首先对表达式def foo.bar
求值,然后在结果对象的单例类上定义方法foo
( definee )。如果您不提供bar
,即您只说foo
,则default definee上将定义def bar
,这有点棘手。通常,它是最接近的词法包围模块定义。如果没有封闭模块定义,即您在顶层,则默认定义为Object
,方法可见性为private
。
当您使用bar
发送一条消息时,Ruby将首先对表达式foo.bar
求值,然后将消息foo
发送到结果对象(< em> receiver )。如果您不提供bar
,即您只说foo
,则默认接收者为bar
。
这带给我们下一个问题:
self
?在方法定义主体中,self
是导致该方法调用的消息发送的接收方。因此,如果您发送消息self
,那么在方法foo.bar
的定义中对self
的任何引用将在该方法的这一执行期间求值为bar
在模块或类定义主体中,foo
是类或方法本身。 (这就是为什么使用self
定义单例类的 singleton方法实例方法的原因在模块或类定义主体中起作用。)
在块或lambda文字中,def self.bar
在词法上被捕获,即self
在写块或lambda文字时的位置。 但是,有两种方法可以更改self
的评估方式,最著名的是BasicObject#instance_eval
,BasicObject#instance_exec
,Module#module_eval
,{{3 }},Module#module_exec
,Module#class_eval
,…方法族。
如果您始终在代码中的任何时候都知道这三个问题的答案(我在哪里定义方法,什么是方法调用的接收者,什么是self
),那么您就有了基本了解Ruby最重要的部分。
因此,将它们放在一起:
在使用self
定义方法时,我们位于类定义主体中,因此def self.columns
引用类对象本身(self
)。
语法SQLObject
在def foo.bar
的单例类上定义了方法bar
。在这种情况下,您可以在foo
的单例类上定义方法columns
。这意味着对象SQLObject
是Universe中唯一会响应消息SQLObject
的对象。
这两项同样适用于columns
的定义。
在self.table_name
的方法体内,self.columns
将动态引用接收者。
当您向self
发送消息columns
时(即您写SQLObject
),然后在SQLObject.columns
方法的主体内,接收方(即{{1} })设置为SQLObject::columns
。 (在这种情况下,接收者将始终为self
,因为这是单例类的方法。)
因此,在SQLObject
的方法体内,当您编写消息send SQLObject
时,这是一条带有隐式接收器的消息send。由于隐式接收者是self.columns
,因此等效于table_name
。此时的self
已绑定到self.table_name
,因此等效于self
。换句话说,邮件已发送到SQLObject
。
现在,按照我上面概述的算法,方法查找首先从SQLObject.table_name
中检索类指针。如前所述,类指针始终指向单例类。因此,我们查看SQLObject
的单例类,以查看是否找到了名为SQLObject
的方法,我们做到了!
方法查找完成,一切正常。