Video
Jump right into our screencast series TRAILBLAZER TALES to learn how we refactor a messy Rails controller action to a Trailblazer operation.
We're adding new episodes every other week on our channel! Feel free to ping us if you want to explore specific topics.
Structure
Finally know where to put your code
Trailblazer extends the basic MVC pattern of Rails with new abstractions. We call that a high-level architecture. Rock-solid conventions that go far beyond database table naming or route paths let you focus on your application code, minimize bugs and improve the maintainability across large teams.
The pattern
Let Us Take Care of the Flow Control!
Each function of your application is encapsulated into an activity. This activity structures and orchestrates the necessary steps using a modern workflow approach. Depending on the complexity of your function, a step can range from an ad-hoc Ruby method to another nested activity entirely.
Lifecycles and long-running processes can be implemented by chaining activities in workflows.
Features
Framework agnostic
The Trailblazer gems work with any Ruby framework. We provide glue code for Rails and Hanami, but there are TRB-powered apps in Roda, Grape, Sinatra and many more out there.
Legacy ready
You can start using Trailblazer in existing, massive applications today. Refactorings can be applied step-wise, legacy code can be minimized as you go. Remember: Rome wasn’t build in one day, either.
LTS: LONG-TERM-SUPPORT
Trailblazer is in use in thousands of production applications. That’s why we promise: Trailblazer 2.1 is long-term supported. Easy upgrades and stable internal APIs allow us to innovate and keep you stable.
Build to Refactor
Our patterns are developed to be used in highly complex, existing, messy legacy applications. Trailblazer is designed to refactor old code - you do not have to rewrite the entire system to get a better architecture.
Test first
By restructuring business code, application behavior can be tested more efficiently with more unit and less integration tests. Trailblazer engineers do enjoy the simplicity of testing and the speedup of the test suites.
It’s real!
Trailblazer is in use in thousands of production applications. Our patterns have evolved over a decade of engineering, our gems are mature and battle-tested. And: we will never stop innovating.
Our tools
A new developer experience
Newly added developer tools like tracing or step-debugging improve your team’s developer experience. Our new tools reduce frustration to a minimum and will blow your mind - in a good way.
Our new visual editor allows modelling complex long-running processes or visualizes existing activities.
Code
Want some code?
CONTROLLER They end up as lean HTTP endpoints. No business logic is to be found in the controller, they instantly delegate to their respective operation.
Oh, and did we say there won’t be controller tests anymore? That’s right. Only unit and integration tests.
class SongsController < ApplicationController
def create
run Song::Create do |ctx|
redirect_to songs_path(ctx[:model])
end
end
end
MODEL Models contain associations, scopes and finders. Only persistence logic, no callbacks, no validations, no business logic here.
Instead, business code and callbacks are moved to operations.
class Song < ActiveRecord::Base
has_many :albums
belongs_to :composer
end
The OPERATION is the heart of the Trailblazer architecture. It orchestrates validations, policies, models, callback and business logic by leveraging a functional pipeline with built-in error handling.
Designed to be a stateless object, the operation passes around one mutable options hash and makes heavy use of Ruby keyword arguments - if you want it.
class Song::Create < Trailblazer::Operation
step Model( Song, :new )
step Policy::Pundit( Application::Policy, :create? )
step Contract::Build( constant: Song::Contract::Create )
step Contract::Validate()
step Contract::Persist()
fail Notifier::DBError
pass :update_song_count!
def update_song_count!(ctx, current_user:, **)
current_user.increment_song_counter
end
end
Any number of POLICYs can be used in an operation to grant or deny access to functionality.
Also, use your choice of authorization framework.
class Application::Policy < Pundit::Policy
def create?
user.can_create?(model)
end
end
Validations are implemented with CONTRACT.
Trailblazer supports Reform and Dry::Schema
validations in any number.
class Song::Contract::Create < Reform::Form
property :title
property :length
validates :title, presence: true
end
And the best there’s only one way to run an operation.
Any dependency, such as the current_user
, must be injected from the outside.
The concept of global state does not exist in Trailblazer, which leads to simplified, mock-free testability and concurrent-ready code.
Song::Create.(
{ title: "Roxanne", length: 300 }, # params
current_user: current_user # dependencies
)
Clumsy, slow controller tests are history. Now that all your business logic is controlled by the operation, SIMPLE UNIT TESTS can test any edge-case scenario or avoid regressions.
Trailblazer’s encapsulation makes a programmer’s life better.
describe Song::Create do
it "prohibits empty params" do
result = Song::Create.({})
expect(result).to be_failure
expect(result[:model]).to be_new
end
end
new book
A BEGINNER'S GUIDE!
We heard your cries and finally launched a new tutorial ebook series. Part 1, a beginner's guide to Trailblazer, is published!
Building an authentication library similar to Devise we discuss everything from software design basics to tools and library functions of Trailblazer 2.1. Debugging, testing, and, refactoring comes included for free!
Subscribe to our newsletter!
We send you news about Trailblazer's components, posts, events and rants straight to your inbox, every 2-3 months.