SlideShare a Scribd company logo
1 of 64
Download to read offline
Игорь Александров
jetrockets.pro
github.com/jetrockets
• Пишу на Rails c 2008 года 

• Начал с Rails 2.2

• Пришел в Ruby из SmallTalk

• Последние 5 лет слышу “Ruby/Rails мертв”
Not the Rails Way…
Ruby on Rails разработчики валят с проекта
George Catlin, 1832
after_save…
after_commit on:…
accepts_nested_attributes_for…
validates_presence_of…
Пример…
Приложение –
цепочка сервисов
Запрос => [*] => [*] => Ответ
Приложение –
цепочка функций
class CreateDocument
…
end
class CreateDocument
def call
…
end
end
class CreateDocument
def call(document, params)
…
end
end
class CreateDocument
def call(document, params)
document.attributes = params
document.save!
end
end
class CreateDocument
def call(document, params)
document.attributes = params
if document.valid?
rename_document(document)
end
document.save!
end
end
• Единый интерфейс сервисов

• Частично избавились от колбеков

• Тестирование функциональных объектов – проще

• Композиция сервисов
Что стало лучше?
Проблемы
• Валидации остались в моделях:

document.save(validate: false)
# OR
document.from_crm = true
document.save
• Композиция сервисов получалась не всегда,
получалось дублирование

• Тестирование не стало проще из-за
невозможности управлять зависимостями

• Не получалось сделать композицию объектов
(Domain)
Form Object
Reform
http://trailblazer.to/gems/reform/
class CreateDocument
def call(document, params)
form = CreateDocumentForm.new(document)
if form.validate(params)
document = form.sync
rename_document(document)
end
document.save!
end
end
class CreateDocument
def call(document, params)
form = CreateDocumentForm.new(document)
if form.validate(params)
rename_document(document)
end
document.save!
end
end
class CreateDocument
def initialize(form_class:, rename_document:)
…
end
def call(document, params)
…
end
end
class CreateDocument
attr_reader :form_class
attr_reader :rename_document
def initialize(form_class:, rename_document:)
@form_class = form_class
@rename_document = rename_document
end
def call(document, params)
…
end
end
class CreateDocument
def call(document, params)
form = form_class.new(document)
if form.validate(params)
document = form.sync
rename_document(document)
document.save!
end
document
end
end
# Initialize
command = CreateDocument.new(
form_class: Document::CreateDocumentForm,
rename_document: Document::RenameDocument.new
)
# Use several times
command.call(passport, params[:passport])
command.call(visa, params[:visa])
# создание документа от заёмщика
command = CreateDocument.new(
form_class: Document::CreateDocumentForm,
rename_document: Document::RenameDocument.new
)
# создания документа из CRM
command = CreateDocument.new(
form_class: Crm::Document::CreateDocumentForm.new,
rename_document: Document::RenameDocument.new
)
class CreateDocument
attr_reader :form
attr_reader :rename_document
attr_reader :upload_document_to_perfect_audit
…
end
Увеличение зависимостей
# Document::CreateDocumentForm
form_class
# Document::RenameDocument
rename_document(document)
# Document::PerfectAuditUploader
upload_document_to_perfect_audit(document)
class CreateDocument
attr_reader :form_class
attr_reader :rename_document
attr_reader :upload_document_to_perfect_audit
attr_reader :update_expiration_dates
attr_reader :convert_document_to_pdf
attr_reader :create_affiliations
attr_reader :update_cpl_records
attr_reader :update_application_status
# hundreds of other stuff here
…
end
dry-rb
dry-rb
18 библиотек
Каждая библиотека служит своей цели
• IoC Container

• Позволяет реализовать Dependency inversion
principle

