Йордан обнови решението на 31.10.2012 15:59 (преди около 12 години)
+class Song
+ attr_reader :name, :artist, :album
+
+ def initialize(name, artist, album)
+ @name = name
+ @artist = artist
+ @album = album
+ end
+
+ def matches? criteria
+ criteria.songs.any? do |song|
+ song.values_match?(song.name, @name) and
+ song.values_match?(song.artist, @artist) and
+ song.values_match?(song.album, @album)
+ end
+ end
+
+ #The ultimate result of bad "Criteria" conception
+ def values_match?(first_value, second_value)
+ if first_value.empty? or first_value[-1] == "\n"
+ first_value.chomp != second_value
+ else
+ first_value == second_value
+ end
+ end
+
+ def criteria_not
+ @name += "\n" unless @name.empty?
+ @artist += "\n" unless @artist.empty?
+ @album += "\n" unless @album.empty?
+ self
+ end
+
+ def merge songs
+ songs.collect do |song|
+ Song.new(song.name + @name, song.artist + @artist, song.album + @album)
+ end
+ end
+end
+
+class Collection
+ include Enumerable
+ attr_accessor :collection
+
+ def initialize text
+ @collection = text.lines("")
+ @collection = @collection.map do |song|
+ name, album, artist = song.split("\n").map(&:strip)
+ Song.new(name, album, artist)
+ end
+ end
+
+ def self.parse text
+ Collection.new text
+ end
+
+ def each
+ @collection.each { |n| yield n }
+ end
+
+ def names
+ collect { |song| song.name }.uniq
+ end
+
+ def artists
+ collect { |song| song.artist }.uniq
+ end
+
+ def albums
+ collect { |song| song.album }.uniq
+ end
+
+ def filter criteria
+ result = Collection.new ""
+ result.collection = @collection.select { |song| song.matches? criteria }
+ result
+ end
+
+ def adjoin object
+ result = Collection.new ""
+ result.collection = @collection | object.collection
+ result
+ end
+end
+
+class Criteria
+ attr_reader :songs
+
+ def initialize songs
+ @songs = songs
+ end
+
+ def self.name value
+ Criteria.new [Song.new(value, "", "")]
+ end
+
+ def self.artist value
+ Criteria.new [Song.new("", value, "")]
+ end
+
+ def self.album value
+ Criteria.new [Song.new("", "", value)]
+ end
+
+ def &(other)
+ Criteria.new @songs.collect { |song| song.merge other.songs }.flatten
+ end
+
+ def |(other)
+ Criteria.new(@songs + other.songs)
+ end
+
+ #Not working properly in most cases
+ def !
+ Criteria.new @songs.map { |song| song.criteria_not }
+ end
+end