Александър обнови решението на 25.10.2012 20:36 (преди около 12 години)
+class Collection
+ include Enumerable
+
+ def self.parse(text)
+ songs = []
+ text.split("\n").each_slice(4) do |name, artist, album, separator|
+ songs << Song.new(name, artist, album)
+ end
+ Collection.new songs
+ end
+
+ def initialize(songs)
+ @songs = songs
+ end
+
+ def artists
+ @songs.map(&:artist).uniq
+ end
+
+ def albums
+ @songs.map(&:album).uniq
+ end
+
+ def names
+ @songs.map(&:name).uniq
+ end
+
+ def adjoin(other)
+ Collection.new to_a | other.to_a
+ end
+
+ def each
+ @songs.each { |song| yield song }
+ end
+
+ def filter(criteria)
+ criteria.filter self
+ end
+
+end
+
+class Song
+
+ attr_accessor :name, :artist, :album
+ def initialize(name, artist, album)
+ @name, @artist, @album = name, artist, album
+ end
+end
+
+class Criteria
+
+ def self.album(query)
+ Criteria.new :album, query
+ end
+
+ def self.artist(query)
+ Criteria.new :artist, query
+ end
+
+ def self.name(query)
+ Criteria.new :name, query
+ end
+
+ def logic_factory(left_operand, right_operand, operator)
+ lambda do |collection|
+ left_operand.filter_as_array(collection).send(
+ operator,
+ right_operand.filter_as_array(collection))
+ end
+ end
+
+ def |(other)
+ new_criteria = Criteria.new
+ new_criteria.define_singleton_method(:filter_as_array,
+ logic_factory(self, other, '|'))
+ new_criteria
+ end
+
+ def &(other)
+ new_criteria = Criteria.new
+ new_criteria.define_singleton_method(:filter_as_array,
+ logic_factory(self, other, '&'))
+ new_criteria
+ end
+
+ def !
+ new_criteria = Criteria.new @type, @query
+ new_criteria.define_singleton_method(:filter_as_array, negate_filter!(self))
+ new_criteria
+ end
+
+ def negate_filter!(left_operand)
+ lambda do |collection|
+ collection.to_a - left_operand.filter_as_array(collection)
+ end
+ end
+
+
+ def initialize(type=nil, query=nil)
+ @query, @type = query, type
+ end
+
+ def filter_as_array(collection)
+ collection.select { |song| song.send(@type) == @query }
+ end
+
+ def filter(collection)
+ Collection.new filter_as_array(collection)
+ end
+
+
+end
Изискването за max 1 nesting level не е ли твърде свирепо в някои ситуации? Примерно трудно е да се използват дори неща като tap, а и доста код се повтаря, защото реално е невъзможно да дефинираш динамично метод без поне 2 nesting levels.