Идея этого проекта пришла мне в голову однажды, когда я просматривал Netflix Update. Обновление Netflix содержит массу полезных данных о том, какие фильмы приходят и уходят из Netflix. Однако они дают его вам в виде статического списка титулов и лет.

Чтобы узнать больше, мне пришлось зайти на IMDB и самому найти название, используя целых 3 или 4 клика, а также разнообразный набор текста. Я думаю, мы все можем согласиться с тем, что это неприемлемый объем работы.

Поэтому я решил сделать это проще. Что на Netflix выводит список фильмов, которые выйдут или выйдут на Netflix, и все, что вам нужно сделать, это ввести номер фильма, о котором вы хотите узнать больше. Затем What’s On Netflix извлекает информацию непосредственно из IMDB — не нужно нажимать!

Подготовка

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

Поэтому я решил провести небольшое исследование и получить некоторое представление о том, как другие люди подходят к подобным проектам. Видеопошаговое руководство Ави очень помогло, как и записи в блогах других студентов Learn об их жемчужинах CLI.

Постепенно я составил план атаки.

Моим первоначальным побуждением было начать с написания парсера, а затем создать интерфейс и логику CLI, как только я понял, как на самом деле получить данные, которые они будут отображать. Метод Ави был совсем другим: он начал с того, что заглушил пользовательский интерфейс, используя жестко закодированные данные, точно определив, как пользователь будет взаимодействовать с приложением.

Как только я подумал об этом, этот подход имел большой смысл. Создание пользовательского интерфейса, даже простого CLI, требует экспериментов — если вы уже привязаны к определенному способу ведения дел, потому что сначала создали парсер, у вас не будет такой гибкости при построении CLI, и вы вы с большей вероятностью окажетесь с приложением, которое не особенно удобно для пользователя.

Итак, как только я понял эти первые несколько шагов, я запустил gem bundle whats-on-netflix и приступил к работе.

Bundler отлично подходит для создания основных файлов гема. Я по-прежнему возился с зависимостями файлов, но гораздо меньше, чем если бы я написал gemspec, исполняемый файл, spechelper и другие файлы с нуля.

Кроме того, в комплекте с драгоценными камнями поставляется интересная маленькая функция, называемая консолью, которая запускает сеанс IRB, в который уже загружен весь код из вашего драгоценного камня. Это оказалось невероятно полезным в процессе разработки, так как я мог проводить действительно эффективное ручное тестирование, вызывая отдельные методы с разными аргументами.

Логика интерфейса командной строки

Построение CLI было первым большим препятствием, которое нужно было преодолеть, не в последнюю очередь потому, что оно заставило меня задуматься о том, как я собираюсь одновременно структурировать остальную часть моего кода. Когда объект CLI получит свои данные? Как он будет общаться с другими объектами?

Но сначала был большой вопрос для любого пользовательского интерфейса — как реагировать на неверный ввод? Люди постоянно опечатываются. Они нажимают не на ту кнопку, они нажимают ввод раньше, чем хотели. В хорошем пользовательском интерфейсе есть механизмы, предотвращающие сбой программы из-за этого.

Ответ казался простым: цикл while, который воспроизводится до тех пор, пока ввод пользователя не станет равным «выходу», поэтому программа не закроется, пока ей не прикажут.

На практике это было непросто. Был проведен опрометчивый эксперимент с двумя разными переменными экземпляра для хранения пользовательского ввода, обе из которых должны были равняться «выходу» перед закрытием программы. Я провел много времени, застряв в бесконечных циклах и лихорадочно пытаясь выбраться из них.

Я слишком много думал об этом. Структура, к которой я пришел, была почти обманчиво проста:

@input = gets.strip 
while @input != "exit" 
  (output list view based on user input)    
  while @input != "exit" && @input != "back" 
    (output detail view based on user input) 
  end 
end

В этот момент я работал с жестко запрограммированными данными; как только я разобрался с логикой CLI, пришло время абстрагировать эти жестко запрограммированные значения во что-то более динамичное.

Создание класса ComingSoon

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

Однако у меня была проблема. У меня было два разных типа фильмов, о которых я собирал данные, они использовали очень похожий код, и этот код должен был храниться строго отдельно. Если фильмы, которые скоро уходят, появлялись в списке «Скоро в продаже», драгоценный камень был менее чем бесполезен.

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

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

После этого все было довольно просто. Я занимался этим типом кодирования в течение нескольких недель, пока продвигался по разделу OO Ruby в Learn: инициализировать новый объект, присвоить ему данные из массива или хэша, добавить его в переменную класса @@all.

Вскоре пришло время написать мой парсер.

Очистка

Как только я запустил класс Scraper, я сразу же столкнулся с проблемой. На сайте, который я использовал для составления списка фильмов, поступающих на Netflix и уходящих с него, не использовалась согласованная структура URL-адресов.

Разница между фильмы-скоро-в-сентябре, скоро-выход-в-августе или скоро-июль-скоро может показаться не такой уж большой проблемой для человека, но для парсера это бардак. У меня не было возможности автоматически определять URL каждый месяц. Поэтому я переключился на другой источник: whats-on-netflix.com. Они используют хорошие согласованные URL-адреса, поэтому все, что мне нужно было сделать, это выяснить, как обновить месяц и год.

Получение информации с IMDB было последней и самой сложной частью головоломки. До сих пор я парсил только статические сайты — для этого мне нужно было научиться отправлять запрос на поиск в IMDB, выбирать из списка результатов, чтобы найти страницу с фильмом, а затем извлекать информацию с этой страницы в базу данных. формат, который мог бы использовать мой класс ComingSoon.

К счастью для меня, я не изобретал велосипед. Когда я погуглил в поисках примеров того, что мне нужно сделать, я нашел yayimdbs, драгоценный камень скребка IMDB, который использовал Nokogiri. Конечно, я писал свой собственный парсер, но было очень полезно увидеть пример того, как код может сочетаться друг с другом.

Последние штрихи

Когда Scraper был построен и очищен, пришло время собирать его по частям. Я передал данные из парсера в класс ComingSoon, который получил экземпляры со всеми соответствующими данными, прикрепленными к ним, и отправил пакеты в класс CLI, который вывел данные на терминал.

Я создал класс LeavingSoon, почти отражающий класс ComingSoon. Затем, когда я заметил, сколько общего кода они используют, я создал родительский класс List, чтобы они оба могли наследовать общий код.

Я побежал ruby bin/whats-on-netflix и затаил дыхание. И… появилось сообщение об ошибке. Я забыл изменить имена одной из переменных класса в LeavingSoon.

Ну что ж. Я исправил это и попробовал снова. На этот раз получилось!

Конечно, тут и там еще были ошибки, которые нужно было раздавить. Но это сработало, и через удивительно короткое время я отправил свой код в RubyGems, чтобы мир мог насладиться им.

Вы даже можете попробовать это сами, запустив gem install whats-on-netflix.