gpt4 book ai didi

ruby-on-rails - 为包含的方法调用 super 会导致 "no superclass method"错误 - ActiveSupport

转载 作者:行者123 更新时间:2023-12-05 00:56:15 25 4
gpt4 key购买 nike

Ruby 中有一个“super”关键字,它正在查看祖先链,以便找到链上的第一个方法实现并执行它。所以,这就是它在 Ruby 中的工作方式,不足为奇:

module Mammal
def walk
puts "I'm walking"
end
end
require '~/Documents/rubytest/super/mammal.rb'

class Cat
include Mammal

def walk
super
end
end
2.7.0 :001 > simba = Cat.new
2.7.0 :002 > simba.walk
I'm walking
=> nil

这是理想的行为。现在,在 Rails 中有 ActiveSupport::Concern 为模块提供了一些额外的功能。如果您使用 ActiveSupport 帮助器以类似的方式进行某种,会发生以下情况:

module MammalMixin
extend ActiveSupport::Concern

included do
def show
@mammal = Mammal.find(params[:id])
end
end
end
class SomeController < ApplicationController
include MammalMixin

def show
super
end
end

如果您到达该 Controller ,则会出错:super:#SomeController:0x000055f07c549bc0 没有父类(super class)方法“show”

当然,可以不使用“included do”帮助器并恢复为普通的 Ruby 样式,但有人可以建议 ActiveSupport::Concern 中究竟是什么阻止“super”正常工作并(也许)解释背后的基本原理这个?

我一直在查看 active_support/concern.rb 中的源代码,但没看懂。

最佳答案

答案就在the documentation of ActiveSupport::Concern#included [粗体强调我的]:

Evaluate given block in context of base class, so that you can write class macros here.

所以,这里是你的 block 的内容:

def show
@mammal = Mammal.find(params[:id])
end

根据文档,在基类的上下文中评估此 block 。现在,当你评估一个方法时会发生什么def类上下文中的初始表达式?你在那个类中定义一个方法!

所以,你在这里所做的是定义一个名为 show 的方法。在 SomeController就像你写的一样上课:

class SomeController < ApplicationController
def show
@mammal = Mammal.find(params[:id])
end

def show
super
end
end

换句话说,您的第二个定义是overwriting the first definition, not overriding it ,所以没有 super 方法。

正确的方法使用ActiveSupport::Concern#included是这样的:

module MammalMixin
extend ActiveSupport::Concern

def show
@mammal = Mammal.find(params[:id])
end

included do
acts_as_whatever
end
end

ActiveSupport::Concern#included ,正如文档所说,用于在类的上下文中执行代码(例如“类宏”,如 acts_as_*has_manybelongs_to 等)。

Here's how including a module normally works :

当你写作时

class C
include M
end

您调用 Module#include 方法(它不会被 Class 覆盖,因此不会更改地继承)。

现在,Module#include实际上并没有做任何有趣的事情。它基本上看起来像这样:

class Module
def include(mod)
mod.append_features(self)
end
end

这是一个经典的 Double Dispatch 习惯用法,可以让模块完全控制它希望如何被包含到类中。当打电话时

C.include(M)

这意味着 C处于控制之中,它只是委托(delegate)给

M.append_features(C)

其中包含 M控制。

什么 Module#append_features 通常是这样的(我会用伪 Ruby 来描述它,因为在 Ruby 中无法解释这种行为,因为必要的数据结构是引擎内部的):

class Module
def append_features(base)
if base.is_a?(Module)
base.included_modules << self unless base.included_modules.include?(self)
else
old_superclass = base.__superclass__

klazz = Class.new(old_superclass)
klazz.__constant_table__ = __constant_table__
klazz.__class_variable_table__ = __class_variable_table__
klazz.__instance_variable_table__ = __instance_variable_table__
klazz.__method_table__ = __method_table__
klazz.__virtual__ = true

base.__superclass__ = klazz
end

included(base)

self
end
end

那么,Ruby 创建了一个新类,称为包含类,它的常量表指针、类变量表指针、实例变量表指针和方法表指针都指向常量表、模块的类变量表、实例变量表、方法表。基本上,我们正在创建一个隐藏模块的类。

然后它使这个类成为该类的新父类(super class),并使旧父类(super class)成为包含类的父类(super class)。实际上,它将类和父类(super class)之间的包含类插入到继承链中。

这样做是因为 the method lookup algorithm doesn't need to know anything about mixins and can be kept very simple :去类,检查方法是否存在,如果不获取父类(super class),检查方法是否存在,依此类推。由于方法查找是面向对象语言执行引擎中最常见和最重要的操作之一,因此算法的简单和快速至关重要。

这个 include 类将被 Class#superclass 跳过方法,所以你看不到它,但它会显示为 Module#ancestors .

这就是为什么super有效:因为模块字面意思变成了父类(super class)。

我们从 C < Object 开始我们最终得到 C < M' < Object .

现在, ActiveSupport::Concern 完全搞砸了。

ActiveSupport::Concern#included method 的有趣部分这是:

@_included_block = block

它只是存储 block 供以后使用。

如上所述,当 MammalMixin被纳入 SomeController ,即当 SomeController.include(MammalMixin)被调用,SomeController.include (即 Module#include )将依次调用 MammalMixin.append_features(SomeController) . MammalMixin.append_features在这种情况下是 ActiveSupport::Concern#append_features ,最有趣的部分是:

base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)

如您所见,它使用的是 Module#class_eval 为了评估它之前在包含它的基类的上下文中保存的 block 。这就是使您的方法最终成为基类而不是模块的实例方法的原因。

关于ruby-on-rails - 为包含的方法调用 super 会导致 "no superclass method"错误 - ActiveSupport,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62579411/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com