Оглавление
И снова рефакторинг. Наконец, мы подобрались к самому главному.
Быстрые результаты
Перед началом написания тестов мы поправили стиль кода, но не трогали логику. С этим все более менее ясно. А одной из главных проблем, с которой мы столкнулись, стал код, написанный на 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” контроллера.
Вывод
Главный посыл этой статьи заключается в том, что вам следует найти баланс между качеством кода и временем, которое вы тратите на задачу. Рефакторинг — это процесс, который зависит от последовательности выполняемых действий. Упустили всего одну деталь, и результат может оказаться плачевным. Ключом же к успеху является грамотный план и список контрольных точек. В качестве примера вы можете использовать наши статьи. В следующей публикации мы поговорим о рефакторинге моделей. Она обязательно поможет вам сделать код еще чище. Не переключайтесь!