Ruby:如何为 class 字段的 << 方法添加验证

Ruby: How to add validation for a class field's << method

我了解如何实现(验证)setter (def item=),但如何拦截字段上的 << 操作?

class Bla 
  attr_reader :item

  def initialize
    @item = []
  end

  # only called for =, +=, -= operations (not <<)
  def item=(value)
    puts "Changing value to #{value}"
    # pretend that there is a conditional here
    @item = value
  end

  # This is wrong:
  #def item<<(value)
  #  puts "adding value #{value}"
  #  @item << value
  #end

end

b = Bla.new
b.item = ['one']  # works
b.item += ['one'] # works
b.item << 'two'   # bypasses my setter

我试过了def item<<(value),好像不行。

class Pit
  def <<(dirt)
    puts 'shovel ' + dirt
  end
end
Pit.new << 'sandy loam'
# => shovel sandy loam

当您调用 b.item << 'two' 时,您是在直接调用 item 上的 << 方法。所以你在这里有几个选择:

  1. 直接在您的 Bla class 上实施 <<,然后使用 b << 'two':

    # in class Bla
    def <<(value)
      # do validation here
      @item << value
    end
    
  2. 使用其他一些更好命名的包装器方法名称,例如 add_item:

    # in class Bla
    def add_item(value)
      # do validation here
      @item << value
    end
    
  3. @item 使用一个特殊的数组 class,它有一个自定义定义 <<:

    class MyArray < Array
      def <<(item)
        # run validation here
        super
      end
    end
    
    # in Bla class
    def initialize
      @item = MyArray.new
    end
    

我可能会选择选项 2,它最简单易读。

这是另一个建议:

class Bla 
  # DO NOT DEFINE A READER:
  # attr_reader :item

  def initialize
    @items = []
  end

  def set_items(new_items)
    puts "Changing value to #{new_items}"
    # pretend that there is a conditional here
    @items = new_items
  end

  def remove_item(item)
    # pretend that there is a conditional here
    @items -= items
  end

  def add_item(item)
    # pretend that there is a conditional here
    @items += items
  end
end

通过不直接公开 @items 对象,您可以完全控制如何操作变量的接口。

(除非来电者做了一些非常骇人听闻的事情,比如 bla.instance_variable_get('@items')!!)