<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nick Sutterer</title>
    <description>The latest articles on Forem by Nick Sutterer (@apotonick).</description>
    <link>https://forem.com/apotonick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F446477%2F7d0f0aa7-bd84-4ae5-87a9-d8e402574b2c.jpeg</url>
      <title>Forem: Nick Sutterer</title>
      <link>https://forem.com/apotonick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/apotonick"/>
    <language>en</language>
    <item>
      <title>Unit tests in Trailblazer: less code, more coverage.</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Mon, 10 Feb 2025 09:16:40 +0000</pubDate>
      <link>https://forem.com/trailblazer/unit-tests-in-trailblazer-less-code-more-coverage-4oig</link>
      <guid>https://forem.com/trailblazer/unit-tests-in-trailblazer-less-code-more-coverage-4oig</guid>
      <description>&lt;p&gt;&lt;em&gt;by Nick Sutterer &lt;a class="mentioned-user" href="https://dev.to/apotonick"&gt;@apotonick&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Writing and maintaining automated tests for code is the most hated part for every developer. You might find yourself nodding to this as you're reading.&lt;/p&gt;

&lt;p&gt;It's not only the pain to set up the environment needed for testing a specific case, but also the amount of &lt;em&gt;actually&lt;/em&gt; written lines of code to make sure you're covering "everything" that could be affected by the new chunk of code you introduced.&lt;/p&gt;

&lt;p&gt;In this post, I want to focus on the second part of this bold statement and quickly show you how the just released &lt;code&gt;trailblazer-test&lt;/code&gt; gem allows writing very concise unit tests for Trailblazer operations. A lot of work has been put into it to make covering edge cases as simple as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's that "operation"?
&lt;/h2&gt;

&lt;p&gt;Trailblazer provides a service object called &lt;em&gt;operation&lt;/em&gt;. It's the place where you put the business logic for a particular use case you are working on. With it's very simple DSL, logic can be organized in chunks and executed step-wise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Memo::Operation&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Create&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:check_data&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:validate&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:save&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:model&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:memo&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In short, when invoking the &lt;code&gt;Memo::Operation::Create&lt;/code&gt; operation, the three exemplary steps will be executed in the order you defined them. If a step fails (by returning false), the remaining steps are skipped and the operation terminates, indicating a &lt;em&gt;failure&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you're keen to learn more about operations and the internal railway model, check our &lt;a href="https://trailblazer.to/2.1/docs/operation/" rel="noopener noreferrer"&gt;extensive docs&lt;/a&gt; or, much better, simply watch a &lt;a href="https://www.youtube.com/watch?v=9elpobV4HSw" rel="noopener noreferrer"&gt;5 minute video&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's good practice to have full coverage of your operations as this is the place (in a Trailblazer-driven app) where the business code lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minitest or RSpec?
&lt;/h2&gt;

&lt;p&gt;In the examples used to illustrate the new gem, we're using &lt;code&gt;Minitest&lt;/code&gt;, as we think it's much more readable than RSpec. RSpec on the other hand is providing a great toolset but, whatsoever, is putting too much effort into a test &lt;em&gt;DSL&lt;/em&gt; that's extremely verbose.&lt;/p&gt;

&lt;p&gt;As a matter of fact, this is a matter of taste - no holy wars here: we also have RSpec support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asserting success
&lt;/h2&gt;

&lt;p&gt;Now, to show you a very simple test case, let's create a test file, configure it, and run the &lt;code&gt;Create&lt;/code&gt; operation with a particular input to assert that it terminates successfully.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/operation/memo_test.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoOperationTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Minitest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Spec&lt;/span&gt;
  &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# install our helpers.&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"passes with valid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;memo: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="s2"&gt;"Stock up beer"&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;

    &lt;span class="n"&gt;assert_pass&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;Test.module!&lt;/code&gt; you include the &lt;code&gt;#assert_pass&lt;/code&gt; assertion into the test class. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;#assert_pass&lt;/code&gt;, in its purest form, takes the operation constant, and an input hash. Internally, the assertion now runs the operation with the specified input and then tests if the outcome was successful. This roughly translates to the following snippet, something I've seen throughout many TRB projects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"passes with valid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is nothing wrong with doing the above manually, but our assertions bring a (hopefully!) much better developer experience that we're about to discover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging? You're welcome!
&lt;/h2&gt;

&lt;p&gt;A typical issue for developers when writing or changing tests is that an operation supposed to be passing actually &lt;em&gt;fails&lt;/em&gt;. Most of the times, this is due to validation errors. Given that you're using a &lt;a href="https://trailblazer.to/2.1/docs/macro/#macro-contract" rel="noopener noreferrer"&gt;contract with an errors object&lt;/a&gt;, a failing &lt;code&gt;#assert_pass&lt;/code&gt; will automatically print out the validation errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rs6h3p7n01iec848cgj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rs6h3p7n01iec848cgj.png" alt="Image description" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within a second, you know that your input passed to the operation is not satisfying the validations. If that is not enough, you can simply add a question mark to the assertion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"passes with valid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;assert_pass?&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you the highly popular &lt;a href="https://trailblazer.to/2.1/docs/trailblazer/#trailblazer-developer-wtf-" rel="noopener noreferrer"&gt;&lt;code&gt;#wtf?&lt;/code&gt; trace&lt;/a&gt; on the console&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd9vxkp1r1vxdfiiyo2cq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd9vxkp1r1vxdfiiyo2cq.png" alt="Image description" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those two incredibly helpful features for debugging have been suggested by several TRB users over the years, as both checking the contract errors as well as turning on tracing (&lt;code&gt;#wtf?&lt;/code&gt;) are the first things immediately done manually by many developers when hitting an issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the model
&lt;/h2&gt;

&lt;p&gt;While checking if an operation ran successfully is a great thing to do, bringing joy and happiness to the team, the product managers, and the clients, a good test needs to do a bit more.&lt;/p&gt;

&lt;p&gt;In most cases, an operation produces or alters a model, which is usually found under &lt;code&gt;ctx[:model]&lt;/code&gt;. After running, you may want to check if model attributes match your high expectations.&lt;/p&gt;

&lt;p&gt;One way would be to use the block style and do the testing yourself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"passes with valid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;assert_pass&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:model&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Stock up beer"&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The block simply yields the result objectand it's up to you what's done inside.&lt;/p&gt;

&lt;p&gt;Alternatively, you can use the built-in attributes test of &lt;code&gt;#assert_pass&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"passes with valid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;assert_pass&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;content:    &lt;/span&gt;&lt;span class="s2"&gt;"Stock up beer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;persisted?: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;id:         &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asserted&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;asserted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your new best friend &lt;code&gt;#assert_pass&lt;/code&gt; takes keywords as its third argument. Those are automatically matched against &lt;code&gt;result[:model]&lt;/code&gt;. As you can see for &lt;code&gt;:id&lt;/code&gt;, even dynamic assertions are possible.&lt;/p&gt;

&lt;p&gt;The combination of the block style and the built-in model assertions provides a rich interface for testing any successful outcome of your operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  When things go wrong
&lt;/h2&gt;

&lt;p&gt;Testing the scenarios where an operation passes is a wonderful thing to do. However, probably even more important is testing when things don't play and operations are supposed to actually &lt;em&gt;fail&lt;/em&gt;. To test a failing operation, we got &lt;code&gt;#assert_fail&lt;/code&gt; - you already guessed that method name, right?&lt;/p&gt;

&lt;p&gt;In many cases, an operation will fail if its validations aren't met. You can simply check if the operation terminated on the &lt;code&gt;failure&lt;/code&gt; terminus by using the new assertion with one argument, only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"fails with invalid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;invalid_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;memo: &lt;/span&gt;&lt;span class="p"&gt;{}}}&lt;/span&gt;

  &lt;span class="n"&gt;assert_fail&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invalid_input&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In rare cases, this might be a sufficient test, but most of the times you want to assert errors more detailed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing error messages
&lt;/h2&gt;

&lt;p&gt;Given that you're using a [contract in the operation], you can ask &lt;code&gt;#assert_fail&lt;/code&gt; to check for specific validation error messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"fails with invalid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;assert_fail&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invalid_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# erroring fields.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assertion will now check if the internal contract errors object contains the erroring fields you provided, resulting in a manual test that could look like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"fails with invalid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"contract.default"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To write an even stricter test, you can provide the error messages as an additional constraint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"fails with invalid input"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;assert_fail&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invalid_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"must be filled"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"must be filled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"size cannot be less than 8"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When expected error messages do not match the actual ones, the assertion automatically shows you the latter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqckig89iyd7eubp17rze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqckig89iyd7eubp17rze.png" alt="Image description" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, optimizing your experience and shortcutting ways to help you debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about extendability?
&lt;/h2&gt;

&lt;p&gt;Both assertions shipped with &lt;code&gt;trailblazer-test&lt;/code&gt; provide the block syntax and return the result, in case you need to add more test code.&lt;/p&gt;

&lt;p&gt;Also, keep in mind that the assertions described here are what &lt;em&gt;we&lt;/em&gt; needed to minimize time, code and brain when writing tests. Feel free to &lt;a href="https://trailblazer.zulipchat.com" rel="noopener noreferrer"&gt;ping us&lt;/a&gt; for discussing any extensions of the gem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Suite: Minimizing code
&lt;/h2&gt;

