У меня было две образовательные цели при создании Случайного парсера поэзии. (github, rubygems)

1: Практикуйте хороший объектно-ориентированный дизайн, как описано в Практический объектно-ориентированный дизайн в Ruby.

2: Создайте хороший интерфейс командной строки, как это описано Дэйвом Коуплендом в его докладе Создание потрясающих приложений командной строки с помощью Ruby и соответствующей книге Создание потрясающих приложений командной строки на Ruby.

Мой код не соответствует всем (или даже почти всем) принципам хорошего дизайна, описанным в этих книгах. Но я многому научился, пытаясь следовать этим принципам в течение того временного окна, которое я отвел себе на создание проекта.

Уроки выучены

Не используйте генератор драгоценных камней, пока полностью не поймете, что он делает

Когда я начал свой проект, я создал драгоценный камень, используя следующую команду:

bundle gem gem-name

Я нашел команду, описанную в этом RailsCast. Эта команда генератора устанавливает каталог моего проекта, файл gemspec и множество зависимостей в проекте. Эти леса были удобны... пока не стали ими.

В конце моего проекта я хотел сделать гем исполняемым, чтобы пользователь мог запустить его прямо в терминале с помощью команды

random_poetry_scraper

Я часами пытался заставить эту функцию работать. Некоторая магия шаблонов в файле .gemspec помешала мне установить исполняемый файл с помощью этого Добавления исполняемой процедуры. Я до сих пор точно не знаю, что пошло не так, но я надеюсь выяснить больше в будущем.

Разочарованный, я в итоге перестроил всю структуру драгоценного камня с нуля, не используя волшебство строительных лесов и этот учебник в качестве руководства. Процесс был утомительным, но стоящим. Было легко добавить исполняемый функционал, как только я понял все, что происходит в моем проекте. Я также удалил каталоги и зависимости, созданные скаффолдером, которые мне не нужны.

«Ожидайте, что выходные данные каждой программы станут входными данными для другой, пока неизвестной программы» (Путь Unix)

Цитата в заголовке раздела является частью кредо, которое часто называют Путь Unix. это кредо в действие в этой жемчужине.

Первый интерфейс, который выводит стихи и их атрибуты в виде JSON, инициируется с помощью флага —json.

random_poetry_scraper --json -num-poems=5

Второй интерфейс, называемый «читатель удовольствия», позволяет пользователю читать стихи в удобном для человека формате в терминале. Это инициируется флагом удовольствия.

random_poetry_scraper --pleasure -num-poems=5

Я решил, что интерфейс JSON следует «Unix Way», потому что я ожидаю, что выходные данные этого интерфейса станут входными данными для другой программы. Я не следовал правилу интерфейса «увлекательного чтения».

Помимо прочих отличий, это означает, что при вызове с флагом «-удовольствие» гем Random Poetry Scraper пишет удобные для пользователя сообщения stdout, подобные следующим:

1 poem(s) fetched successfully
2 poem(s) fetched successfully
...

Напротив, при вызове с флагом «— json» гем будет записывать только чистый JSON в stdout. Если бы я оставил сообщения X poem(s) fetched successfully в интерфейсе JSON, кому-то, кто хотел бы использовать JSON из stdout, программе пришлось бы тщательно анализировать эти сообщения.

«Зависит от поведения, а не от данных» (Практический объектно-ориентированный дизайн в Ruby, 49)

Один из принципов проектирования,подчеркиваемый снова и снова в POODR, заключается в том, что данные должны быть заключены в методы. Это облегчает изменение ваших программ. Возьмем пример переменной экземпляра, указывающей вес:

class Person
  @weight
end

Если вы завернете экземпляр веса в ридер следующим образом:

class Person
  def weight
    @weight
 end
end

Код сразу становится более пригодным для повторного использования в следующей ситуации. Предположим, что ваша программа будет работать на Марсе. Это означает, что для марсианских пользователей вместо земного веса должен отображаться вес на Марсе. Если вы обернули переменную экземпляра в weightreader и вызвали person.weight по всей программе, вам нужно только настроить наш код в одном месте, чтобы внести изменения.

class Person
  def weight
    @weight * martian_adjustment_factor
  end
end

Однако если бы вы вызвали @weightнепосредственно, вам пришлось бы изменить намного больше кода.

В своем проекте я использовал считыватели для переноса одного типа данных, которого я никогда не ожидал: сообщений об ошибках и документации.

В моем проекте впервые было много таких функций:

Я реорганизовал их, чтобы они выглядели примерно так:

Я думаю, что это более читабельно и гибко.