Втора задача

Предадени решения

Краен срок:
31.10.2012 17:00
Точки:
6

Срокът за предаване на решения е отминал

Имплементирайте класове за работа с музикална колекция. Те трябва да могат да прочитат текстов файл с определен формат и да предлагат определена функционалност за филтриране.

Формат

Текстовият файл, дефиниращ музикална колекция, е подобен на информацията, която MTV предлагаше през 90те:

Fields of Gold
Sting
Ten Summoner's Tales

Mad About You
Sting
The Soul Cages

Fields of Gold
Eva Cassidy
Live at Blues Alley

Autumn Leaves
Eva Cassidy
Live at Blues Alley

Autumn Leaves
Bill Evans
Portrait in Jazz

Brain of J.F.K
Pearl Jam
Yield

Jeremy
Pearl Jam
Ten

Come Away With Me
Norah Johnes
One

Acknowledgment
John Coltrane
A Love Supreme

Ruby, My Dear
Thelonious Monk
Mysterioso

Всяка песен е блок от три реда. Първият ред е името на песента, вторият ред е изпълнителят, а третият - албумът. Песните са разделени с един празен ред. Приемете, че входът винаги е правилно форматиран и (очевидно) няма символ за нов ред в имената на песни, изпълнители или албуми.

Създаването на колекция трябва да става така:

collection = Collection.parse(text)

Където text е текстов низ със съдържание във формата, описан по-горе (с цел опростена проверка).

Операции с колекция

Всяка колекция трябва да има три метода - artists, albums и names - които да връщат масив от низове, съдържащи съответно имена на изпълнители, албуми или песни. Във върнатия списък не трябва да има повторения. Редът трябва да е същият като подадения в text.

Отделно, колекциите трябва да имплементират Enumerable. Всяка песен от колекцията трябва да е обект и с (поне) три метода - name, artist и album, които връщат съответната информация (като низ). Редът трябва да бъде същият като подадения в text.

Филтриране на колекции

Колекцията трябва да има метод filter, който да приема критерии за филтриране. Възможни критерии са:

Criteria.name('Fields of Gold')         # Име на песен
Criteria.artist('Sting')                # Име на изпълнител
Criteria.album("Ten Summoner's Tales")  # Име на албум

Criteria е някакъв обект/модул/клас, който има съответните три метода. Всеки от тях връща някакъв обект, представляващ критерий, по който да може да се филтрира колекция, подавайки го като аргумент на Collection#filter. Например, ако искате да намерите всички песни на Jeff Buckley, може да го направите така:

collection.filter Criteria.artist('Jeff Buckley')

filter връща подмножество на колекцията. Няма значение от какъв клас е, стига да има същите методи като колекцията.

Отделно, критериите могат да се комбинират с &, |, !, където първото е конюнкция, а второто - дизюнкция. Така може да намерите (1) всички песни на Eva Cassidy и Norah Johnes или (2) всички песни на Sting, които не са Fields of Gold:

collection.filter Criteria.artist('Eva Cassidy') | Criteria.artist('Norah Johnes')
collection.filter Criteria.artist('Sting') & !Criteria.name('Fields of Gold')

Обединяване на подмножества

Колекциите и подмножествата, връщани от filter, трябва да имат допълнително и метод adjoin, който да може да обедини две подмножества. Например, ако искате да вземете всички песни на Eva Cassidy и Norah Johnes, може да го направите по два начина:

collection.filter Criteria.artist('Norah Johnes') | Criteria.artist('Eva Cassidy')
collection.filter(Criteria.artist('Norah Johnes')).adjoin(collection.filter(Criteria.artist('Eva Cassidy')))

При такова обединение на подмножества/колекции, редът на песните в резултата не е дефиниран.

Бележки

  • Имаме следните изисквания към константи и интерфейс:
    • Collection с "класов" метод parse и методи artists, albums, names, filter и adjoin. Последните два връщат нещо, което има същите методи.
    • Criteria с "класови" методи name, artist и album, които връщат обекти, имплементиращи оператори &, | и !. Въпросните обекти могат да бъдат подавани на filter.
    • Колекцията, върната от Collection.parse, да е enumerable и да може да се третира като стандаретн обект от този тип. Това важи и за обектите тип "подмножество на колекция".
    • Елементите (обектите песни), които колекцията подава при обхождане, да имат методи name, artist и album.
  • Обърнете внимание, че:
    • Не ни интересува типът на върнатото от Collection.parse.
    • Не ни интересува дали "колекция" и "подмножество на колекция" са един и същи или два различни класа, стига и двата да отговарят на един и същ интерфейс (да могат да бъдат обхождани).
    • Аналогично, не ни интересува типът на обекта, връщан от Criteria, стига да отговаря на съответните условия.
    • Не ни интересува типът на песните, а само интерфейсът към тях.
    • Свободни сте да вземете всички тези решения както намирате за уместно.
  • Ако ползвате eval или нещо сродно, ще ви взимаме точки.
  • Редът на песните винаги е като този във файла. Изключение прави само резултатът от adjoin.
  • Възникват интересни въпроси за това кога две песни са равни. За простота, приемете че adjoin се прави само между подмножества на една и съща колекция. Възможно е да се прави и между колекция и нейно подмножество. Няма да се прави между подмножества от две различни колекции, обаче. Т.е., не е дефинирано какво прави Collection.parse(text).adjoin(Collection.parse(text)).
  • filter и adjoin връщат нов обект, а не променят стария.

Примерен тест

Можете да намерите примерен тест и инструкции как да го пуснете в хранилището с домашните в GitHub.

Ограничения

Тази задача има следните ограничения:

  • Най-много 80 символа на ред
  • Най-много 4 реда на метод
  • Най-много 1 нива на влагане

Ако искате да проверите дали задачата ви спазва ограниченията, следвайте инструкциите в описанието на хранилището за домашните.

Няма да приемаме решения, които не спазват ограниченията. Изпълнявайте rubocop редовно, докато пишете кода. Ако смятате, че rubocop греши по някакъв начин, пишете ни на fmi@ruby.bg, заедно с прикачен код или линк към такъв като private gist. Ако пуснете кода си публично (например във форумите), ще смятаме това за преписване.