• Thread safe
dry-container
dry-container
# app/containers/global_container.rb
module GlobalContainer
extend Dry::Container::Mixin
namespace('services') do
register('create_document') do
Document::CreateDocument.new(
self[‘forms.create_document_form_class']
)
end
end
namespace('forms') do
register('create_document_form_class') do
Document::DocumentForm
end
end
end
dry-container
# создание документа от заёмщика
command = GlobalContainer[‘services.create_document’]
command.form # Document::CreateDocumentForm
command.class # Document::CreateDocument
# создания документа из CRM
command = GlobalContainer[‘crm.services.create_document’]
command.form # Crm::Document::CreateDocumentForm
command.class # Document::CreateDocument
dry-container
# создание документа от заёмщика
command = GlobalContainer[‘services.create_document’]
command.form # Document::CreateDocumentForm
command.class # Document::CreateDocument
# создания документа из CRM
command = GlobalContainer[‘crm.services.create_document’]
command.form # Crm::Document::CreateDocumentForm
command.class # Document::CreateDocument
dry-monads
• Монада – контейнер, в котором есть результат с
объектом

• Either монада (классическая) – это Result монада в dry-rb

• [паттерн] позволяет делать chaining

• Синтаксический сахар
Maybe
# corporate_action/charts_mapper.rb
class CorporateAction::ChartsMapper
include Dry::Monads::Maybe::Mixin
def fetch_mapping(corporate_action, fund, entry)
Maybe(
# some calculation that may return nil
)
end
end
# fund/calculate_share_price.rb
mapping = mapper.fetch_mapping(action, fund, entry)
mapping.bind do |m|
calculate_price_by_valuation_amount(fund, m))
end.or(issue_price_of_account(account)))
Result
class CreateDocument
def call(document, params)
form = form_class.new(document)
if form.validate(params)
document = form.sync
rename_document(document)
document.save!
end
document
end
end
Result
class CreateDocument
def call(document, params)
form = form_class.new(document)
if form.validate(params)
document = form.sync
rename_document(document)
document.save!
else
return false
end
document
end
end
Result
class CreateDocument
include Dry::Monads::Result::Mixin
def call(document, params)
form = form_class.new(document)
if form.validate(params)
…
document.save!
else
return Failure(form)
end
Success(document)
rescue => e
Failure(e)
end
end
Try
class CreateDocument
include Dry::Monads::Result::Mixin
include Dry::Monads::Try::Mixin
…
def call(document, params)
form = form_class.new(document)
if form.validate(params)
Try do
document.save!
…
document
end.to_either
else
Failure(form)
end
end
end
dry-matcher
• pattern matching

• Синтаксический сахар

• Есть встроенный Result Matcher

• Можно написать свой
dry-matcher
class CreateDocument
include Dry::Matcher.for(:call,
with: Dry::Matcher::ResultMatcher)
…
def call(document, params)
form = form_class.new(document)
if form.validate(params)
Try do
document.save!
…
document
end.to_either
else
Failure(form)
end
end
end
dry-matcher
command = GlobalContainer[‘services.create_document’]
command.call(document, params[:document]) do |m|
m.success do |document|
render json: {
document: DocumentRepresenter.new.call(document)
}
end
m.failure do |form|
render_failure_json(
422, document: form.react_errors_hash
)
end
end
dry-matcher (custom)
class CreateDocument
include Dry::Matcher.for(:call, with: TripleMatcher)
…
def call(document, params)
form = form_class.new(document)
if form.validate(params)
…
document.save!
[:success, document]
else
[:failure, :validation, form]
end
rescue => e
[:failure, e]
end
end
dry-matcher (custom)
command = GlobalContainer[‘services.create_document’]
command.call(document, params[:document]) do |m|
m.success do |document|
…
end
m.failure :validation do |form|
render_failure_json(
422, document: form.react_errors_hash
)
end
m.failure do |e|
render_failure_json(500, document: e.message)
end
end
• Валидации основаны на логике предикатов

• Работают с любыми типами входных данных

• Можно валидировать формы, HTTP параметры, JSON
документы, конфигурацию из YAML

• Расширяемость

• Широко используются в других библиотеках
dry-validation & dry-types
dry-initializer
• param и option

• default values

• type constraints

• private/protected readers
dry-initializer
class CreateDocument
attr_reader :form_class
attr_reader :rename_document
def initialize(form_class:, rename_document:)
@form_class = form_class
@rename_document = rename_document
end
def call(document, params)
…
end
end
dry-initializer
class CreateDocument
extend Dry::Initializer
param :form_class
param :rename_document
def initialize(form_class:, rename_document:)
@form_class = form_class
@rename_document = rename_document
end
def call(document, params)
…
end
end
RSpec
describe Document::CreateDocument do
subject(:command) do
GlobalContainer[
‘services.create_document_command'
]
end
…
end
Что мы тестируем?
RSpec / Stubs
require ‘dry/container/stub'
describe "#call" do
before(:all) do
GlobalContainer.enable_stubs!
end
…
GlobalContainer.stub(
‘support.services.exception_notifier’,
ExceptionRaiser
)
end
Итоги
Плюсы
• тестирование функциональных объектов

• простое расширение логики

• композиция – основа моделирования процессов

• масштабирование и слабая связанность
компонентов приложения
Минусы
• имитация функций объектами

• концептуально множатся сущности (которые не
являются сущностями по факту)

• IoC с доступом по строкам

• можно написать монадический метод и вернуть не
монадическое значение
Спасибо
github.com/igor-alexandrov
facebook.com/igor.alexandrov
twitter.com/igor_alexandrov
Not the Rails Way

More Related Content

Similar to Not the Rails Way

Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?Vasil Remeniuk
 
Scala, SBT & Play! for Rapid Application Development
Scala, SBT & Play! for Rapid Application DevelopmentScala, SBT & Play! for Rapid Application Development
Scala, SBT & Play! for Rapid Application DevelopmentAnton Kirillov
 