&lt;p&gt;The assertions described so far are designed to take away pain in your testing, but they require you to repeat arguments over and over again. The &lt;a href="https://trailblazer.to/2.1/docs/test/index.html#testing-with-trailblazer-minitest-suite" rel="noopener noreferrer"&gt;"Suite" mode&lt;/a&gt; targets defaulting, so the written code is even less.&lt;/p&gt;

&lt;p&gt;Imagine you're testing our &lt;code&gt;Create&lt;/code&gt; operation and you want to make sure that all validations are actually working, each one in a separate test case. Here's how that could look using the suite feature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/memo/operation_test.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoOperationTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Minitest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Spec&lt;/span&gt;
  &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;suite: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"Create"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# insert defaulting here, see below...&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"{content} works"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;assert_pass&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="s2"&gt;"chill beer"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="s2"&gt;"chill beer"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"{tag_list} is converted to array"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;assert_pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;tag_list: &lt;/span&gt;&lt;span class="s2"&gt;"fridge,todo"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;      &lt;span class="c1"&gt;# input&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;tag_list: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fridge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;  &lt;span class="c1"&gt;# model value.&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In suite mode, assertion arguments such as operation, the incoming ctx and expected attributes on the model can be set (and overwritten!) on the class and &lt;code&gt;describe&lt;/code&gt; level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defaulting over verbosity
&lt;/h2&gt;

&lt;p&gt;You can default arguments by simply defining special-named &lt;code&gt;let()&lt;/code&gt; blocks on any level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/memo/operation_test.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoOperationTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Minitest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Spec&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"Create"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:default_ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="ss"&gt;memo: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;# Note the {:memo} key here!&lt;/span&gt;
            &lt;span class="ss"&gt;title:   &lt;/span&gt;&lt;span class="s2"&gt;"Todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="s2"&gt;"Stock up beer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:expected_attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of having to repeat those values, the suite-enabled assertions will use and accordingly merge arguments for you. A desired side-effect is that &lt;code&gt;#assert_pass&lt;/code&gt; always checks &lt;em&gt;all&lt;/em&gt; attributes on the model as it merges &lt;code&gt;expected_attributes&lt;/code&gt; with the second hash you provided.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://trailblazer.to/2.1/docs/test/index.html#testing-with-trailblazer-minitest-suite" rel="noopener noreferrer"&gt;Check the docs&lt;/a&gt; to dive into this simple yet helpful feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop mocking me!
&lt;/h2&gt;

&lt;p&gt;While it's usually good practice to test the entire stack of logic, meaning your tests also cover complex system parts like external services, sometimes it's necessary to stub a component.&lt;/p&gt;

&lt;p&gt;Replacing a particular step can easily be done using &lt;code&gt;#mock_step&lt;/code&gt;. You are correct when objecting that this method should be named &lt;code&gt;#stub_step&lt;/code&gt;, but that's too close to &lt;code&gt;dub_step&lt;/code&gt; and we haven't added an alias, yet. Note that a "step" could be an entire, nested operation using &lt;code&gt;Subprocess()&lt;/code&gt;, anything modeled as a step can be stubbed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"runs fine"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;stubbed_create&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:save&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**|&lt;/span&gt;
    &lt;span class="c1"&gt;# new logic for {save}.&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:saved&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;assert_pass&lt;/span&gt; &lt;span class="n"&gt;stubbed_create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;:path&lt;/code&gt; option allows targeting either a first-level step sitting directly in &lt;code&gt;Create&lt;/code&gt;, or a deeply nested step somewhere 6 levels down in your nested operation graph. &lt;a href="https://trailblazer.to/2.1/docs/test/index.html#testing-with-trailblazer-minitest-mock_step" rel="noopener noreferrer"&gt;Check the docs&lt;/a&gt; for some more detailed examples.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;#mock_step&lt;/code&gt; helper returns a new operation class which can then be passed to the assertions, or even returned from &lt;code&gt;let(:operation)&lt;/code&gt; if using suite mode.&lt;/p&gt;

&lt;p&gt;Maybe this post is a good place to mention that the entire stubbing logic is simply using the &lt;a href="https://trailblazer.to/2.1/docs/operation/index.html#operation-step-dsl-patching" rel="noopener noreferrer"&gt;patch feature&lt;/a&gt; of Trailblazer internally - implementing this for the &lt;code&gt;trailblazer-test&lt;/code&gt; gem was nothing more but applying the patching mechanics with three lines of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  RSpec and more
&lt;/h2&gt;

&lt;p&gt;In the next post we're going to introduce the RSpec matchers that are based on this gem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"passes with manual attributes"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;memo: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s2"&gt;"Reminder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="s2"&gt;"Do not forget"&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;

  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;
    &lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;pass_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s2"&gt;"Reminder"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Being a bit more verbose, they provide the exact same behavior that we ship for Minitest. If you have suggestions or ideas, never hesitate to discuss those with us! Now, have fun testing. Or at least, try to!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>GC compaction: Behold your hash keys!</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Mon, 11 Nov 2024 10:59:07 +0000</pubDate>
      <link>https://forem.com/trailblazer/gc-compaction-behold-your-hash-keys-2ii1</link>
      <guid>https://forem.com/trailblazer/gc-compaction-behold-your-hash-keys-2ii1</guid>
      <description>&lt;p&gt;&lt;em&gt;or: The release of trailblazer-activity-0.16.4&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Apparently, recent versions of Ruby (&amp;gt;= 3.2) allow for a new feature to save memory in production: garbage collection &lt;em&gt;compaction&lt;/em&gt;. According to the bug reports of a bunch of Trailblazer power users, calling the compaction manually - after the application has loaded - is now a thing in the Rails world.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app has eager-loaded all dependencies.&lt;/span&gt;
&lt;span class="no"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify_compaction_references&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;expand_heap: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;toward: :empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, the fragmentation of the stack is minimized after invoking the compaction, objects and their pointers are moved around, and memory is freed.&lt;/p&gt;

&lt;p&gt;While this sounds like a great thing to do, an actual bug in Ruby lead to runtime errors coming in from applications using this pattern and Trailblazer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NoMethodError: undefined method `[]' for nil
      # trailblazer-activity/lib/trailblazer/activity/circuit.rb:80:in `next_for'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, at first glance, this looks like a problem in Trailblazer's heart, the &lt;code&gt;activity&lt;/code&gt; gem, which implements the runtime object for an operation. Every time you're running a &lt;code&gt;Trailblazer::Operation&lt;/code&gt;, the internal activity will &lt;a href="https://github.com/trailblazer/trailblazer-activity/blob/624776389a671c76929dc489ef916efa53c8793e/lib/trailblazer/activity/circuit.rb#L44" rel="noopener noreferrer"&gt;execute a particular step&lt;/a&gt; and check for the next step to be invoked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hash keys do matter
&lt;/h2&gt;

&lt;p&gt;A "step" in an activity can literally be any callable object. In Trailblazer core code, we often use the pattern of method objects to implement step logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyOperation&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;task: &lt;/span&gt;&lt;span class="no"&gt;Validation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:extract_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;task: &lt;/span&gt;&lt;span class="no"&gt;Validation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:validate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deep inside the operation's activity (well, it's actually not really deep), a hash is created that looks roughly as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;circuit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;#&amp;lt;Method: Validation.extract_params&amp;gt; =&amp;gt; {&lt;/span&gt;
    &lt;span class="no"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;Method: Validation.validate&amp;gt;,&lt;/span&gt;
    &lt;span class="no"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;#&amp;lt;Method: Validation.validate&amp;gt; =&amp;gt; {...}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see, the gist of Trailblazer is actually a hash of steps pointing to possible outcomes and the "next" step for each outcome. Super simple stuff!&lt;/p&gt;

&lt;p&gt;The problem we now faced was that we use &lt;code&gt;Method&lt;/code&gt; instances as hash keys. This worked fine until people started using GC compaction, because the compaction erroneously changed those &lt;em&gt;method&lt;/em&gt; hash keys, they were now pointing to ...nothing, crashing the running operation with a &lt;code&gt;NoMethodError&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;hsh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;hsh&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 

&lt;span class="no"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify_compaction_references&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;expand_heap: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;toward: :empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;hsh&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep in mind that this problem only arouse when people were deploying the compaction "trick". I personally didn't even know about this new GC feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's all your fault!
&lt;/h2&gt;

&lt;p&gt;As if that runtime error wasn't enough, bringing us &lt;a href="https://github.com/trailblazer/trailblazer-activity/issues/60" rel="noopener noreferrer"&gt;several dramatic bug reports&lt;/a&gt;, people now started challenging our code design, asking "why are you using a method instance as a hash key, that's not good style!" or something along that. &lt;/p&gt;

&lt;p&gt;However, it turned out that this is really a &lt;a href="https://github.com/ruby/ruby/pull/11966" rel="noopener noreferrer"&gt;bug in Ruby&lt;/a&gt; and will be fixed in Ruby 3.2.7, 3.3.7 and 3.4.0. While we're at it, I'd love to thank the Ruby core team for their swift responses and their efficiency at fixing this. It took Peter Zhu only a few hours, I wish I was as motivated as this gentleman. &lt;/p&gt;

&lt;h2&gt;
  
  
  Quick fix
&lt;/h2&gt;

