Trailblazer is...

Сложно выговорить.

 

Трудно запомнить.

 

Что это?

 

И зачем?

Сделали стандартный web-проект

+

=

Заказчик попросил убрать колесо

Но с Trailblazer-архитектурой все иначе

Все на своих местах

"ВездеСУЩНОСТЬ"

app
├── concepts
│   ├── comment
│   │   ├── cell
│   │   │   └── new.rb
│   │   ├── operation
│   │   │   ├── create.rb
│   │   │   └── update.rb
│   │   └── view
│   │       ├── new.erb
│   │       └── show.erb

concept - самостоятельная сущность со  своим поведением

Объекты повсюду?

старый Controller, новая Operation

class CommentsController < ApplicationController
  def create
    op = Comment::Create.(params)
    @model = op.model
  end
class Comment < ActiveRecord::Base
  class Create < Trailblazer::Operation
    model Comment, :create

    contract do
      property :body
      property :author_id

      validates :body, presence: true
    end

    def process(params)
      validate(params[:comment]) do
        contract.save
      end
    end
  end
end

Поместив бизнес-логику в операции, можем:

 

- переиспользовать операции;

 

- получить модульную структуру проекта, которую
легче редактировать и масштабировать;

 

- писать простые и понятные тесты на каждую операцию;

 

- поменять framework

Operation (gem Trailblazer)

Тестирование

describe Comment::Create do
  it "persists valid input" do
    op = Comment::Create.(comment: { body: "RoR!", author_id: 1 })

    op.model.persisted?.must_equal true
    op.model.body.must_equal "RoR!"
  end
end

- простые Unit-тесты операций покрывают всю бизнес-логику;

 

- integration, API тесты и тестирование контроллеров упрощаются в разы - можно проверять лишь общее поведение без бизнес-логики.

 

- не нужны фабрики, их заменяют операции, которые одинаково работают как в тестовом режиме, так и в production mode;

Делим UI на фрагменты (gem Cells)

class CommentsController < ApplicationController
  def show
    comment = Comment::Create.present(params)
    render Comment::Cell::New, comment, layout: Application::Layout
  end
module Comment::Cell
  class New < Trailblazer::Cell
    property :body

    def author_name
      model.author.full_name || "Anonymous"
    end
  end
end
%h1 Comment

.row
  = body
.row
  = author_name

Делим UI на фрагменты (gem Cells)

<%= concept('menu/cell/main_panel', 
            nil,
            { current_user: current_user })%>

Парсинг и рендеринг (gem Roar)

 внутри операций

require 'roar/decorator'

class SongRepresenter < Roar::Decorator
  include Roar::JSON

  property :title
end
song = Song.new(title: "The Wall")

SongRepresenter.new(song).to_json
class Create < Trailblazer::Operation
 representer do
   include Roar::JSON
   include Roar::Hypermedia

   property :id
   property :body
   property :author_id

   link(:self) { comment_path(represented.id) }
 end

отдельным классом

Может как передавать параметры в исходном виде, добавлять, переименовывать их, так и записывать прямо в модель

Comment::Create.('{"body": "Solnic rules!"}').json

Репрезентеры

https://uptimeguard.net/

Подключаем к проекту...

Должны пригодиться:

gem 'reform-rails'
gem 'roar-rails', github: 'alaibe/roar-rails'
gem 'roar', github: 'apotonick/roar'
gem 'cells-erb'
gem 'cells-rails'
gem 'trailblazer-cells'
gem 'kaminari-cells'
gem 'trailblazer'
gem 'trailblazer-rails'

http://trailblazer.to

 

"Trailblazer. A new Acrhitecture For Rails"

https://leanpub.com/trailblazer