2. Что представляет из себя
наше приложение
• Полно бизнес логики
• Работа с деньгами клиентов
• Общение со сторонними системами
• Очереди обработки данных (sidekiq)
• Асинхронность
• Поддержка плагинов через Rails engines
• Звездолет
4. Быть или не быть?
• Посмотрим с какими проблемами мы
столкнулись и как с ними боролись
• Какие проблемы не решены
• Попробуем сделать выводы, можно ли
использовать Rails в коммерческих приложениях
6. Ruby modules
• Многие гемы просто не готовы к тому, что мы будем
использовать Ruby модули
• DataGrid - не было возможности изменить шаблоны для
client и admin области
• Draper - для работы декоратора в пространстве имен
приходится писать магические строчки
• InheritedResources - поиск ресурса (AR модельки) в
модуле не был реализован
• Rails - polymorphic routes генерировали неправильные
helper-методы
7. Rails Engines
• Нельзя использовать разные версии gems в ядре и плагине
• В Ruby нет интерфейсов. Нельзя заставить плагины
реализовать все методы, которые нам нужны
• Тестирование Rails Engines в контексте ядра -
болезненный процесс
• rake railties:install:migrations постоянно генерирует
новые файлы
• Установленный Rails Engine может поломать все
приложение. У него есть доступ ко всему
8. Имеем дело со всеми известными
проблемами модульного приложения
9. Как мы преодолеваем
проблемы сейчас
• Pull request’ы в гемы, которые не готовы к
использованию Ruby модулей
• Для плагинов предоставляем миксины
• Общение с плагинами стараемся выстраивать через
самописный Pub-Sub, основанный на
ActiveSupport::Notifications-
• Тестируются плагины вручную-
• Собственный регистратор плагинов
10. Пример Pub-Sub
# Core
Publisher.broadcast_event('user.created', {user_id: 1})
!
# Plugin
Subscriber.subscribe('user.created') do |event|
payload = event.payload
puts payload[:user_id]
end
http://goo.gl/QJxujy
12. Собственный регистратор
плагинов
• Дает простую установку копированием плагина
в нужную папку
• Контролируемый процесс подключения плагина
к системе
• Плагин - Rails engine
13. Преимущества Rails
engines как модулей
• В плагинах можно изменить все. View, Routes,
Controllers, Models, Helpers…
• Быстро работает (нет сторонних вызовов)
14. ActiveRecord
• Избегаем nested_attributes
• Используем FormObjects, ServiceObjects,
QueryObjects, PolicyObjects
• В моделе остается только отображение данных и
самые необходимые валидации, ассоциации
• Избегаем Observers и Callbacks
15. Исключение для
использования callbacks
• Отправка сообщения в очередь для фоновой
обработки
• Не изменяет состояние объектов
16. Observers
• Правила использования не отличаются от
callbacks
• Ведут к более запутанному коду чем callbacks
• Лучше никогда не использовать
18. Возможноcть развязать
узлы в тестах
!
• Отключаем все колбэки в моделях
• Включаем колбэки в тестах в тех местах, где их
вызов необходим
• Не используйте этот подход в новых
приложениях!
19. Возможность оставить
тесты рабочими
class ActiveRecord::Base
cattr_accessor :skip_callbacks-
end
!
class User < ActiveRecord::Base
after_create :send_invitation, unless: :skip_callbacks
def send_invitation
puts 'hello'
end
end
!
ActiveRecord::Base.skip_callbacks = true
User.create # =>
ActiveRecord::Base.skip_callbacks = false
User.create # => 'hello'
20. Слой View
• Проблема с длинными именами helper-методов
• Использование instance переменных (@var) во
view
• Рендеринг таблиц
• fields_for и nested_attributes
• Фарш AngularJS
21. Длинные имена helper-
методов
plugin_exchange_client_account_application_contact_
contact_distribution_group_path(parent.account,
parent.core_application, parent, resource)
!
Всего 141 символ!
22. Решение
# Controllers
class SomeController < ApplicationController
helper_method :submit_path
helper_method :cancel_path
!
private
!
def submit_path
any_size_of_helper_method_you_want_submit_path(
arg1,
arg2,
…
)
end
!
def cancel_path
any_size_of_helper_method_you_want_cancel_path(
arg1,
arg2,
…
)
end
!
end
23. Решение
# Views
!
= form_tag submit_path do
= link_to 'Cancel', cancel_path
24. Преимущества решения
• “Чистый”, читабельный и более надежный код во
view
• HAML вам сважет спасибо
!
PS. Не используйте HAML! Есть более удачный
шаблонизатор - SLIM.
25. Helper-методы вместо @var
• Ошибка рендеринга более адекватная (undefined
method my_helper_method вместо
undefined_method “…” for nil class)
26. Рендеринг таблиц
• DataGrid-
• Сортировка и фильтрация из коробки
• Беспроблемная интеграция с пагинаторами и
полнотектовыми движками (через свои scopes)
• Возможность изменять шаблоны таблицы
• view занимают всего 3 (!!!) строчки кода
• и другое - https://github.com/bogdan/datagrid
27. fields_for и
nested_attributes
• В FormObjects сложно обрабатывать магические
хеши nested_attributes
• В большинстве случаев необходимо
использовать свой primary key для поиска
записи на обновление в базе. А не id, который
зашит в rails хардкором
• Огромные имена переменных ассоциаций
28. fields_for и
nested_attributes
- form_object.locations.each do |location|
= f.fields_for 'locations[]', location, include_id: false,
index: nil do |ff|
# include_id: false - предотвращает генерацию
hidden field c id, т.к. нас это не устраивает
# index: nil - не включать index в название
сгенерированного аттрибута
29. Преимущества данного
подхода
• Имена параметров чистые и понятные
• Возможность передавать на сервер свой primary
ключ для поиска модели на обновление
• Проще обработка в FormObjects
30. Фарш AngularJS
• Т.к. состояние объектов хранится на сервере
необходимо вручную вызывать ng-init
• В каждый input необходимо передать ng-model
атрибут
• Если в ng-init забыт атрибут, который связан с ng-model,
то получим некорректное отображение
объекта на форме
• Что делать при успешном сохранении формы?
Reload страницы? - Теряем flash сообщения
31. Разработка на Rails
быстрая?
• Только для небольших
приложений
• Огромное количество кода
препятствует быстрой
разработке
33. Выводы
• Rails отлично подходит для создания прототипов-
• К модульности и огромным приложениям Rails не готов
• В сложных приложениях приходится отказываться от
многих магических палочек Rails-
• Это ведет к увелечению собственного кода
• Код требует ухода. Знание шаблонов
проектирования, принципов проектирования
классов (SOLID) здесь просто необходимо
34. Что в Ruby хорошо?
• Писать DSL
• Сам язык очень
выразительный
• Код приятно писать и читать
• И на этом все :(
35. Решились писать Enterprise
на Rails?
• Нужен ли Вам Rails? Может, достаточно взять Sinatra + Grape,
если у вас будет SPA (Single Page Application)?
• Тщательно обрабатывайте требования. Выясняйте Use Cases-
• На основании UseCases стройте доменную модель (набор
классов и их взаимодействия).
• Не привязывайтесь к ActiveRecord. ActiveRecord - только для
представления данных!
• Тщательно выбирайте гемы. Подумайте 100500 раз прежде чем
выбрать InheritedResources, например.
• Следуйте принципам SOLID, не нарушайте закон Деметры
36. Литература
1. http://goo.gl/GyqjXg - 7 steps to get started with Clean
Architecture in Ruby
2. http://goo.gl/g0VA1 - 7 Patterns to Refactor Fat ActiveRecord
Models
3. http://goo.gl/lz1fEX - Ruby Midwest 2011 - Keynote: Architecture
the Lost Years by Robert Martin
4. http://goo.gl/MP6L7Z - High-Low Testing
5. http://goo.gl/aI3kdc - Growing Rails Applications in Practice
6. http://goo.gl/9KsmM - Принципы проектирования классов
(S.O.L.I.D.)
37. На правах рекламы
!
Основы Rake
Простые примеры применения
в реальном мире
Сравнение с конкурентом -
Thor