&lt;p&gt;If you happen to be stuck with a Ruby version that's not fixing the compaction problem, you can update to &lt;code&gt;trailblazer-activity-0.16.4&lt;/code&gt; and include our temporary fix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# For Ruby &amp;lt;3.2.7, &amp;lt;3.3.7, &amp;lt;3.4.0 &lt;/span&gt;
&lt;span class="c1"&gt;# initializers/trailblazer.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"trailblazer/activity/circuit/ruby_with_unfixed_compaction"&lt;/span&gt;
&lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Activity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Circuit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Activity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Circuit&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RubyWithUnfixedCompaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enjoy the runtime-error free time, and don't forget to &lt;a href="https://trailblazer.zulipchat.com" rel="noopener noreferrer"&gt;join our Zulip chat&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;PS: Also, shoutouts to &lt;a href="https://github.com/tiagotex" rel="noopener noreferrer"&gt;Tiago Teixera&lt;/a&gt;, who actively spotted and isolated this gnarly bug!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Fetching models using the new Model::Find() macro</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Thu, 27 Jun 2024 06:43:20 +0000</pubDate>
      <link>https://forem.com/trailblazer/fetching-models-using-the-new-modelfind-macro-jf3</link>
      <guid>https://forem.com/trailblazer/fetching-models-using-the-new-modelfind-macro-jf3</guid>
      <description>&lt;p&gt;Trailblazer comes with predefined steps we call &lt;a href="https://trailblazer.to/2.1/docs/operation/#operation-macro-api"&gt;"macros"&lt;/a&gt; that help you with common tasks such as validating a form object or finding an existing model using ActiveRecord (actually, any other ORM you approve of).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://trailblazer.to/2.1/docs/macro/#macro-model-model-find"&gt;newly released &lt;code&gt;Model::Find()&lt;/code&gt; macro&lt;/a&gt; is a replacement for &lt;code&gt;Model()&lt;/code&gt; that, over the years, turned out to be helpful but a bit hard to customize, as it wouldn't really allow you to change how things are performed in the background.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Update&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Activity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Railway&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;find_by: :id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params_key: :slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are going to need &lt;code&gt;trailblazer-macro&lt;/code&gt; 2.1.16 for this goodie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting the ID
&lt;/h2&gt;

&lt;p&gt;The new macro provides options such as &lt;code&gt;:params_key&lt;/code&gt; and &lt;code&gt;:column_key&lt;/code&gt; to configure how the ID is extracted. If you want to do it yourself, simply use a block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;find_by: :id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="o"&gt;**|&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:song&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:song&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bunch of discussions with long-term users lead us to the decision that overriding ID extraction should be straight-forward since this is more than just an edge case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing the query
&lt;/h2&gt;

&lt;p&gt;Once the ID is extracted, it's now very simple to customize how the query is performed (e.g. &lt;code&gt;find_by: id&lt;/code&gt; vs &lt;code&gt;find(id)&lt;/code&gt;). Nevertheless, the new key feature is the &lt;code&gt;:query&lt;/code&gt; option that allows you to write that code manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="no"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;query: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how the query logic can access &lt;code&gt;ctx&lt;/code&gt; and keyword arguments, just like a real step. The extracted ID is available in the variable &lt;code&gt;:id&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not_found terminus
&lt;/h2&gt;

&lt;p&gt;If one of the two steps weren't successful, you can instantly go to a &lt;a href="https://trailblazer.to/2.1/docs/macro/#macro-model-model-find-not-found"&gt;new terminus &lt;code&gt;not_found&lt;/code&gt;&lt;/a&gt; in your business operation, indicating to the outer world that this particular step failed. With the release of &lt;code&gt;trailblazer-endpoint&lt;/code&gt; this will become interesting as the endpoint code could, for instance, automatically render a 404 page for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;The code of this macro is nothing special. In fact, it simply creates a tiny nested activity behind the scenes with two steps, one to extract the ID, and one to actually fetch the model.&lt;/p&gt;

