A Little Trailblazer Cookbook
Jul 14, 2016, 2:18:33 PMTrailblazer is a high-level Architecture libraries for the Ruby web application (not only for Rails). If you are no familiar with it yet - take a 20 minutes walk through guide...
I want to cover once again those places where our team had issues or misunderstanding. Trailblazer documentation got a lot of improvements recently and keep getting more and more care. Some points from this list are covered by the docs, but in practice not everything was smooth.
Model
Don't try to fit all operations into CRUD. Like this...
    include Model
    model MyModel, :create
you can define an arbitrary model structure by overriding model! method.
It could be custom finder
def model!(params)
    MyModel.find_by_super_magic(params['voila'])
end
or even pass you model externally
def model!(params)
        params[:model_object]
end
Change/add params before validation
When you need to modify param or create a new based on input params. The best place to do it before validation is in setup_params! :
def setup_params!(params)
        super
        params[:port] = port_from_host(params[:host]) if params[:host]
        params
 end
don't forget to add check if params[:host] if your input param is optional.
Or right before validate call in process:
   def process(params)
        params[:port] = port_from_host(params[:host]) if params[:host]
        validate(params) do |f|
             ...
        end
      end
Don't forget SUPER
In methods like setup_params! it is very important to remember to add super call.
def setup_params!(params)
        super
        ...
Or you will have very hard times debugging the side effects.
Operation lifecycle
This part of docs was very helpful for me to understand what is the Operation initialization flow.
::call
├── ::build_operation
│   ├── #initialize
│   │   ├── #setup!
│   │   │   ├── #assign_params!
│   │   │   │   │   ├── #params!
│   │   │   ├── #setup_params!
│   │   │   ├── #build_model!
│   │   │   │   ├── #assign_model!
│   │   │   │   │   ├── #model!
│   │   │   │   ├── #setup_model!
│   ├── #run
│   │   ├── #process
Namespace conflict
Trbrb documentation and book uses such an approach in declaring operation class.
class Message < ActiveRecord::Base
  class Create < Trailblazer::Operation
....
It requires this weird < ActiveRecord::Base declaration. But it is needed only when you have Message ActiveRecord model declared. In such case, if you omit < ActiveRecord::Base in operation it will raise an error that class declaration differ from initial.
It works pretty except that is looks weird, and sometimes it just does not work and complains about wrong inheritance declaration during code reload. To eliminate this issue I provide I rule to add a core namespace
module Core 
  module Message
    class Create < Trailblazer::Operation
A little bit verbose, but clearer as for me.
Make separate files for each Cell and Operation
Just do it :) Use trailblazer-cells to have separate view files.
It will be easier to navigate all these classes when the project is growing!
Inherit multiple contracts
When you have a long Contract - separate it into modules. When you inherit Update operation from Create - it inherits Contract by default.
Imagine we have BasicPost and  AdvancedPost.
Create separate common Create and Update Contracts
module Post::Contract
  module Create
    include Reform::Form::Module
    property :a, default: "Oh, that's A"
    property :b
and forbid possibility to edit property b while Update, for example
module Post::Contract
  module Update
    include Reform::Form::Module
    property :b, writable: false
Then BasicPost::Create::Operation
module Core
  module BasicPost
    class Create < Trailblazer::Operation
...
      contract do
        include ::Post::Contract::Create
      end
and  BasicPost::Update::Operation is
module Core
  module BasicPost
    class Update < Create
...
      contract do
        include ::Post::Contract::Update
      end
What about AdvancedPost that reuses BasicPost and adds it's own fields?
Do not inherit Advanced Create from Basic create - it causes duplication of rules and issues in Update operation. Inherit it from plain Operation class and use Composition.
module Core
  module AdvancedPost
    class Create < Trailblazer::Operation
....
      contract do
        include ::Post::Contract::Create
        property :c, default: 80
and Update
module Core
  module AdvancedPost
    class Update < Create
     ...
      contract do
        include ::Post::Contract::Update
        property :c, default: 80, writeable: false
      end
Yes, it requires a lot of control, but in the end, it works as expected!
Require dependencies
trailblazer_loader gem does a great job by requiring you classes in expected order (btw, you better read about the order  :)), but sometimes with complex Operations inheritance you could face an issue that some Concept class is not loaded before it's "child". 
Do not invent anything, just do explicit require_dependency declaration.
Contract is a Twin
Don't forget that Contract is a Twin. So all the great thigs like a composition, default, nilify etc. could be used. And these docs helps a lot.
Property is not the same in Contract, Cell, and Representable
Despite Operation, Cell and Representable have a very similar syntax of property declaration. They are not the same. And it is a little confusing.
- Operation's Contract propertyis derived from a Twin.
- Cell's propertyis just a direct call delegation to themodel, no Twin tricks are available. Because Cell is not meant to transform or parse input parameters.
- Representable uses it's own Shema handling and you should not try to find parallels with Contract definition.
Use decorator lib like Draper
Twin could serve as a decorator, but for me, it seems a bit overloaded with all that sync functionality (as for read-only decorator). So I use Draper to keep all reusable decorations for a Model.
Simple rule decorates Models only when you really need decoration methods.
If you need decoration methods inside the operation, DO NOT DO this:
def setup!(params)
        super
        @model = @model.decorate
end
You will end up with exceptions in really unexpected places (while operation tries to sync data). Let the operation use Model, and use Decorated model only in places where logic (based on decorator) is performed.
If you need the decorated Model in all views (that is very likely), you can make this trick
class ApplicationController < ActionController::Base
  # ...
  # Here should be all your code from root Controller class
  # ...
  private
  # Override default Trbrb model assignment
  def setup_operation_instance_variables!(operation, options)
    super
    # reassign @model with decorated one (this example is for Draper)
    @model = @model.decorate if !@model.nil? && @model.decorator_class?
  end
end
Kaminari and html_safe
When you add a Kaminari pagination inside a Cell and use generated templates, it will render as escaped HTML text.
Because Kaminari uses a special classes for each tag, I got this solution. Add .to_s.html_safe in places where you render Kaminiri tags.
prev_page_tag.to_s.html_safe
  next_page_tag.to_s.html_safe
  page_tag(page).to_s.html_safe
  ...
