- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
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_many
、 belongs_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/
尝试构建我的 Polymer 2.0 项目,但每次我尝试时,无论预设(es5-bundled、es6-bundled)还是单独的标志,我都会收到以下针对我拥有的一个 mixin 的警告: EdmMac
早上好,这是从 oracle 教程中摘录的,假设我们有 class Shape { /* ... */ } class Circle extends Shape { /* ... */ } class
这可能根本不是悖论,但从新手的角度来看,确实如此。 > Class.superclass => Module > Class.superclass.class => Class > Class.sup
我一直在实例化javascript中的子类,使用 object = new class () 但我注意到有人实例化使用 object.prototype = new class () 问题:有什么区别
编辑:用一个简单的用例创建了一个 repo 来复制问题,并在 API 平台问题队列中创建了一个问题 https://github.com/api-platform/api-platform/issue
我正在试验 Java 泛型。我了解使用 Java 泛型我们可以创建仅处理特定类型的类和方法。这使得能够在编译时检测编程错误。 我的问题听起来很奇怪。为什么使用 E extends Superclass
我有一个项目,涉及创建不同类型的对象,这些对象从父类(super class)继承方法和变量,但是当我尝试更改子类中的变量(更改为通过构造函数输入的值)时,变量保持相同的值它在父类(super cla
当我在线部署一个包含映射父类(super class)实体的 symfony 网站时,我收到以下错误: AnnotationException: [Semantical Error] The anno
我有ForceGaugeViewController类和ForceGaugeController类。我试图使 ForceGaugeController 类成为 ForceGaugeViewContro
我想创建一个 Num 的父类(super class),称为 Linear class Linear a where add :: a -> a -> a instance (Num a) =>
我正在尝试从类中删除父类(super class)。 ODB手册说“NULL将删除它”。但它给了我这个错误: "java.lang.IllegalArgumentException: Supercla
我有一个实体数组列表。实体类有 public void update(int delta, Level level, ArrayList entities){ //do stuff } 实体
问题 大多数情况下,当我重新导入项目到eclipse的时候,我重写的方法都不能被正确格式化,导致这样的错误:> The method must override a superclass method
我正在制作一个解决许多类似问题的程序。 我从一个看起来像(例如)的类开始: class problem { void init(); void solve(); int print_res
我的一些构造函数有问题。两个子类都需要得到相同的类(没有父类(super class)),这就是为什么这些类应该在父类(super class)中初始化: template class Super
我正在使用增强的 for 循环,它看起来像这样: public void addItems (Iterable items) { for (Item item : items) {
我是第一次开发 MATLAB OOP 项目。我的父类将有一个非常大的矩阵, child (很多)需要访问它。如何防止 child 复制数据? 在伪代码中我要求, classdef parent
我正在使用 CommonsWare 的 Android CWAC-Camera 库并尝试将我自己的 UI 用于相机 fragment 。如果这是一个愚蠢的问题,请原谅我。我特别关注这部分的 READM
我正在使用具有以下简单本体的 Protege 4.3(也尝试过 5-beta): Class: Person Class: Man SubClassOf: Person Ind
我正在玩 Scala By Example 开头的 QuickSort 示例并尝试将其调整为通用类型 A ,而不仅仅是 Int s。 到目前为止我的工作是 def sort[A new Y(i, -
我是一名优秀的程序员,十分优秀!