我尝试调整此帖子中引用的方法链示例(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
。有没有更好的方法来过滤数组,以便更容易同时支持strength
和limit
?
赞赏其他编码建议。
答案 0 :(得分:4)
您的Coffee
类定义name
和strength
的方法访问者。对于单个coffee
对象,您可以使用
coffee.name
# => "Laos"
coffee.strength
# => 10
在Criteria#each
方法中,您尝试使用下标运算符访问属性,即c[:strength]
(在这种情况下c
是Coffee
的实例)。现在,在你的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