在 rspec 中定义的匿名 class 不会响应新的

Definied Anonymous class in rspec won't respond to new

所以我有以下匿名 class 定义:

  let!(:fake_class) do
    Class.new(Integer) do
      def initialize(value)
        @value = value
      end

      def ==(other)
        @value == other
      end

      def coerce(other)
        [@value, other]
      end

      def to_s
        @value.to_s
      end
    end
  end

但是当我这样做时:

fake_class.new 4

我刚刚得到undefined method 'new' for #<Class:0x00007fc065377c88>

我试过

define_method :initialize do |value|
  @value = value
end

没有区别

它响应新消息的唯一方式是如果我这样做

class << self
  def new(value)
    @value = value
  end
end

但这显然行不通,因为我需要它表现得像一个真正的 class。

为什么我看到很多使用 intialize 的教程并且它按预期工作但它似乎不适合我?是因为我在 rspec 之类的地方定义它吗?

您的代码是正确的,但是 Integer 没有响应 .new 因此您的 child class 也不会响应 .new

irb(main):001:0> Integer.new
NoMethodError (undefined method `new' for Integer:Class)

当您调用 Integer(123) 时,您实际上调用了此处定义的全局函数: https://github.com/ruby/ruby/blob/v2_5_1/object.c#L3987 https://github.com/ruby/ruby/blob/v2_5_1/object.c#L3178

此处的问题与rspec无关,也与匿名class无关。

问题是在ruby,你不能subclass Integer*.

Ruby 将小的 Integers(以前称为 Fixnums)存储为立即值,使用单词的一些低位来标记它,而不是指向堆上对象的指针。因此,您不能将方法添加到 Integer 的单个 "instance",也不能将其子 class。

如果您真的想要 "Integer-like" class,您可以使用具有整数实例变量的 class 构建变通方法,并适当地转发方法调用:

class FakeInteger
  def initialize(integer)
    @integer = integer
  end

  def method_missing(name, *args, &blk)
    ret = @integer.send(name, *args, &blk)
    ret.is_a?(Numeric) ? FakeInteger.new(ret) : ret
  end
end

* 技术上你可以,但是因为你不能从它实例化任何对象,所以它很没用:)