ZFConf 2010: What News Zend Framework 2.0 Brings to Us
ZFConf 2010: What News Zend Framework 2.0 Brings to UsZFConf 2010: What News Zend Framework 2.0 Brings to Us
ZFConf 2010: What News Zend Framework 2.0 Brings to UsZFConf Conference
 
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETОпыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETGoSharp
 
Easy authcache 2 кэширование для pro. Родионов Игорь
Easy authcache 2   кэширование для pro. Родионов ИгорьEasy authcache 2   кэширование для pro. Родионов Игорь
Easy authcache 2 кэширование для pro. Родионов ИгорьPVasili
 
Easy authcache 2 кеширование для pro родионов игорь
Easy authcache 2   кеширование для pro родионов игорьEasy authcache 2   кеширование для pro родионов игорь
Easy authcache 2 кеширование для pro родионов игорьdrupalconf
 
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...Magneta AI
 
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
View как чистая функция от состояния базы данных  - Илья Беда, bro.agencyView как чистая функция от состояния базы данных  - Илья Беда, bro.agency
View как чистая функция от состояния базы данных - Илья Беда, bro.agencyit-people
 
Alexander Dymo - IT Jam 2009 - Ruby: Beaty Or The Beast
Alexander Dymo - IT Jam 2009 - Ruby: Beaty Or The BeastAlexander Dymo - IT Jam 2009 - Ruby: Beaty Or The Beast
Alexander Dymo - IT Jam 2009 - Ruby: Beaty Or The BeastAlexander Dymo
 
Корпоративное приложение на Rails
Корпоративное приложение на RailsКорпоративное приложение на Rails
Корпоративное приложение на RailsAndrei Kaleshka
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...pgdayrussia
 
functional patterns - dotnetconf'11
functional patterns - dotnetconf'11functional patterns - dotnetconf'11
functional patterns - dotnetconf'110xffAA
 
Теории и практики функционального программирования.
Теории и практики функционального программирования.Теории и практики функционального программирования.
Теории и практики функционального программирования.Dev2Dev
 
Командная разработка “толстых клиентов”
Командная разработка “толстых клиентов”Командная разработка “толстых клиентов”
Командная разработка “толстых клиентов”Open-IT
 
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo
 
Adymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlAdymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlOleksandr Petrov
 

Similar to Not the Rails Way (20)

Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?
 
Scala, SBT & Play! for Rapid Application Development
Scala, SBT & Play! for Rapid Application DevelopmentScala, SBT & Play! for Rapid Application Development
Scala, SBT & Play! for Rapid Application Development
 
ZFConf 2010: What News Zend Framework 2.0 Brings to Us
ZFConf 2010: What News Zend Framework 2.0 Brings to UsZFConf 2010: What News Zend Framework 2.0 Brings to Us
ZFConf 2010: What News Zend Framework 2.0 Brings to Us
 
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETОпыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
 
Easy authcache 2 кэширование для pro. Родионов Игорь
Easy authcache 2   кэширование для pro. Родионов ИгорьEasy authcache 2   кэширование для pro. Родионов Игорь
Easy authcache 2 кэширование для pro. Родионов Игорь
 
UWDC 2013, Yii2
UWDC 2013, Yii2UWDC 2013, Yii2
UWDC 2013, Yii2
 
Easy authcache 2 кеширование для pro родионов игорь
Easy authcache 2   кеширование для pro родионов игорьEasy authcache 2   кеширование для pro родионов игорь
Easy authcache 2 кеширование для pro родионов игорь
 
Clojure #2 (2014)
Clojure #2 (2014)Clojure #2 (2014)
Clojure #2 (2014)
 
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
 
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
View как чистая функция от состояния базы данных  - Илья Беда, bro.agencyView как чистая функция от состояния базы данных  - Илья Беда, bro.agency
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
 
Erlang tasty & useful stuff
Erlang tasty & useful stuffErlang tasty & useful stuff
Erlang tasty & useful stuff
 
Alexander Dymo - IT Jam 2009 - Ruby: Beaty Or The Beast
Alexander Dymo - IT Jam 2009 - Ruby: Beaty Or The BeastAlexander Dymo - IT Jam 2009 - Ruby: Beaty Or The Beast
Alexander Dymo - IT Jam 2009 - Ruby: Beaty Or The Beast
 
Корпоративное приложение на Rails
Корпоративное приложение на RailsКорпоративное приложение на Rails
Корпоративное приложение на Rails
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
 
functional patterns - dotnetconf'11
functional patterns - dotnetconf'11functional patterns - dotnetconf'11
functional patterns - dotnetconf'11
 
Теории и практики функционального программирования.
Теории и практики функционального программирования.Теории и практики функционального программирования.
Теории и практики функционального программирования.
 
Командная разработка “толстых клиентов”
Командная разработка “толстых клиентов”Командная разработка “толстых клиентов”
Командная разработка “толстых клиентов”
 
Yserver
YserverYserver
Yserver
 
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
 
Adymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlAdymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher Sql
 

Not the Rails Way