从实例方法调用中初始化

Initialize from within instance method call

在我的 Rails 申请中。我有一个模块,我在其中重写 .eql? 方法,如下所示

# lib/item_util.rb

module ItemUtil
  def eql?(item, field: "cost", op: "==")
    item.send(field).present? &&
      item.send(field).send(op, self.send(field))
  end
end

Item 模型

中包含
# app/models/item.rb

class Item < ApplicationRecord
  include ItemUtil
end

在我的控制器中,我想根据属性值检查各种条件。例如:

@item1 = Item.find(:id)
@item2 = Item.find(:id)

@item1.eql?(@item2, field: "discount", op: ">") # @item2.discount > @item1.discount
@item2.eql?(@item1, op: "<=") # @item1.cost <= @item2.cost

# ...

所有这些工作正常,我想以如下更简洁的方式编写 ItemUtil 模块:

  module ItemUtil
    attr_accessor :item, :field

    def initialize(item, field: "cost")
      @item = item
      @field = field
    end

    def eql?(item, field: "cost", op: "==")
      new_item.present? && new_item.send(op, current_item)
    end

    def new_item
      @item.send(@field)
    end

    def current_item
      self.send(@field)
    end
  end

此 returns TypeError (nil is not a symbol nor a string) for @field inside new_item 方法,因为 initialize 未在任何地方调用

Traceback (most recent call last):
        2: from lib/item_util.rb:12:in `eql?'
        1: from lib/item_util.rb:17:in `new_item'
TypeError (nil is not a symbol nor a string)

但我不想改变我在对象上调用 .eql? 的方式,即我想保持这些行完好无损

@item1.eql?(@item2, field: "discount", op: ">") # @item2.discount > @item1.discount
@item2.eql?(@item1, op: "<=") # @item1.cost <= @item2.cost

您不能实例化模块的实例。 (您可以实例化 class 的实例,但不能实例化 模块 。)

事实上,您在这里实际做的是覆盖Item#initialize的定义!!

坚持您的一般设计模式,我建议将此逻辑抽象为 新的 class - 例如 ItemComparator

class ItemComparator
  attr_reader :item, :other

  def initialize(item, other)
    @item = item
    @other = other
  end

  def eql?(field:, op:)
    item_field = item.send(field)
    other_field = other.send(:field)

    other_field.present? && item_field.send(op, other_field)
  end
end

module ItemUtil
  def eql?(other, field: "cost", op: "==")
    ItemComparator.new(self, other).eql?(field: field, op: op)
  end
end

class Item < ApplicationRecord
  include ItemUtil
end