Исмаил обнови решението на 16.01.2017 01:41 (преди почти 8 години)
+REPOSITORY = 'https://github.com/ialidzhikov/ruby-retrospective-2016'
+
+# Двадесет неща, които научих.
+#
+# 1. На първа задача първоначалният ми подход беше следният - за всяка мерна единица бях създал клас,
+# който се грижи за конвентирането от тази единица до всички останали и спрямо дадената единица извиквах
+# съответния метод на класа. По този начин трябваше да поддържам преобразования от всяка към всяка мерна
+# единица. Благодарение на коментарите на Георги осъзнах, че много по-елегантно и изчистено ще бъде, ако
+# си избера дадена мерна единица за междинна и всеки път преобразувам от изходната в междинната и от
+# междинната в крайната. По този начин се налага да поддържам преобразования от всички мерни единици в
+# една конкретна и от тази конкретна до всички останали. Доста по-четимо и елегантно.
+# 2. В условието на първа задача изрично е казано, че няма да се подават мерни единици различни от 'C', 'F'
+# и 'К', а аз имам добавена логика, която проверява дали подадената единаца е сред трите и в случай, че
+# не е, raise-вам ArgumentError. Сега като погледна мисля, че тези проверки са ненужни в контекста на
+# задача и ги премахвам. Последното правило на Кент Бек за прост и ясен дизайн - "and finally it must be
+# the smallest code that meets all of the above.".
+# 3. В първа задача имам Hash, който пази за всяко вещество инстанция, която има полета за melting_point и
+# boiling_point. Ключовете в Hash-a са string-ове и ги сменям на символи. Причините са, че символите са
+# immutable и не се тревожим за това дали сме променили някой ключ; символите заемам по-малко памет,
+# понеже се интернират; и от не толкова съществено значение, хеширането на string е със сложност O(n), а
+# на символ е с константна сложност.
+# 4. На втора задача имах неминаващ тест, заради това че бях разбрал условието погрешно. Бях писал и до вас
+# така че не е голяма болка. По условие беше написано, че #reshape преобразува текущия хеш в нов с
+# различна структура и аз го бях изтълкувал, че трябва да променя self и ми гърмеше тест, който
+# проверява дали self-a се променя. Оправих си това малко бъгче.
+# 5. В контекста на втора задача разбрах, че #clone и #dup правят плитки (shallow) копия и ако искаш
+# дълбоко копие на обект, трябва сам да си го напишеш.
+# 6. Във втора задача относно метода #reshape на Hash вместо да копирам подадения shape в нов и да му
+# извиквам #fetch_deep, много по-елегантно е да извикам #map, като по този начин избягвам и помощна
+# функция за дълбоко копиране.
+# 7. На втора задача забелязвам, че мога да избегна проверката за Array в #fetch_deep метода на Hash. Мога
+# да дефинирам #fetch_deep на Array и по този начин да се уверя, че и двете структури ще handle-нат
+# метода - duck typing.
+# 8. На трета задача прибягнах до създаване на класове контейнери, които да ми държат съответно аргументите
+# и опциите. Класът за аргументите симулира частично опашка, като той може да бъде заменен от масив,
+# като при parse-ване просто се отделят опциите от аргументите. Аналогично може да бъде заменен и класът
+# контейнер за опциите.
+# 9. На трета задача логиката за това дали дадена опция може да прихване даден аргумент се намираше в
+# класа контейнер, като от гледна точка на обектно-ориентирания дизайн много по-добре е тази логика да
+# стои в самата опция.
+# 10. На трета задача OptionWithParameter наследява от Option и не override-ва метода #execute, който
+# изпълнява подадения блок с runner аргумента. CommandRunner класът решаваше дали на дадена опция да
+# подаде true или съответния параметър, като по-добър подход би бил OptionWithParameter класът да
+# override-не #execute и сам да си parse-не параметъра. От друга старана #execute на Option класа няма
+# да се интересува от подадения му параметър и ще извика блока с true.
+# 11. На трета задача при построяване на help съобщението подходът, който бях предприел беше да си съставя
+# отделните части и накрая да използвам интерполация. В случая с #help се получава доста по-изчистено,
+# когато се конкатенира към дадена променлива.
+# 12. В четвърта задача добавям тестови case-ове за сравненията, понеже има брутални решения, където даден
+# оператор е хард-коднат да връща само определена стойност. Определено трябваше да включа повече expect
+# клаузи във въпросните тестове.
+# 13. В четвърта задача използвах грешно конструкцията let, като бях изнесъл повтарящи се обекти. Това
+# влоши четимостта на тестовете. За да видиш какви обекти използва тестър, трябваше да scroll-неш до
+# началото на файла, където се инициализираха. Основното свойство на unit тестовете е да бъдат възможно
+# най-опростени, за да може някой като погледне веднага да разбере за какво става въпрос.
+# 14. В четвърта задача имах логически дупки. Например тествах дали може да се създаде версия с zerolike
+# string-ове (Version.new, Version.new('') или Version.new('0')). Проблемът се състоеше в това, че в
+# няколко теста по-надолу ползвах абсолютно същите zerolike аргументи, за да тествам различна
+# функционалност. Тестът за това дали може да се създаде версия с zerolike string-ове бяха безсмислени,
+# защото ако те фейлнеха, то със сигурност някой от следващите щеше да фейле.
+# 15. Имах представата, че 1 тест е 1 или няй-много 2, 3 expect-a/assert-a. С четвърта задача разчупих тази
+# своя представа, понеже голяма част от тестовете, които бях писал преди задачката съдържаха най-често
+# 1, 2 expect-a.
+# 16. В контекста на четвърта задача разбрах, че let може да се използва и в describe блок.
+# 17. В пета задача бях използвал class_eval { attr_accessor attribute }, като това е безсмислено - сетвам
+# self-a на блока на self. attr_accessor attribute е достатъчно.
+# 18. В пета задача имплементирал съм генерирането на ново id по такъв начин, че всеки път броя до
+# следващото свободно ID. Просто increment върши чудесна работа, но в примерните тестове се създават
+# модели с id-та и за да няма 2 модела с едно и също id, взимам следващото свободно. Но явно този check
+# тестовете не го покриват.
+# 19. В пета задача #data_store методът се използва с 2 цели - за сетване на ново хранилище и за достъп до
+# вече зададеното. Моят подход към метода беше да примера произволен брой аргументи (*args) и да
+# проверя дали масивът не е празен и спрямо това да върна data_store или да сетна такъв. Една идея
+# по-елегантен подход е методът да приема параметър със стойност по подразбиране (store = nil).
+# 20. The wrong abstraction - беше доста полезно да разбера за това понятие и мисля, че module Store не е
+# пример за такова.