Oops... your message was not sent

Your message has been successfully sent

тематические истории, основанные на опыте компании JetRuby
Rails, Веб-разработка

Рефакторинг как положено. Часть 3 — важнейшие шаги

И снова рефакторинг. Наконец, мы подобрались к самому главному.

Быстрые результаты

Перед началом написания тестов мы поправили стиль кода, но не трогали логику. С этим все более менее ясно. А одной из главных проблем, с которой мы столкнулись, стал код, написанный на Ruby, но без использования Ruby. Поэтому нашим первым шагом будет избавление от таких конструкций.

Поехали. С помощью Rubocop и Rubycritic или codeclimate мы ищем самые явные косяки в коде, относящиеся к логике, которую мы рефакторим. Далее мы переключаемся на поиски мини-костылей. Локальные имплементации «map», «select», «detect» и т.д. После этого — убираем код, логику которого следует реализовывать на уровне базы данных.

->

Тесты проходят, и мы движемся дальше.

Поиск явных дублей в коде

Эту часть работы также можно бы было отнести к быстрым результатам. Однако на данном этапе код еще недостаточно знаком, для того чтобы найти все неявные дубли и ошибки в проектировании. А вот избавиться от явных дублей — самое то. Для этого мы используем Flay gem, который, кстати, входит в состав «rubycritic».

Первые шаги рефакторинга

Рефакторим контроллеры

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

Небольшой чеклист:

  • В контроллере нет бизнес-логики.
  • В контроллере нет управляющих конструкций, отличных от if success? then respond_with_success else respond_with_error end.
  • Контроллер ничего не знает об устройстве данных. Чтобы сделать пользователя активным, нужно вызвать current_user.activate! а не current_user.update(status: ‘active’).
  • У нас нет дублирующейся логики для new, create, edit, update actions. Записи строятся/находятся в before_action.
  • Если в action методах у нас отсутствует возможность проверки выполнения этого экшна — выносим в before_filter.

После того, как мы вынесли из методов контроллера всю логику, в нем все еще остается неправильная структура. Rails контроллеры должны быть REST контроллерами, то есть — работать только с одним ресурсом. Однако мы часто наблюдаем картину, когда разработчики не различают понятия ресурс и модель. И в результате приходится получать на рефакторинг контроллеры в несколько сотен строк.

Давайте рассмотрим реальный пример процесса чекаута:

Этот пример описывает процесс оплаты покупки. Если пользователь не зарегистрирован, он имеет возможность зарегистрироваться или моментально восстановить пароль. Далее юзер подтверждает заказ, выбирает форму оплаты (карта или PayPal) и после зачисления средств видит success экран.

Что же мы видим? Мы видим довольно много методов в одном контроллере. Их код не всегда говорит о том, зачем они нужны, в каком порядке вызываются и т.д. То есть методы вообще ничего не говорят о процессе рефакторинга. За исключением шагов, которые он в себя включает. Кроме того, здесь могут присутствовать  legacy методы. Они знамениты тем, что больше не используются, но вводят программистов в заблуждение. Нам же вполне очевидно, что вспомогательные методы cart и redirect_to paypal не нужны во всех экшенах.

И что мы сделали? С помощью логов Rails сервера составили диаграмму процесса оплаты и следующую структуру:

“select_conf” внезапно оказался не экшеном, а вспомогательным методом. Кроме того, мы добавили корзину в базовый контроллер (она нужна не всем потокам и это не есть идеально). Но так мы можем наблюдать баланс между читаемостью, надежностью кода и потраченным временем.

Контроллер корзины — начало процесса оплаты:

Авторизация и регистрация, если пользователь еще не авторизован:

Собственно, оплата. Если выбрана карта — показываем экран подтверждения. В противном случаем — отправляем платить на PayPal.

Со страницы подтверждения или из PayPal данные попадают на “OrderController”, который проводит данные о заказе и отображает его пользователю.

Если у нас все получилось, отправляем пользователя на метод “show” этого же контроллера. Если нет — на страницу оплаты. И отображаем ошибки.

Как видите, у нас получилось разделить один контроллер на пять маленьких, легко читаемых и, что самое главное — отвечающих только за одну конкретную задачу. Бинго!

Рефакторим представления

С представлениями все довольно просто, поэтому ограничимся чеклистом.

  • Используем haml или slim.
  • Повторяющиеся куски выносим в партиалы.
  • Во вью не должно быть логики. Выносим ее в хелперы.
  • Инстанс переменные контроллера должны быть только непосредственно в текущем представлении. Во все паршиалы и хелперы передаются через параметры. Иногда от инстанс переменных можно уйти в пользу “helper_method” контроллера.

Вывод

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

department
Статью подготовил
Отдел веб-разработки
Профессиональная разработка сайтов и приложений на стороне сервера и клиента. Проектирование дизайна, верстка страниц и техническое обслуживание реализованных проектов.
New Articles