Генади обнови решението на 30.10.2011 13:53 (преди около 13 години)
+class Song
+ attr_accessor :name, :artist, :genre, :subgenre, :tags
+
+ def initialize(format = nil)
+ return if format.nil?
+ @name, @artist, @genre, @subgenre, @tags = parse format
+ end
+
+ def [](key)
+ send(key) rescue nil
+ end
+
+ private
+
+ def parse(format)
+ [].tap do |result|
+ name, artist, genre, tags = format.split('.').map(&:strip)
+ result << name << artist
+ genre.split(',').map(&:strip).tap do |parsed_genre|
+ genre, subgenre = parsed_genre
+ result << genre << subgenre
+ tags = tags.to_s if tags.nil?
+ tags.split(',').map(&:strip).tap do |tags|
+ tags.concat parsed_genre
+ result << tags.map(&:downcase)
+ end
+ end
+ end
+ end
+end
+
+class Collection
+ attr_reader :songs
+
+ def initialize(catalog, additional_tags = {})
+ @songs = catalog.lines.map { |format| Song.new format }
+ additional_tags.each do |artist, tags|
+ find_by_artist(artist).map { |song| song.tags.concat tags }
+ end
+ end
+
+ def find(criteria)
+ return songs if criteria.empty?
+ result = []
+ criteria.each do |field, query|
+ if result.empty?
+ result.concat send("find_by_#{field}", query)
+ else
+ result &= send("find_by_#{field}", query)
+ end
+ end
+ result
+ end
+
+ %w{name artist genre subgenre}.each do |field|
+ define_method("find_by_#{field}") do |query|
+ songs.select { |song| song[field] == query }
+ end
+ end
+
+ def find_by_tags(query)
+ selected, rejected = [].tap do |me|
+ Array(query).tap do |it|
+ me << it.reject { |tag| tag.end_with? '!' }
+ me << it.map { |tag| tag.chomp '!' } - me.first
+ end
+ end
+
+ songs.select do |song|
+ selected.all? { |tag| song.tags.include? tag } and not \
+ rejected.any? { |tag| song.tags.include? tag }
+ end
+ end
+
+ def find_by_filter(query)
+ songs.select(&query)
+ end
+end
-
send(key) rescue nil
е ужасно. Това ще погълне всякакви грешки. По-добреsend(key) if respond_to? key
. Можеш да имаш изключение в някой от тези методи и няма да го видиш. -
me
иit
са странни имена на променливи. -
not rejected.any?
е още известно катоrejected.none?
. - Обективно погледнато, нямаш нужда от
attr_reader :songs
вCollection
. Можеш в кода просто да ползваш@songs
. - Не мога да разбера защо в
Song#initialize
имашreturn if format.nil?
. Не се ползва никъде. - В
Collection#find
трябваше да направиш единselect
, вместо да създаваш масив, да итерираш друг и да правишconcat
и&=
. Тази логика е доста сложна за проследяване. - Интересно, че си открил
define_method
, но това не е най-удачното ми приложение. -
find_by_tags
е изключително сложен с тези дваtap
-а и сложнотиите които прави вътре. Щях да ти дам бонус точки, ама това ме разубеди. Можеше вместо всичко това да направиш:selected, rejected = Array(query).partition { |tag| tag.end_with? '!' } rejected = rejected.map(&:chop)