&lt;p&gt;Anyhow, we strongly recommend sticking with this macro instead of writing your own, for three reasons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Well, code we write and maintain is less work for you. Keep in mind that we also provide &lt;a href="https://trailblazer.to/2.1/docs/macro/#macro-model-model-find"&gt;documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Features like the &lt;code&gt;not_found&lt;/code&gt; terminus we added with forward-compatibility in mind: they will save you code once endpoints are becoming a thing.&lt;/li&gt;
&lt;li&gt;Debugging &lt;code&gt;Model::Find()&lt;/code&gt; is a matter of using our internal tracing. &lt;a href="https://trailblazer.to/2.1/docs/macro/#macro-model-model-find-debugging"&gt;In the trace&lt;/a&gt;, you can see which part failed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldoy6sfcwtfth7m70ikp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldoy6sfcwtfth7m70ikp.png" alt="Image description" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please &lt;a href="https://github.com/trailblazer/trailblazer/discussions/257"&gt;give us some feedback&lt;/a&gt; about what's missing or what you like about this simple addition to our stack. Have fun!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Trailblazer 2.1.3: How we made tracing +4x faster</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Fri, 15 Dec 2023 07:19:50 +0000</pubDate>
      <link>https://forem.com/trailblazer/trailblazer-213-how-we-made-tracing-4x-faster-j33</link>
      <guid>https://forem.com/trailblazer/trailblazer-213-how-we-made-tracing-4x-faster-j33</guid>
      <description>&lt;p&gt;&lt;em&gt;(PaaS providers hate this trick!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With the release of Trailblazer 2.1.3 not only does &lt;code&gt;#wtf?&lt;/code&gt; tracing receive a notable performance boost, we also paved the way for a web-based debugger by refactoring tracing internals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracing faster by factor 4-10
&lt;/h2&gt;

&lt;p&gt;When you run a (deeply nested) operation with #wtf?, the tracing code uses the runtime taskWrap to inject tracing before and after an actual step is called. BTW, we also uploaded new docs for this whole thing "taskWrap", if you're interested in extending TRB (for example, to add logging or APM), check out the new chapter: &lt;a href="https://trailblazer.to/2.1/docs/activity#activity-taskwrap"&gt;https://trailblazer.to/2.1/docs/activity#activity-taskwrap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason the old tracing code is super slow was because every time our tracing is run, a snapshot of &lt;code&gt;ctx&lt;/code&gt; was taken. That was &lt;a href="https://github.com/trailblazer/trailblazer-developer/blob/7dab86e84a4fef0fd4abb6af63e105dd227e258d/lib/trailblazer/developer/trace.rb#L90"&gt;literally just a &lt;code&gt;ctx.inspect&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;default_input_data_collector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrap_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# ctx: ctx.to_h.freeze,&lt;/span&gt;
    &lt;span class="ss"&gt;ctx_snapshot: &lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# TODO: proper snapshot!&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the &lt;code&gt;# TODO: proper snapshot!&lt;/code&gt;, right? Well that's what we were working on the past weeks! Instead of "photographing" the entire &lt;code&gt;ctx&lt;/code&gt;  over and over with &lt;code&gt;#inspect&lt;/code&gt; - which is tremendously slow once the &lt;code&gt;ctx&lt;/code&gt; grows - we now compare its variables and only &lt;a href="https://github.com/trailblazer/trailblazer-developer/blob/5254b4e8ab9617d58abd62d5be4e17b32c633f0c/lib/trailblazer/developer/trace/snapshot/versions.rb#L64"&gt;snapshot those that have changed&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmarks
&lt;/h3&gt;

&lt;p&gt;The entire refactoring is summarized &lt;a href="https://github.com/trailblazer/trailblazer-developer/pull/42"&gt;in PR 42&lt;/a&gt;, which, by its sheer name, should be answering all your questions. And yes, there are still &lt;code&gt;# TODO&lt;/code&gt;s after this refactoring, but tracing a moderate &lt;code&gt;ctx&lt;/code&gt; is now 4-10 times faster and consumes a lot less memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Calculating -------------------------------------
        inspect-only     10.862  (± 9.2%) i/s -     54.000  in   5.066396s
            snapshot     61.024  (± 8.2%) i/s -    305.000  in   5.032931s

Comparison:
            snapshot:       61.0 i/s
        inspect-only:       10.9 i/s - 5.62x  slower
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This  benchmark was run in a huge client application and made my day.&lt;/p&gt;

&lt;p&gt;Another win in this refactoring is that we can now configure a per-object snapshooter, so if you don't want your ActiveRecords to run arbitrary queries, a &lt;code&gt;value_snapshooter&lt;/code&gt; will be your friend. Big thanks to @richardboehme for his support here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Added &lt;code&gt;#left&lt;/code&gt; alias
&lt;/h2&gt;

&lt;p&gt;And for all of you who have a problem with &lt;code&gt;Operation.fail&lt;/code&gt; because your IDE doesn't like the name, we introduced &lt;code&gt;#left&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://trailblazer.to/2.1/docs/activity#activity-strategy-railway-left"&gt;https://trailblazer.to/2.1/docs/activity#activity-strategy-railway-left&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Trailblazer 2.1.2 bringing the Each() macro.</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Sun, 18 Dec 2022 08:49:10 +0000</pubDate>
      <link>https://forem.com/trailblazer/trailblazer-212-bringing-the-each-macro-1j5l</link>
      <guid>https://forem.com/trailblazer/trailblazer-212-bringing-the-each-macro-1j5l</guid>
      <description>&lt;p&gt;Recently we released a bunch of updated Trailblazer gems and along with those the &lt;code&gt;trailblazer&lt;/code&gt; framework 2.1.2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loops in Operation
&lt;/h2&gt;

&lt;p&gt;It felt great to close &lt;a href="https://github.com/trailblazer/trailblazer-macro/pull/38" rel="noopener noreferrer"&gt;several&lt;/a&gt; &lt;a href="https://github.com/trailblazer/trailblazer/issues/245" rel="noopener noreferrer"&gt;pull requests&lt;/a&gt; that were asking for (or even adding) support to iterate over datasets in TRB, basically an &lt;code&gt;each do ... end&lt;/code&gt; for operations.&lt;/p&gt;

&lt;p&gt;Integrating the features added by our fellow users with internal advanced mechanics such as tracing or patching took its time, that's why the "simple" PR sat there quite a while.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Song::Operation&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cover&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:model&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="no"&gt;Each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;dataset_from: :composers_for_each&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:notify_composers&lt;/span&gt;
      &lt;span class="c1"&gt;# more steps here or Subprocess()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:rearrange&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;composers_for_each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;composers&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Each()&lt;/code&gt; macro allows you to specify where the data set is coming from, and will then run the provided block (or another operation) for each item at runtime. &lt;/p&gt;

&lt;p&gt;Assuming we had three &lt;code&gt;composers&lt;/code&gt; in the data set when we invoke the operation, the nested code is run subsequently as if there were three dedicated &lt;code&gt;notify_composers&lt;/code&gt; steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cover&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:model&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:notify_composers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:notify_composers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:notify_composers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:rearrange&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added features such as collecting values from the iterated block or input/output filtering, all described in &lt;a href="https://trailblazer.to/2.1/docs/macro#macro-each" rel="noopener noreferrer"&gt;the newly added docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Speaking of, we moved the macro docs to an &lt;a href="https://trailblazer.to/2.1/docs/macro#macro-overview" rel="noopener noreferrer"&gt;entirely new section&lt;/a&gt;, and rewrote docs for &lt;code&gt;Wrap()&lt;/code&gt; as they were pretty basic before.&lt;/p&gt;

&lt;p&gt;One thing I particularly love about this new macro is that it integrates nicely with our debugging API. You can even see in which iteration an error occurred! Check the trace below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvw9fnunz9dv7nb58x2yh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvw9fnunz9dv7nb58x2yh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using Ruby's "native" &lt;code&gt;each&lt;/code&gt;, this'd be a PITA to debug.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://trailblazer.zulipchat.com/#" rel="noopener noreferrer"&gt;Your input&lt;/a&gt; on &lt;code&gt;Each()&lt;/code&gt; is crucial - feel free to chat to us at any time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composable input/output
&lt;/h2&gt;

&lt;p&gt;In former versions of Trailblazer, we had the &lt;code&gt;:input&lt;/code&gt; and &lt;code&gt;:output&lt;/code&gt; option to define what comes in and goes out of a step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Create&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:validate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;input: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:params&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# pass :params into the step&lt;/span&gt;
    &lt;span class="ss"&gt;output: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:result&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# only let :result out!&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Variable mapping - as this is called in Trailblazer - is, after nesting, the most commonly used feature of the framework.&lt;/p&gt;

&lt;p&gt;Good news: we are not dropping support for &lt;code&gt;:input&lt;/code&gt; and &lt;code&gt;:output&lt;/code&gt;! However, this is now superseded by "composable" variable mapping. On first glance, nothing much has changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:validate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;In&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:params&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
    &lt;span class="no"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:result&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, we replaced the &lt;code&gt;:symbol&lt;/code&gt; option with &lt;code&gt;In()&lt;/code&gt;, &lt;code&gt;Out()&lt;/code&gt;, and &lt;code&gt;Inject()&lt;/code&gt;. However, this comes in handy if you want to add or alter variable mapping that has been defined formerly, for example in a macro.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="no"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;# adds In() and maybe Inject()&lt;/span&gt;
  &lt;span class="no"&gt;In&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:current_user&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# we can add this on top!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even override and alter &lt;a href="https://trailblazer.to/2.1/docs/activity.html#activity-variable-mapping-inheritance" rel="noopener noreferrer"&gt;inherited variable mapping&lt;/a&gt; in a subclass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Upsert&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Create&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:validate_for_upsert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="ss"&gt;replace: :validate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# override inherited #validate step.&lt;/span&gt;
    &lt;span class="ss"&gt;inherit: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:variable_mapping&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# inherit filters from Create.&lt;/span&gt;
    &lt;span class="no"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:current_user&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# add this on top.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire approach with composable filters is extremely flexible and helps reducing bulky input/output code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introspect your filter chains
&lt;/h3&gt;

&lt;p&gt;While this might look like a syntax touch-up, and, yes, &lt;em&gt;anoooother&lt;/em&gt; API change, this brings us not only better structural features, but we can now introspect filter chains with the &lt;code&gt;developer&lt;/code&gt; gem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:validate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This renders the taskWrap and along with it the input and output filter chain. We even combine that with our upcoming IDE.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3w2l02diw508818w4ng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3w2l02diw508818w4ng.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that this is not the final introspect API, though!&lt;/p&gt;

&lt;p&gt;The code for variable mapping sits in the &lt;a href="https://github.com/trailblazer/trailblazer-activity-dsl-linear/blob/9cdcf1efe5e656a2e586e3159d0b92b054b9da2c/lib/trailblazer/activity/dsl/linear/feature/variable_mapping.rb#L125" rel="noopener noreferrer"&gt;&lt;code&gt;dsl-linear&lt;/code&gt; gem&lt;/a&gt; and got some love, again. It is much easier to follow (and about 1/3 faster!) so maybe have a peek and add whatever you're missing?&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://trailblazer.to/2.1/docs/activity.html#activity-variable-mapping" rel="noopener noreferrer"&gt;brand-new docs on our website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugger and IDE
&lt;/h2&gt;

&lt;p&gt;When working on Reform 3 and &lt;code&gt;Each()&lt;/code&gt; I used &lt;a href="https://trailblazer.to/2.1/docs/trailblazer.html#trailblazer-developer" rel="noopener noreferrer"&gt;Trailblazer's debugging tools&lt;/a&gt; frequently and while benefiting from &lt;code&gt;wtf?&lt;/code&gt; and friends, we decided to improve the internal debugging API. &lt;/p&gt;

&lt;p&gt;This turned into a bare-bones IDE that allows you to traverse through the trace of a deeply nested operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8vso8czei3xckivar403.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8vso8czei3xckivar403.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can trace the incoming and outgoing ctx for each step, and even inspect its compiled taskWrap - where you can see the variable mapping chain!&lt;/p&gt;

&lt;p&gt;The IDE will be a service launched by us in 2023.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Don't get me started, I could keep talking for hours! Anyhow, let's wrap this up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vNAUZ0rUcow" rel="noopener noreferrer"&gt;Reform 3 is coming&lt;/a&gt;, we're pretty close to a presentable version. It is entirely built with Trailblazer, so tracing, variable mapping, extendability and all things TRB come for free!&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;endpoint&lt;/code&gt; gem will get a stable release, promised!&lt;/li&gt;
&lt;li&gt;More &lt;a href="https://www.youtube.com/channel/UCi2P0tFMtjMUsWLYAD1Ezsw" rel="noopener noreferrer"&gt;Trailblazer Tales&lt;/a&gt; are planned, sorry for the long interruption! Next topics will be nesting, variable mapping and the Wiring API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're looking forward to your feedback! I deleted Twitter and &lt;a href="https://ruby.social/@apotonick" rel="noopener noreferrer"&gt;live on Mastodon&lt;/a&gt; now. Cheers and happy holidays!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Five first episodes of TRAILBLAZER TALES</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Thu, 13 Jan 2022 10:17:20 +0000</pubDate>
      <link>https://forem.com/trailblazer/five-first-episodes-of-trailblazer-tales-3l2k</link>
      <guid>https://forem.com/trailblazer/five-first-episodes-of-trailblazer-tales-3l2k</guid>
      <description>&lt;p&gt;Finally, I started producing short 6 minutes &lt;a href="https://www.youtube.com/channel/UCi2P0tFMtjMUsWLYAD1Ezsw"&gt;episodes on Youtube&lt;/a&gt; to learn about Trailblazer. This is actually more fun than I expected!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--blmhzhrb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1wengxz7cm7si4sl7yo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--blmhzhrb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1wengxz7cm7si4sl7yo.png" alt="Our Youtube channel" width="625" height="899"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first five episodes focus on refactoring a messy Rails controller action to a Trailblazer operation. Here is the &lt;a href="https://www.youtube.com/channel/UCi2P0tFMtjMUsWLYAD1Ezsw"&gt;link to our channel&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Let us know what you think, and what is missing! In the following episodes we are going to discuss Reform, macros, nesting operations, and the new dependency injection features.&lt;/p&gt;

</description>
      <category>screencast</category>
    </item>
    <item>
      <title>Don't mix Forwardable and ActiveSupport::Delegate</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Thu, 02 Dec 2021 11:47:13 +0000</pubDate>
      <link>https://forem.com/trailblazer/dont-mix-forwardable-and-activesupportdelegate-5hmm</link>
      <guid>https://forem.com/trailblazer/dont-mix-forwardable-and-activesupportdelegate-5hmm</guid>
      <description>&lt;p&gt;A few days ago I released the &lt;a href="https://github.com/apotonick/disposable/commit/979dc5f4950881c913263340a12628ec1ea52566"&gt;&lt;code&gt;disposable&lt;/code&gt; gem&lt;/a&gt; minor line 0.6.0. The goal was to &lt;a href="https://github.com/apotonick/disposable/commit/979dc5f4950881c913263340a12628ec1ea52566#diff-cbfbccff8b533b3c195c7058cf7245b5559ecf5f0a16b4e7b83390a2ade42be6"&gt;remove unnecessary coupling&lt;/a&gt; to the old &lt;code&gt;uber&lt;/code&gt; gem that was once planned to hold a set of abstractions we need in all Trailblazer gems.&lt;/p&gt;

&lt;p&gt;What I mainly worked on was removing &lt;code&gt;Uber::Delegate&lt;/code&gt; and supersede it with Ruby's built-in &lt;code&gt;Forwardable&lt;/code&gt;. We simply need to delegate a bunch of methods from one object to another, and &lt;code&gt;Forwardable&lt;/code&gt; does just that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7cvuFNA6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yr8651xwcvieb9tx46w7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7cvuFNA6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yr8651xwcvieb9tx46w7.png" alt="Image description" width="591" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what the new code basically did was to extend the &lt;code&gt;Disposable::Twin&lt;/code&gt; class and use &lt;code&gt;def_delegators&lt;/code&gt; to create automatic delegations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Disposable::Twin&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Forwardable&lt;/span&gt; &lt;span class="c1"&gt;# this used to be Uber::Delegate&lt;/span&gt;

  &lt;span class="n"&gt;def_delegators&lt;/span&gt; &lt;span class="ss"&gt;:model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;def_delegators&lt;/span&gt; &lt;span class="ss"&gt;:model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously, removing gem dependencies, even if they're your own, feels good, so I pushed this new gem after running tests in two projects and leaned back in my pricey office desk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here comes another delegate!
&lt;/h2&gt;

&lt;p&gt;A day later we received &lt;a href="https://github.com/trailblazer/reform/issues/534"&gt;bug reports&lt;/a&gt; from several users. All of them were using &lt;a href="https://github.com/trailblazer/reform"&gt;Reform&lt;/a&gt; form objects, the problem was thrown from &lt;code&gt;Reform::Form&lt;/code&gt; classes they had in their app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ArgumentError:
   wrong number of arguments (given 2, expected 1)
 # ruby/3.0.0/forwardable.rb:133:in `instance_delegate'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developers reported that before the upgrade to &lt;code&gt;Disposable 0.6.0&lt;/code&gt;, code as the following worked fine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateForm&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Reform&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Form&lt;/span&gt;
  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:assignment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :model&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the upgrade, this would break with the &lt;code&gt;ArgumentError&lt;/code&gt; illustrated above. My first suspicion was that this is some code not ready for Ruby 3, as this kind of exception is often observed in older code that runs with Ruby 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delegate or delegate?
&lt;/h2&gt;

&lt;p&gt;An hours going through the &lt;a href="https://ruby-doc.org/stdlib-3.0.0/libdoc/forwardable/rdoc/Forwardable.html"&gt;&lt;code&gt;Forwardable&lt;/code&gt; code&lt;/a&gt; and playing with the broken example app provided by some nice user didn't bring any progress.&lt;/p&gt;

&lt;p&gt;At some point I realized that what the users were actually using to create delegations was the &lt;code&gt;delegate&lt;/code&gt; method not from &lt;code&gt;Forwardable&lt;/code&gt; but from &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt;. The &lt;a href="https://apidock.com/rails/v5.2.3/Module/delegate"&gt;docs enlightened me&lt;/a&gt; that this module mixes a method &lt;code&gt;#delegate&lt;/code&gt; into the class - the method that was used by all users reporting the error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Order matters!
&lt;/h2&gt;

&lt;p&gt;It feels really stupid in hindsight, and it took at least two hours to understand the problem.&lt;/p&gt;

&lt;p&gt;Here's the chain of sparks that lead to fixing the problem.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;Reform::Form&lt;/code&gt; class inherits from &lt;code&gt;Twin::Disposable&lt;/code&gt;. This means that each &lt;code&gt;Form&lt;/code&gt; class gets &lt;code&gt;Forwardable&lt;/code&gt; methods mixed in. Of course, only &lt;em&gt;after&lt;/em&gt; upgrading &lt;code&gt;disposable&lt;/code&gt; to 0.6.0 where we automatically included &lt;code&gt;Forwardable&lt;/code&gt; into &lt;code&gt;Twin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Before 0.6.0, the former delegation from &lt;code&gt;Uber::Delegate&lt;/code&gt; only mixed in one method called &lt;code&gt;#delegates&lt;/code&gt; (mind the &lt;code&gt;s&lt;/code&gt;). &lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Forwardable&lt;/code&gt; module includes a method called &lt;code&gt;#delegate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Our nasty &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; module also has a method called &lt;code&gt;#delegate&lt;/code&gt; - just like &lt;code&gt;Forwardable&lt;/code&gt;!&lt;/li&gt;
&lt;li&gt;In some Rails apps, delegation from ActiveSupport is automatically included into all classes. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem we were facing was that ActiveSupport and its delegation code was included automatically into the &lt;code&gt;Reform::Form&lt;/code&gt; class &lt;em&gt;before&lt;/em&gt; the &lt;code&gt;disposable&lt;/code&gt; gem included &lt;code&gt;Forwardable&lt;/code&gt; and hence the latter overriding the &lt;code&gt;#delegate&lt;/code&gt; method you are expecting.&lt;/p&gt;

&lt;p&gt;In other words, &lt;code&gt;Forwardable&lt;/code&gt; killed ActiveSupport's &lt;code&gt;#delegate&lt;/code&gt; method as it was loaded and included later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem solved
&lt;/h2&gt;

&lt;p&gt;We decided to &lt;a href="https://github.com/apotonick/disposable/commit/684a8929c495123a3e7537d2e0be1051342a007f"&gt;removed &lt;code&gt;Forwardable&lt;/code&gt; from &lt;code&gt;disposable&lt;/code&gt;&lt;/a&gt; for now and fixed the obscure issue. When using &lt;code&gt;delegate&lt;/code&gt; in a form it now refers to ActiveSupport's implementation.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Official Trailblazer 2.1 Release Notes</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Thu, 02 Dec 2021 10:57:56 +0000</pubDate>
      <link>https://forem.com/trailblazer/official-trailblazer-21-release-notes-48e8</link>
      <guid>https://forem.com/trailblazer/official-trailblazer-21-release-notes-48e8</guid>
      <description>&lt;p&gt;(Not that there's an unofficial one anywhere, but yeah...)&lt;/p&gt;

&lt;p&gt;This was posted on 27 Feb 2021 on the TRB website.&lt;/p&gt;

&lt;p&gt;Note: We have &lt;a href="https://trailblazer.to/2.1/docs/trailblazer.html#trailblazer-2-1-migration"&gt;fully documented the migration path&lt;/a&gt; to 2.1 while upgrading several complex apps. It worked.™&lt;/p&gt;

&lt;p&gt;After around 3 years of silence, Trailblazer is back with its 2.1 release. Lots of improvements on the core gems come hand-in-hand with a quite flat migration path, so don't ya worry! We're also proud to tell you all about our new upcoming projects in this post, as well as communicating a looot of background about what's been going on in those past few years.&lt;/p&gt;

&lt;p&gt;Here's a quick summary for those who want to get back to coding.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API CHANGE&lt;/strong&gt; The &lt;code&gt;call&lt;/code&gt; API has slightly changed from two arguments to just one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AUTOLOADING&lt;/strong&gt; We removed the &lt;code&gt;trailblazer-loader&lt;/code&gt; gem just like Apple removed the headphone jack from the iPhone 6. This brings you faster startup and consistency with Rails autoloading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOCUMENTATION&lt;/strong&gt; We have a new website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TRACING&lt;/strong&gt; We heard your cries. You can now see what path an operation took and use exception tracing for a beautiful dev experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Good to see you, again!
&lt;/h2&gt;

&lt;p&gt;It almost feels unreal finishing up this release post. It's been so long! The work put into Trailblazer 2.1 has been tremendous, it could easily have been TRB 3.0, or even TRB III, since Roman version numbering turns out to be quite a fancy thing to do. However, as much as the internals have been improved, as little has changed on the public APIs of Trailblazer, so we decided to go with a minor release.&lt;/p&gt;

&lt;p&gt;With the introduction of another new callback &lt;code&gt;after_save_commit&lt;/code&gt; in Rails 6 it becomes obvious that we do have a problem in software engineering: how do we structure business code? We do have a few dozen database abstractions in every language, well written and designed, but, yeah, how do I structure the actual code of my domain, of my application's needs? What's the best way to put together application functions, put them in order, prevent other functions from being executed when it's not the time, and so on?&lt;/p&gt;

&lt;p&gt;Trailblazer is an architectural style coming as a framework to implement our patterns, trying to answer that one question: "How do I implement business processes?". It gives you strong conventions along with patterns to structure your code flow better, handle errors, and integrate those components into bigger flows - or &lt;em&gt;processes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Having said that, I let you decide whether or not you join the ride! There is nothing wrong with building your own "service layer", and many companies have left the Traiblazer track in the past years due to problems they had and that we think we now fixed.&lt;/p&gt;

&lt;p&gt;Speaking of problems, the craziness of the past years working on Trailblazer 2.1 can be visualized with the following screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--juHp0TV8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/psb0151q08mjtznvdksu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--juHp0TV8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/psb0151q08mjtznvdksu.png" alt="Image description" width="702" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Very often, in order to fix one function &lt;em&gt;A&lt;/em&gt; in a commercial app, I had to rewire a track in the &lt;code&gt;endpoint&lt;/code&gt; gem, which would only work if I add another DSL option in the &lt;code&gt;dsl&lt;/code&gt; gem, which couldn't work straight away because the normalizer in the &lt;code&gt;context&lt;/code&gt; gem needed to be extended, which was only possible if you could finally extract the start event via the &lt;code&gt;activity&lt;/code&gt; gem whose test suite was currently broken because I needed to document feature &lt;code&gt;x&lt;/code&gt; first, which couldn't be documented because before I had to allow dynamic params in the &lt;code&gt;macro&lt;/code&gt; gem and... I think you got my point.&lt;/p&gt;

&lt;p&gt;Luckily, I got help from a band of great people, I took it easy on the conference front, and for some other reasons I still don't understand all this is now finished and here's TRB 2.1.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Design
&lt;/h2&gt;

&lt;p&gt;A big change from TRB 1.1 and even 2.0 is that we apply functional principles in most places. We barely keep state in instance variables. Rather, data is passed around from operation to operation, from step to step. We use OOP and inheritance solely for compile-time configuration. You define classes, steps, tracks and flows, inherit those, customize them using Ruby's built-in mechanics, but this all happens at compile-time. At runtime, no structures are changed anymore, your code is executed dynamically but only the &lt;code&gt;ctx&lt;/code&gt; (formerly &lt;code&gt;options&lt;/code&gt;) and its objects are mutated. This massively improves the code quality and with it, the runtime stability, which Ruby is famous for *cough.&lt;/p&gt;

&lt;p&gt;I extracted a lot of smaller libraries from the original &lt;code&gt;trailblazer&lt;/code&gt; gem. Most gems use the &lt;code&gt;activity&lt;/code&gt; gem itself, which is the core of an operation and provides the runtime object that executes your steps in a certain order. It's so simple that I sometimes wonder why it took years to develop it! But all those little interfaces, structures and principles that you see as "common sense", they took a while to figure out.&lt;/p&gt;

&lt;p&gt;A lot of work went into the &lt;code&gt;dsl-linear&lt;/code&gt; gem that provides a DSL to create activities (or operations) using your old friend &lt;code&gt;#step&lt;/code&gt;. It's not even a DSL since it doesn't create code at run-time, it doesn't even create code: it creates runtime objects, &lt;em&gt;activities&lt;/em&gt;, that are executed when your operation is run. You could create those objects manually without the DSL, or even write your own DSL, which we're hoping for!&lt;/p&gt;

&lt;p&gt;So, whenever you hear the medieval argument "Trailblazer is just a nasty DSL!", forgive your opponent, you now know better. The entire framework is based on small, clean Ruby structures that can be executed programmatically.&lt;/p&gt;

&lt;p&gt;Also, the more I &lt;em&gt;use&lt;/em&gt; Trailblazer in projects or even in Trailblazer itself, I feel how needed those new abstractions are. Yes, you can write everything with your own code, you don't need abstractions for flow control and automatic error handling, which makes me wonder why you're not programming in assembler since Ruby is also an "unnecessary abstraction" on top of a processor. We need abstractions, unless you want to program like we did 30 years ago.&lt;/p&gt;

&lt;p&gt;Ok, enough of this - let's jump into the things that actually help you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loader / Startup Speed
&lt;/h2&gt;

&lt;p&gt;Startup speed and loading of operations has been a never-ending problem.&lt;/p&gt;

&lt;p&gt;To make it short: we returned to the &lt;em&gt;Rails Way™&lt;/em&gt;, lowering our heads in shame, and adhere to the Rails file and class naming structure for &lt;em&gt;operations&lt;/em&gt;. What used to be named &lt;code&gt;Memo::Create&lt;/code&gt; is now &lt;code&gt;Memo::Operation::Create&lt;/code&gt;, which reflects its file location &lt;code&gt;concepts/memo/operation/create.rb&lt;/code&gt; (still the same!) and works with Rails' autoloading.&lt;/p&gt;

&lt;p&gt;The big benefit is: we could remove the &lt;code&gt;trailblazer-loader&lt;/code&gt; gem. The alternative loader was a naive attempt by me to combine "our" naming style with the Rails file structure. After endless fire-fighting against broken development reloading, inconsistencies, slow startup times in develpment mode, more incompatibilities with Rails' autoloader and some clarifying discussions with the great &lt;a href="https://github.com/fxn"&gt;Xavier Noria&lt;/a&gt; himself (author of Rails loading and zeitwerk), we ditched our loading mechanism.&lt;/p&gt;

&lt;p&gt;If you want to take advantage of the fast server startup times and (almost) seamless hot reloading of classes, you need to &lt;a href="https://trailblazer.to/2.1/docs/trailblazer.html#trailblazer-rails-loader-loader-with-rails"&gt;rename your operations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This change is not super dramatic at all, given that in the future, you won't be calling operations yourself all too much anymore. With the upcoming &lt;code&gt;workflow&lt;/code&gt; and &lt;code&gt;endpoint&lt;/code&gt; gems, those abstractions will invoke the business logic for you, so don't worry too much.&lt;/p&gt;

&lt;h2&gt;
  
  
  New website
&lt;/h2&gt;

&lt;p&gt;In case you haven't noticed: you are reading this release blog post on a newly designed website! Not only does it come with the most beautiful, romantic and inspiring &lt;a href="https://trailblazer.to/2.1/"&gt;hero backdrop ever&lt;/a&gt; (featuring a James-Cameron-worthy parallax effect, too!), it also attempts to provide all the old documentation while bringing you the hottest off-the-press docs for our new gems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yZZ75SJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87zs3kva7g70io851dhg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yZZ75SJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87zs3kva7g70io851dhg.png" alt="Image description" width="454" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We try to keep the "information architecture" - a word I wouldn't have learned without the inspiring &lt;a href="https://github.com/myabc"&gt;Alex Coles&lt;/a&gt; - as simple as possible: so far, we got a handful of pages accessible through the top navigation, and then the documentation behind the &lt;a href="https://trailblazer/to/2.1/docs/trailblazer.html"&gt;DOCS&lt;/a&gt; link. Here, the right sidebar helps you to navigate within the chapter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VXpAshO3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iclmwozf7g3huxzztxa2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VXpAshO3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iclmwozf7g3huxzztxa2.png" alt="Image description" width="880" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After all, we're a nerdy developer's library and don't need no million hipster landing pages! One is more than enough and took months of collaboration between us, the design and web team in Argentina and our awesome comic artist &lt;a href="http://www.joshbauman.com/"&gt;Josh Bauman&lt;/a&gt; who also drew the epic &lt;a href="https://leanpub.com/trailblazer"&gt;Trailblazer book&lt;/a&gt; illustrations in 2016.&lt;/p&gt;

&lt;p&gt;The documentation has been designed to make coherent information as quickly accessible as possible. We even added a search feature - something our beloved user base has asked for many times. Just hit the &lt;code&gt;/&lt;/code&gt; key (or &lt;code&gt;Shift+7&lt;/code&gt;) in the docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  API CHANGES
&lt;/h2&gt;

&lt;p&gt;The new 2.1 version comes with a few necessary but reasonable changes in method signatures. As painful as that might sound to your Rails-spoiled ears, we preferred to fix design mistakes now before dragging them on forever.&lt;/p&gt;

&lt;h3&gt;
  
  
  New call API
&lt;/h3&gt;

&lt;p&gt;In versions before 2.1, the automatic merging of the &lt;code&gt;params&lt;/code&gt; part and the additional options was confusing many new users and an unnecessary step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# old style&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"current_user"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument (&lt;code&gt;params&lt;/code&gt;) was merged into the second argument using the key &lt;code&gt;"params"&lt;/code&gt;. You now pass one hash to &lt;code&gt;call&lt;/code&gt; and hence do the merging yourself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# new style&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;current_user: &lt;/span&gt;&lt;span class="n"&gt;current_use&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your steps use the existing API, and everything here is as it used to be before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Memo::Operation::Create&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:create_model&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ..&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new &lt;code&gt;call&lt;/code&gt; API is much more consistent and takes away another thing we kept explaining to new users - an indicator for a flawed API. Believe it or not, but not a single time has this topic come up again on our &lt;a href="https://trailblazer.zulipchat.com/"&gt;support forum&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&amp;lt;%= h4 "Symbol keys for ctx" %&amp;gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ctx&lt;/code&gt; (or &lt;code&gt;options&lt;/code&gt;) object now behaves like a &lt;code&gt;HashWithIndifferentAccess&lt;/code&gt;, a pattern used for Rails' &lt;code&gt;params&lt;/code&gt; object. In other words, you can now access &lt;code&gt;ctx[:current_user]&lt;/code&gt; instead of &lt;code&gt;ctx["current_user"]&lt;/code&gt; and conveniently use the keyword argument version of it, too.&lt;/p&gt;

&lt;p&gt;Another handy addition to &lt;code&gt;ctx&lt;/code&gt; is aliasing. Before 2.1, you had to refer to, say, the main contract via &lt;code&gt;ctx["contract.default"]&lt;/code&gt;. While I designed that name "back in the days" with multiple contracts per operation in mind, it turned out to be quite clumsy since you couldn't access it as a keyword argument.&lt;/p&gt;

&lt;p&gt;You're able to set aliases now for the &lt;code&gt;ctx&lt;/code&gt; object, allowing magical things to happen, amongst those an aliasing from &lt;code&gt;"contract.default"&lt;/code&gt; to &lt;code&gt;:contract&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Memo::Operation::Create&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:inspect_contract&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inspect_contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; same as ctx["contract.default"]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ctx&lt;/code&gt; object is &lt;a href="https://trailblazer.to/2.1/docs/trailblazer.html#trailblazer-context"&gt;now documented&lt;/a&gt; in its own section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Experience
&lt;/h2&gt;

&lt;p&gt;Debugging deeply nested operations before 2.1 &lt;strong&gt;sucked&lt;/strong&gt;. Even if there was no nesting applied, an exception thrown from a random step was hard to find, the native Ruby stacktrace didn't really help, the TRB and Rails framework noise made it a painful experience to spot the source of an exception.&lt;/p&gt;

&lt;p&gt;This was one of the major arguments in many companies against Trailblazer, besides it "being too complex" while it seemed much easier to program pure Ruby as we did in the 90s. And we liked it that way!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NxW8_82---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9npws6fweu5li12t9u2s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NxW8_82---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9npws6fweu5li12t9u2s.png" alt="Image description" width="394" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To tell you the truth, the new tracing feature was the &lt;a href="https://www.youtube.com/watch?v=mjsnd8dJbew"&gt;original reason why I decided to write 2.1&lt;/a&gt; and make you sit and wait in agony for years. Nevertheless, tracing is simply blowing my mind. I can't count how many hours and angering rushs of adrenaline I've saved since the introduction of the &lt;code&gt;wtf?&lt;/code&gt; method and its helpful higher-level stack trace.&lt;/p&gt;

&lt;p&gt;Not only does tracing show you the path the execution took in a successful invokation, it also shows you where an exception happenend. And not "in Ruby", it's "in Trailblazer", in the actual step where things broke.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CxojkPwY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ni4mn8iny4kzlnk2avp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CxojkPwY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ni4mn8iny4kzlnk2avp.png" alt="Image description" width="718" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the &lt;a href="https://trailblazer.to/2.1/docs/trailblazer.html#trailblazer-developer"&gt;new &lt;code&gt;developer&lt;/code&gt; gem&lt;/a&gt;, you can trace executions, find exceptions, observe certain variables in the &lt;code&gt;ctx&lt;/code&gt; throughout the flow, render operations and activities to visually understand what's going on, and much more.&lt;/p&gt;

&lt;p&gt;As this new gem turns out to be a massive time saver for everyone, yes, even for &lt;em&gt;us&lt;/em&gt;, we're focusing on extending and adding functions such as visualizing nested activities.&lt;/p&gt;

&lt;p&gt;It might sound a bit overly sentimental or even cheesy, but I personally think that this is lifting the dev experience to a new level that will be hard to come by in pure Ruby. Yes, Trailblazer is adding new abstractions and concepts and they are different to the 90s-Ruby, but now, at the latest, it becomes obvious how this improves the developing process. We're no longer talking in two-dimensional method stack traces or byebug hoops, the language and conception is changing to the actual higher level code flow, to activities sitting in activities structured into smaller &lt;code&gt;step&lt;/code&gt; units.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wtf?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Memo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've used the &lt;code&gt;wtf?&lt;/code&gt; method, you will feel it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Unlimited Wiring
&lt;/h2&gt;

&lt;p&gt;Over the past years, harnessing the flow control and error handling mechanics we added in 2.0, one thing became obvious pretty quickly: users want more wiring possibilities. The linear railway and "fast track" concepts have been a helpful tool for structuring code flow, but people need to be able to model arbitrary flows.&lt;/p&gt;

&lt;p&gt;Needless to say that 2.1 provides you just that. We added the concepts of outputs to steps that can be added or "re-wired" using the &lt;a href="https://trailblazer.to/2.1/docs/activity.html#activity-wiring-api"&gt;Wiring API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RykJoPn2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xwl22yrps5895t0cwvtg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RykJoPn2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xwl22yrps5895t0cwvtg.png" alt="Image description" width="679" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may add additional outputs, rewire, go back, error-out in additional termini, go nuts, and all that using a super simple DSL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Execute&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Activity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Railway&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:failure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;End&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:not_found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:validate&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="no"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:retry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for both &lt;code&gt;Activity&lt;/code&gt; subclasses and operations (which are just special-flavored &lt;code&gt;Activity::FastTrack&lt;/code&gt;s).&lt;/p&gt;

&lt;p&gt;While you're still wondering whether we've completely lost it, we already use those new wiring mechanics to build everything from simple edge cases in code flows to long-running, complex business workflows.&lt;/p&gt;

&lt;p&gt;A major improvement here is the ability to maintain more than two explicit termini. In 2.0, you had the &lt;code&gt;success&lt;/code&gt; and the &lt;code&gt;failure&lt;/code&gt; termini (or "ends" as we used to call them). Now, additional ends such as &lt;code&gt;not_found&lt;/code&gt; can be leveraged to communicate a non-binary outcome of your activity or operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qCAVKV0n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iad6mj5hcya3kr9ctfu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qCAVKV0n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iad6mj5hcya3kr9ctfu0.png" alt="Image description" width="880" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using a terminus to indicate a certain outcome - in turn - allows for much stronger interfaces across nested activities and less guessing! For example, in the new &lt;code&gt;endpoint&lt;/code&gt; gem, the &lt;code&gt;not_found&lt;/code&gt; terminus is then wired to a special "404 track" that handles the case of "model not found". The beautiful thing here is: there is no guessing by inspecting &lt;code&gt;ctx[:model]&lt;/code&gt; or the like - the &lt;code&gt;not_found&lt;/code&gt; end has only one meaning!&lt;/p&gt;

&lt;p&gt;Check the &lt;a href="https://trailblazer.to/2.1/docs/endpoint.html"&gt;&lt;code&gt;endpoint&lt;/code&gt; gem&lt;/a&gt; if you want to see that in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core team
&lt;/h2&gt;

&lt;p&gt;In the past 1 ½ years something weird happened: a &lt;em&gt;real&lt;/em&gt; core team formed around the Trailblazer gems. I say "real" because in the past 15 years of OSS, I've had people come and go, being of great help but never staying and taking over long-term responsibilities - which I found to be the pivotal element of a core team.&lt;/p&gt;

&lt;p&gt;Eventually, those kids convinced me to start the &lt;a href="https://github.com/trailblazer/"&gt;Trailblazer organization&lt;/a&gt; on Github and move over all "apotonick gems". Over the course of time, I saw myself giving away that aforementioned responsibility with a smile on my face, adding owners and collaborators to gems, yes, even giving away entire gems, letting people work on documentation and just trusting someone and their skills.&lt;/p&gt;

&lt;p&gt;I have no words to describe how good that feels!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wdrISe52--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3lg8cbibpdevbn8nw1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wdrISe52--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3lg8cbibpdevbn8nw1l.png" alt="Image description" width="880" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For me, a dream has come true. I work with crazy geniuses who share many of my opinions (not all, and that's good). I learned to "let go" and simply trust others to maintain certain gem suites. Messages like "don't worry, I'll do it" combined with a pull requests minutes later - things I literally dreamed of a few years ago, are now part of my daily routine.&lt;/p&gt;

&lt;p&gt;Here are those kids, in the order of appearance: &amp;lt;3&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/fernandes"&gt;Celso Fernandes&lt;/a&gt; who's been part of TRB since the 12ths commit or something. He's currently focusing on the business side, consulting, and is the magical sysop of our infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/emaglio"&gt;Emanuele Magliozzi&lt;/a&gt; worked for many years as a TRB consultant and is now the master of &lt;code&gt;reform&lt;/code&gt; and its 2.x line and interrupting our important daily work with Aussie bantering.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/seuros"&gt;Abdelkader Boudih&lt;/a&gt; who's still unaware that I secretly call him &lt;em&gt;"The Machine"&lt;/em&gt; since I'm not sure he's an AI bot trained to fulfill basically any request an OSS author might have.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/peterpunk"&gt;Pedro Visintin&lt;/a&gt;, besides keeping me busy talking about punk rock guitars, is the person behind the PRO editor and our sharply-dressed agile marketing director.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/KamilMilewski"&gt;Kamil Milewski&lt;/a&gt; has been contributing to TRB for years both intellectually and with many code additions and even more deletions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/krzykamil"&gt;Krzysztof Piotrowski&lt;/a&gt; has been tirelessly helping with writing example applications and turned out to be &lt;em&gt;the&lt;/em&gt; refactoring expert on board.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/panSarin"&gt;Adam Piotrowski&lt;/a&gt; is resisting my deceiving calls to work on the code side and rather manages our processes - so we gladly let him do just this!&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/yogeshjain999"&gt;Yogesh Khater&lt;/a&gt; is now working full-time for Trailblazer, working on all 48 repositories at the same time (plus our private ones!), releasing gems when I sleep, leaving me jobless and peacefully napping in the sun.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to the organically formed core team, I started &lt;a href="https://trailblazer.to/2.1/about_us.html"&gt;Trailblazer GmbH&lt;/a&gt; 4 years ago with my relocation from Australia back to Europe. One of our consulting clients is the central police department of a German state that has kept me busy for more than three years now. And yes, at TRB GmbH, we do pay people to work on OSS!&lt;/p&gt;

&lt;h2&gt;
  
  
  License and PRO
&lt;/h2&gt;

&lt;p&gt;Around 2 years ago I decided to end the experiment of "TRB PRO" as I felt I didn't provide enough value to paying users. In the end, we had around 150 companies and individuals signed up, which was epic and a great funding source for more development.&lt;/p&gt;

&lt;p&gt;We're now relaunching PRO, but instead of a paid chat and (never existing) paid documentation, your team gets access to paid gems, our visual editor for workflows, and a commercial license.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xY0xXuf6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5td5lub5ntzkknt9qg5h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xY0xXuf6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5td5lub5ntzkknt9qg5h.png" alt="Image description" width="880" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The editor (that mostly plays along with the &lt;code&gt;workflow&lt;/code&gt; gem) is already in use for a bunch of commercial projects that we consult, but will be launched in its stable version somewhere this year.&lt;/p&gt;

&lt;p&gt;The core gems of Trailblazer have been re-licensed to LGPLv3 to raise awareness for this. For 99% of all our users, this has &lt;a href="https://trailblazer.to/2.1/docs/pro.html#pro-license"&gt;no legal implication at all&lt;/a&gt; - go use TRB for free if you don't feel like paying money for OSS.&lt;/p&gt;

&lt;p&gt;With all this "monetization" happening around Trailblazer, we will also make sure that all free and paid parts of the project grow adult and maintan an LTS - or long-term support - status. Those are good news to all you users out there having been scared to use gems of this project, not knowing whether or not they're being maintained, breaking code in the future or making your developers addicted to and then cutting off the supply chain. Trailblazer 2.1 onwards is LTS, and the last 1 ½ years of collaboration have proven that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming gems and plans
&lt;/h2&gt;

&lt;p&gt;We use Trello boards now to organize our work. Yeah, you can teach old dogs new tricks! Not only does it help to structure myself, it also manifests how many ideas and improvements we got lined up for the near future.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;COMPILE-TIME PERFORMANCE&lt;/strong&gt; While we're pretty good on the runtime performance, compiling all those activities takes more time than it could. We already started improving the compilation process and will ship updates in the next months. Optimization in this case is nothing crazy, just something I neglected while designing the framework.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://trailblazer.to/2.1/docs/test.html"&gt;TRAILBLAZER-TEST&lt;/a&gt; The official stable release is only weeks away bringing you a bunch of new assertions that drastically reduce coding effort for tests! Of course, Minitest and RSpec will both be supported.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/trailblazer/trailblazer-story"&gt;TRAILBLAZER-STORY&lt;/a&gt; will follow as it turned out to be inevitable for setting up application state for tests. Instead of fumbling around with factories and traits in your tests, you "tell a story" about what to create in which order, easily customizable, and all written using activities. Currently, I'm working on designing the interfaces and it's real fun!&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://trailblazer.to/2.1/docs/workflow.html"&gt;TRAILBLAZER-WORKFLOW&lt;/a&gt; is another dream 'o mine come true. It allows creating long-term processes (or state machines) based on BPMN diagrams that can be modeled using our editor. I'm pretty confident that you, coming from AASM or your own state machine engine, might find this approach highly interesting. We're using this gem in several commercial apps already, so the first release should be out this summer (I didn't say which hemisphere).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://trailblazer.to/2.1/docs/endpoint.html"&gt;TRAILBLAZER-ENDPOINT&lt;/a&gt; I've been promising for many years and it turned out I couldn't have fully designed it without the tools we do have now. Endpoint is the missing link between your routing (Rails, Hanami, ...) and the "operation" to be called. It provides standard behavior for all cases 404, 401, 403, etc and lets you hook in your own logic like Devise or Tyrant authentication, again, using TRB activity mechanics.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/trailblazer/tyrant"&gt;TYRANT&lt;/a&gt; is an authentication framework fully written in TRB activities, interacting with Rails, workflows and your database. It's highly customizable using the TRB mechanics and definitely less rough to work with than Devise. Looking forward to the release here!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;lt;%= img "/2.1/blog-ep-draft.png" %&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pXdc0YIf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j48kemurab0tjkfd56r7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pXdc0YIf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j48kemurab0tjkfd56r7.png" alt="A leaked snippet of the endpoint architectural design draft document, highly confidential." width="863" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;Writing documentation for the new website has been fun. Yes, fun! Mainly because we have a &lt;a href="https://github.com/apotonick/torture"&gt;helpful little framework&lt;/a&gt; behind the page rendering that pulls code snippets from real tests out of the actual gems!&lt;/p&gt;

&lt;p&gt;Many users have asked whether there will be an updated version of the &lt;a href="https://leanpub.com/trailblazer"&gt;Trailblazer book&lt;/a&gt;. While it's really cute and humbling to still have around $10 per month income from the now &lt;em&gt;free&lt;/em&gt; book, keep in mind it was published end of June 2016, that's pretty much four years ago!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PDOKZWF_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zq7hd4sz5ua49iqnogit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PDOKZWF_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zq7hd4sz5ua49iqnogit.png" alt="Image description" width="767" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What this means is: I better refrain from writing a new book and we rather focus on more and better docs. Writing examples and technical docs as a team has been working out pretty well recently and we will add more Trailblazer tutorials in the next half year. A comprehensive &lt;a href="https://github.com/trailblazer/blog-example"&gt;workflow tutorial&lt;/a&gt; demonstrating long-running processes and all things Trailblazer is also in the works! The legendary &lt;a href="https://github.com/trailblazer/cfp-app"&gt;&lt;code&gt;cfp-app&lt;/code&gt;&lt;/a&gt; will become a Rails-to-TRB refactoring tutorial.&lt;/p&gt;

&lt;p&gt;We decided against paid documentation, so all will be freely available on our shiny new website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Staying up-to-date
&lt;/h2&gt;

&lt;p&gt;All new publications, changes, ideas, conferences and parties will be posted regularly on our &lt;a href="https://fb.me/trailblazer_to"&gt;Facebook page&lt;/a&gt;. Yepp, that's true. We found it much easier to maintain and follow than most other social media platforms.&lt;/p&gt;

&lt;p&gt;And if you have anything to talk about with us, use the new &lt;a href="https://trailblazer.zulipchat.com/"&gt;Zulip chat&lt;/a&gt;. We're looking forward meeting you! See you on the trail!&lt;/p&gt;

&lt;p&gt;Nick Sutterer, &lt;a href="https://twitter.com/apotonick"&gt;@apotonick&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>We improved the trailblazer-rails gem!</title>
      <dc:creator>Nick Sutterer</dc:creator>
      <pubDate>Sun, 29 Aug 2021 11:07:35 +0000</pubDate>
      <link>https://forem.com/trailblazer/we-improved-th-trailblazer-rails-gem-4mk4</link>
      <guid>https://forem.com/trailblazer/we-improved-th-trailblazer-rails-gem-4mk4</guid>
      <description>&lt;p&gt;While working on an example Rails application for the &lt;a href="https://trailblazer.to/2.1/learn.html#learn-expert-books-building-your-own-authentication-library-with-trailblazer"&gt;new Trailblazer tutorial series&lt;/a&gt; I figured out that &lt;code&gt;trailblazer-rails&lt;/code&gt; is quite inconvenient to use when you need to customize calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controller-wide cells options
&lt;/h2&gt;

&lt;p&gt;When using the Cells gem for rendering views along with a layout cell, you formerly had to pass the &lt;code&gt;:layout&lt;/code&gt; option manually in every controller action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SongsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Song&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;New&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="no"&gt;App&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;People started monkey-patching internal methods from &lt;code&gt;trailblazer-rails&lt;/code&gt; to get the layout (and other options) automatically injected into every &lt;code&gt;#cell&lt;/code&gt; call. &lt;/p&gt;

&lt;p&gt;This is built-in now, just define &lt;code&gt;#options_for_cell&lt;/code&gt; on the controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SongsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;options_for_cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="no"&gt;App&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Layout&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hash returned from this method is passed as a defaults hash to &lt;code&gt;#cell&lt;/code&gt;. It's &lt;a href="https://trailblazer.to/2.1/docs/trailblazer.html#trailblazer-rails-cells-configuration"&gt;documented, too!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime variables for operations
&lt;/h2&gt;

&lt;p&gt;Another clumsiness I noticed was that it's impossible to inject variables into the operation context when using &lt;code&gt;#run&lt;/code&gt;. You can simply provide those as keyword arguments now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SongsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="no"&gt;Song&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="ss"&gt;current_user: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;cookie: &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add :current_user and :cookie to the ctx passed into the Song::Operation::Create operation. Here are &lt;a href="http://localhost:4000/2.1/docs/trailblazer.html#trailblazer-rails-run-runtime-variables"&gt;the docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trailblazer-Rails docs moved!
&lt;/h2&gt;

&lt;p&gt;In the process of the refactoring we also rewrote &lt;code&gt;trailblazer-rails&lt;/code&gt; docs. The &lt;a href="https://trailblazer.to/2.0/gems/trailblazer/2.0/rails.html"&gt;old docs&lt;/a&gt; are still available on the old website. Why would you need them, though, with &lt;a href="https://trailblazer.to/2.1/docs/trailblazer.html#trailblazer-rails"&gt;new docs waiting here&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;The new gem version is 2.2.0. 🔥&lt;/p&gt;

</description>
      <category>trailblazer</category>
    </item>
  </channel>
</rss>
