ݺߣ

ݺߣShare a Scribd company logo
Корпоративное приложение на Rails. 
Отчет после года разработки 
Андрей Колешко 
@ka8725
Что представляет из себя 
наше приложение 
• Полно бизнес логики 
• Работа с деньгами клиентов 
• Общение со сторонними системами 
• Очереди обработки данных (sidekiq) 
• Асинхронность 
• Поддержка плагинов через Rails engines 
• Звездолет
Приблизительное состояние кода в 
проекте
Быть или не быть? 
• Посмотрим с какими проблемами мы 
столкнулись и как с ними боролись 
• Какие проблемы не решены 
• Попробуем сделать выводы, можно ли 
использовать Rails в коммерческих приложениях
Модульность 
• Ruby modules 
• Rails engines
Ruby modules 
• Многие гемы просто не готовы к тому, что мы будем 
использовать Ruby модули 
• DataGrid - не было возможности изменить шаблоны для 
client и admin области 
• Draper - для работы декоратора в пространстве имен 
приходится писать магические строчки 
• InheritedResources - поиск ресурса (AR модельки) в 
модуле не был реализован 
• Rails - polymorphic routes генерировали неправильные 
helper-методы
Rails Engines 
• Нельзя использовать разные версии gems в ядре и плагине 
• В Ruby нет интерфейсов. Нельзя заставить плагины 
реализовать все методы, которые нам нужны 
• Тестирование Rails Engines в контексте ядра - 
болезненный процесс 
• rake railties:install:migrations постоянно генерирует 
новые файлы 
• Установленный Rails Engine может поломать все 
приложение. У него есть доступ ко всему
Имеем дело со всеми известными 
проблемами модульного приложения
Как мы преодолеваем 
проблемы сейчас 
• Pull request’ы в гемы, которые не готовы к 
использованию Ruby модулей 
• Для плагинов предоставляем миксины 
• Общение с плагинами стараемся выстраивать через 
самописный Pub-Sub, основанный на 
ActiveSupport::Notifications- 
• Тестируются плагины вручную- 
• Собственный регистратор плагинов
Пример 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
Особенности нашего Pub- 
Sub 
• Однопоточный 
• Нет обратной связи. Сообщения 
однонаправленные
Собственный регистратор 
плагинов 
• Дает простую установку копированием плагина 
в нужную папку 
• Контролируемый процесс подключения плагина 
к системе 
• Плагин - Rails engine
Преимущества Rails 
engines как модулей 
• В плагинах можно изменить все. View, Routes, 
Controllers, Models, Helpers… 
• Быстро работает (нет сторонних вызовов)
ActiveRecord 
• Избегаем nested_attributes 
• Используем FormObjects, ServiceObjects, 
QueryObjects, PolicyObjects 
• В моделе остается только отображение данных и 
самые необходимые валидации, ассоциации 
• Избегаем Observers и Callbacks
Исключение для 
использования callbacks 
• Отправка сообщения в очередь для фоновой 
обработки 
• Не изменяет состояние объектов
Observers 
• Правила использования не отличаются от 
callbacks 
• Ведут к более запутанному коду чем callbacks 
• Лучше никогда не использовать
Завязались окончательно?
Возможноcть развязать 
узлы в тестах 
! 
• Отключаем все колбэки в моделях 
• Включаем колбэки в тестах в тех местах, где их 
вызов необходим 
• Не используйте этот подход в новых 
приложениях!
Возможность оставить 
тесты рабочими 
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'
Слой View 
• Проблема с длинными именами helper-методов 
• Использование instance переменных (@var) во 
view 
• Рендеринг таблиц 
• fields_for и nested_attributes 
• Фарш AngularJS
Длинные имена helper- 
методов 
plugin_exchange_client_account_application_contact_ 
contact_distribution_group_path(parent.account, 
parent.core_application, parent, resource) 
! 
Всего 141 символ!
Решение 
# 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
Решение 
# Views 
! 
= form_tag submit_path do 
= link_to 'Cancel', cancel_path
Преимущества решения 
• “Чистый”, читабельный и более надежный код во 
view 
• HAML вам сважет спасибо 
! 
PS. Не используйте HAML! Есть более удачный 
шаблонизатор - SLIM.
Helper-методы вместо @var 
• Ошибка рендеринга более адекватная (undefined 
method my_helper_method вместо 
undefined_method “…” for nil class)
Рендеринг таблиц 
• DataGrid- 
• Сортировка и фильтрация из коробки 
• Беспроблемная интеграция с пагинаторами и 
полнотектовыми движками (через свои scopes) 
• Возможность изменять шаблоны таблицы 
• view занимают всего 3 (!!!) строчки кода 
• и другое - https://github.com/bogdan/datagrid
fields_for и 
nested_attributes 
• В FormObjects сложно обрабатывать магические 
хеши nested_attributes 
• В большинстве случаев необходимо 
использовать свой primary key для поиска 
записи на обновление в базе. А не id, который 
зашит в rails хардкором 
• Огромные имена переменных ассоциаций
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 в название 
сгенерированного аттрибута
Преимущества данного 
подхода 
• Имена параметров чистые и понятные 
• Возможность передавать на сервер свой primary 
ключ для поиска модели на обновление 
• Проще обработка в FormObjects
Фарш AngularJS 
• Т.к. состояние объектов хранится на сервере 
необходимо вручную вызывать ng-init 
• В каждый input необходимо передать ng-model 
атрибут 
• Если в ng-init забыт атрибут, который связан с ng-model, 
то получим некорректное отображение 
объекта на форме 
• Что делать при успешном сохранении формы? 
Reload страницы? - Теряем flash сообщения
Разработка на Rails 
быстрая? 
• Только для небольших 
приложений 
• Огромное количество кода 
препятствует быстрой 
разработке
Rails way Enterprise way
Выводы 
• Rails отлично подходит для создания прототипов- 
• К модульности и огромным приложениям Rails не готов 
• В сложных приложениях приходится отказываться от 
многих магических палочек Rails- 
• Это ведет к увелечению собственного кода 
• Код требует ухода. Знание шаблонов 
проектирования, принципов проектирования 
классов (SOLID) здесь просто необходимо
Что в Ruby хорошо? 
• Писать DSL 
• Сам язык очень 
выразительный 
• Код приятно писать и читать 
• И на этом все :(
Решились писать Enterprise 
на Rails? 
• Нужен ли Вам Rails? Может, достаточно взять Sinatra + Grape, 
если у вас будет SPA (Single Page Application)? 
• Тщательно обрабатывайте требования. Выясняйте Use Cases- 
• На основании UseCases стройте доменную модель (набор 
классов и их взаимодействия). 
• Не привязывайтесь к ActiveRecord. ActiveRecord - только для 
представления данных! 
• Тщательно выбирайте гемы. Подумайте 100500 раз прежде чем 
выбрать InheritedResources, например. 
• Следуйте принципам SOLID, не нарушайте закон Деметры
Литература 
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.)
На правах рекламы 
! 
Основы Rake 
Простые примеры применения 
в реальном мире 
Сравнение с конкурентом - 
Thor
Вопросы? 
Андрей Колешко 
@ka8725- 
ka8725@gmail.com

More Related Content

Корпоративное приложение на Rails

  • 1. Корпоративное приложение на Rails. Отчет после года разработки Андрей Колешко @ka8725
  • 2. Что представляет из себя наше приложение • Полно бизнес логики • Работа с деньгами клиентов • Общение со сторонними системами • Очереди обработки данных (sidekiq) • Асинхронность • Поддержка плагинов через Rails engines • Звездолет
  • 4. Быть или не быть? • Посмотрим с какими проблемами мы столкнулись и как с ними боролись • Какие проблемы не решены • Попробуем сделать выводы, можно ли использовать Rails в коммерческих приложениях
  • 5. Модульность • Ruby modules • Rails engines
  • 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
  • 11. Особенности нашего Pub- Sub • Однопоточный • Нет обратной связи. Сообщения однонаправленные
  • 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
  • 38. Вопросы? Андрей Колешко @ka8725- ka8725@gmail.com