使用Enumerable类

时间:2018-01-26 23:34:42

标签: ruby

我尝试调整此帖子中引用的方法链示例(Method chaining and lazy evaluation in Ruby)来处理实现Enumerable类(Implement a custom Enumerable collection class in Ruby )的对象

咖啡班:

class Coffee

  attr_accessor :name
  attr_accessor :strength

  def initialize(name, strength)
    @name = name
    @strength = strength
  end

  def <=>(other_coffee)
    self.strength <=> other_coffee.strength
  end

  def to_s
    "<name: #{name}, strength: #{strength}>"
  end

end

标准类:

class Criteria

  def initialize(klass)
    @klass = klass
  end

  def criteria
    @criteria ||= {:conditions => {}}
  end

  # only show coffee w/ this strength
  def strength(strength)
    criteria[:strength] = strength
    self
  end

  # if there are multiple coffees, choose the first n=limit
  def limit(limit)
    criteria[:limit] = limit
    self
  end

  # allow collection enumeration
  def each(&block)
    @klass.collection.select { |c| c[:strength] == criteria[:strength] }.each(&block)
  end

end

CoffeeShop课程:

class CoffeeShop

  include Enumerable

  def self.collection
    @collection=[]
    @collection << Coffee.new("Laos", 10)
    @collection << Coffee.new("Angkor", 7)
    @collection << Coffee.new("Nescafe", 1)
  end

  def self.limit(*args)
    Criteria.new(self).limit(*args)
  end

  def self.strength(*args)
    Criteria.new(self).strength(*args)
  end

end

当我运行此代码时:

CoffeeShop.strength(10).each { |c| puts c.inspect }

我收到错误:

criteria.rb:32:in block in each': undefined method '[]' for #<Coffee:0x007fd25c8ec520 @name="Laos", @strength=10>

我确定我没有正确定义Criteria.each方法,但我不确定如何纠正它。我该如何纠正?

此外,each方法不支持当前编写的limit。有没有更好的方法来过滤数组,以便更容易同时支持strengthlimit

赞赏其他编码建议。

1 个答案:

答案 0 :(得分:4)

您的Coffee类定义namestrength的方法访问者。对于单个coffee对象,您可以使用

获取属性
coffee.name
# => "Laos"
coffee.strength
# => 10

Criteria#each方法中,您尝试使用下标运算符访问属性,即c[:strength](在这种情况下cCoffee的实例)。现在,在你的Coffee类中,你还没有实现下标访问器,导致你在那里看到NoMethodError

您可以按照以下方式调整Criteria#each方法:

def each(&block)
  @klass.collection.select { |c| c.strength == criteria[:strength] }.each(&block)
end

或者您可以在Coffee类上实现下标运算符:

class Coffee
  attr_accessor :name
  attr_accessor :strength

  # ...

  def [](key)
    public_send(key)
  end

  def []=(key, value)
    public_send(:"#{key}=", value)
  end
end

Noe,作为附录,您可能希望在任何情况下扩展您的each方法。一个常见的(通常是隐式预期的)模式是,如果没有给出块,像each这样的方法会返回一个Enumerator。这允许使用CoffeeShop.strength(10).each.group_by(&:strength)等模式。

您可以在方法中实现这个简单的在线:

def each(&block)
  return enum_for(__method__) unless block_given?
  @klass.collection.select { |c| c.strength == criteria[:strength] }.each(&block)
end