<?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: Julik Tarkhanov</title>
    <description>The latest articles on Forem by Julik Tarkhanov (@julik).</description>
    <link>https://forem.com/julik</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%2F116403%2F3d418e38-bd2a-44e8-8e70-1b5a620f904b.png</url>
      <title>Forem: Julik Tarkhanov</title>
      <link>https://forem.com/julik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/julik"/>
    <language>en</language>
    <item>
      <title>Improve Your Life with Long Error Messages</title>
      <dc:creator>Julik Tarkhanov</dc:creator>
      <pubDate>Wed, 29 Sep 2021 13:23:46 +0000</pubDate>
      <link>https://forem.com/appsignal/improve-your-life-with-long-error-messages-eon</link>
      <guid>https://forem.com/appsignal/improve-your-life-with-long-error-messages-eon</guid>
      <description>&lt;p&gt;It is incredibly helpful to quickly detect when a customer encounters an error and where the error originated from in your source code. Thankfully, this is where many interpreted languages shine. They always include a complete backtrace — the path where the call was made, which caused the error to be raised (or thrown).&lt;/p&gt;

&lt;p&gt;In AppSignal, any error alert will bring you to an incident detail page that shows you that backtrace. With a Github or Gitlab integration, you can immediately jump from an error to the place to solve it. &lt;/p&gt;

&lt;p&gt;Beyond that, however, we can make our errors more useful — with a little bit of prose. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Main Questions an Error Message Must Answer
&lt;/h2&gt;

&lt;p&gt;Folks who are old enough will likely remember this screen from the days of old (pre-UNIX) MacOS:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N7o60poQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-09/classic-macos-error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N7o60poQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-09/classic-macos-error.png" alt="Classic MacOS error screen with just an error code (ID)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It told us only one thing: your work is over, it's time to reboot the machine. But it took me some time to figure out why this error was so particularly frustrating — it would not answer any of the "big three" questions I believe an error must satisfy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What happened?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not "a catastrophic failure", but what exactly happened? Did the flux capacitor overcombobulate the particle instigator? Was a certain parameter above (or below) an acceptable value? Was a piece of incorrect user input to blame? The error should explain &lt;strong&gt;what&lt;/strong&gt; happened.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Where did it happen?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Was it QuickTime Player that crashed my machine? Was it this new USB mouse driver that I have just installed?&lt;/p&gt;

&lt;p&gt;The error should explain &lt;strong&gt;where&lt;/strong&gt; it originated. Not where it happened exactly in the code — the backtrace usually covers this part — but which high-level component it stems from. If your &lt;code&gt;WindowServer&lt;/code&gt; crashes on macOS, it is not that hard to understand that your entire UI has crashed because &lt;code&gt;WindowServer&lt;/code&gt; is the process that owns and manages everything on screen. But which application caused &lt;code&gt;WindowServer&lt;/code&gt; to crash?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What can I do to make the error go away?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Did invalid input cause it? Maybe we are trying to access some data that is, for example, no longer available, and we could catch this access attempt at a higher level of the stack? Or maybe we have a client that sends invalid input and always leads to this error? The error should do its best to tell us &lt;strong&gt;how to prevent it from happening again.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Item 4 on this list would be "automatic recovery" and it becomes relevant when multiple systems know of the same error semantics and try to recover from them in a distributed way. This can be a great topic for a future article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing Your Error for the Reader
&lt;/h2&gt;

&lt;p&gt;Take a look at this error as you would see it in AppSignal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LviCAZRe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-09/open-circuit-error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LviCAZRe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-09/open-circuit-error.png" alt="OpenCircuit error with no useful information"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It gets raised by a popular Ruby gem called &lt;a href="https://github.com/yammer/circuitbox"&gt;circuitbox&lt;/a&gt;. This gem implements the famous &lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker"&gt;Circuit Breaker&lt;/a&gt; pattern in Ruby.&lt;/p&gt;

&lt;p&gt;A short summary of what circuit breakers do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you call an external service (a web service API, database, or cloud storage system), your call might fail. If the service you are calling is down for a long time, all of your calls will fail, and you will be bombarding this service with calls and connection attempts.&lt;/li&gt;
&lt;li&gt;To allow your application to proceed regardless — and to avoid the service you are calling from getting overloaded — a so-called "circuit breaker" gets inserted between your application (the caller) and the service your application calls (the callee).&lt;/li&gt;
&lt;li&gt;Should N consecutive calls from the caller to the callee fail, the circuit breaker will "open" and forbid any subsequent calls from going through, failing these calls rapidly.&lt;/li&gt;
&lt;li&gt;After some time, the circuit breaker will attempt to "close" and allow calls through. If the callee has recovered by then, normal operation will resume.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you start using multiple services, having a circuit breaker becomes an absolute necessity. Sometimes, you need several circuit breakers — one for each service you call (or for each host you call, depending on the situation). A particular service that you call might be responsive, while another might be having a bad time.&lt;/p&gt;

&lt;p&gt;Circuitbox provides such a wrapper, and when a service you are calling isn't working (for some time), the circuit breaker is open. The gem raises an exception to indicate that the call isn't allowed to go through.&lt;/p&gt;

&lt;p&gt;Let's examine this error against the "big three" questions I mentioned above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What happened?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, a call to a service tripped a circuit breaker — &lt;em&gt;some&lt;/em&gt; circuit breaker. One could say that this satisfies the &lt;strong&gt;What?&lt;/strong&gt; question, although for me, it would be constructive to know what the originating error was. This error is &lt;em&gt;derived&lt;/em&gt; (or a &lt;em&gt;wrapper&lt;/em&gt;, if you will). It gets raised once some other error (the one causing the original service failure) happens for some time. That's the error I would expect to see in the message.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Where did it happen?&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The error does have a backtrace, and if we follow it closely, we find some clues as to &lt;strong&gt;where&lt;/strong&gt; it has originated. The application this was lifted from produces a backtrace of 34 levels for this error, and the spot where the circuit breaker is called from is located on line 4.&lt;/p&gt;

&lt;p&gt;It does not tell us which service we were calling or how long the service was inoperable. So it certainly does not fulfill the &lt;strong&gt;where&lt;/strong&gt; part and makes investigation harder. What would help is for the error to tell us in which &lt;em&gt;service&lt;/em&gt; it originated — what were we calling when it happened?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What can I do to make the error go away?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This error does not tell us what we can do to re-establish operation. At first glance, it would seem that it never could — a remote service does not respond, nobody knows when it could recover. So, &lt;strong&gt;preventing it from happening again&lt;/strong&gt; seems to be off the table.&lt;/p&gt;

&lt;p&gt;But think again: we could know, for example, that the service hasn't responded for 30 seconds, or that it has been down four times in the last 24 hours — which would enable us to contact the service owners and ask them about &lt;a href="https://www.accelerance.com/blog/4-reasons-why-sla-is-important"&gt;their SLOs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;More importantly, in this particular case, the error led to a developer attempting to do an incorrect bug fix. This is a fairly real danger for errors that seem severe but do not answer the three main questions. Bear in mind, when you are handling errors, you will often be in a stressful situation. It might be that your application is overloaded and you need to get it fixed quickly. Or it might be that your business depends on this transaction taking place and the circuit breaker is in the hot path of the transaction.&lt;/p&gt;

&lt;p&gt;In our specific situation, an engineer attempted to capture all calls and forbid outgoing HTTP requests to a specific hostname because they assumed that the circuit breaker covered calls to &lt;em&gt;all&lt;/em&gt; hosts handled by a code path. They did not know that the service name for the circuit breaker was specific to the hostname we were calling and that the circuit breaker tripped for external service A did not mean that it also tripped for service B. This is the price of error messages that are not informative enough.&lt;/p&gt;

&lt;p&gt;So, although a circuit breaker is practical, this error message could use some TLC. Let's see where we can make improvements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding More Context to the Error with Heredocs
&lt;/h2&gt;

&lt;p&gt;The first — and easiest — change we can make is to explain the meaning of this error. We start with just the class name. It can be very descriptive — for example, &lt;code&gt;ActiveRecord::RecordNotFound&lt;/code&gt; is a reasonably descriptive class name for an exception. &lt;code&gt;Circuitbox::CircuitOpen&lt;/code&gt; is pretty good in that regard — it does communicate the error condition to us.&lt;/p&gt;

&lt;p&gt;The next part to look at is the error message. When you initialize an object that subclasses &lt;code&gt;Exception&lt;/code&gt; in Ruby, you can pass a data string to include as the error message. The message is then available under the &lt;code&gt;Exception#message&lt;/code&gt; method:&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;raise&lt;/span&gt; &lt;span class="no"&gt;ServiceUnavailable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"The service you were trying to call was unavailable"&lt;/span&gt;&lt;span class="sb"&gt;`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's apply those patterns to our open circuit message:&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;raise&lt;/span&gt; &lt;span class="no"&gt;CircuitOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;EOM&lt;/span&gt;&lt;span class="sh"&gt;
  The circuit breaker is open. This means that the service you are calling has been unavailable or has been timing out
  for some time, and the circuit breaker has opened to prevent your application from slowing down and to protect the
  service you are calling from the thundering herd of requests once it recovers.
&lt;/span&gt;&lt;span class="no"&gt;EOM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets us a notch closer to fulfilling question 1 — telling &lt;em&gt;what&lt;/em&gt; happened. Now the error will be understandable for someone who doesn't know what the &lt;code&gt;circuitbox&lt;/code&gt; gem does or what its various exceptions mean. This speeds up debugging. But we can still take this further!&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking It a Notch Further with Dynamic Data
&lt;/h2&gt;

&lt;p&gt;For some inspiration, we can look at &lt;code&gt;ActiveRecord::RecordNotFound&lt;/code&gt; error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Couldn't find Batch with 'id'=14082
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error message communicates a few crucial bits of information, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which model we were trying to find (in this case,&lt;code&gt;Batch&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Which row we were looking for (in this case, the row with the &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;14082&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gets us way closer to fulfilling the second and third questions. It tells us where it happened (somewhere where we were looking for a &lt;code&gt;Batch&lt;/code&gt; with the &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;14082&lt;/code&gt;) but also, to an extent, what we can do to prevent it from happening again. We need to find where code similar to this gets executed in one of our backtrace lines or close to them:&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;Batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if we look at other examples from Rails, we can see some more useful error messages there. For example, the famous "whiny nil" extension tells you when you mistakenly try to obtain the &lt;code&gt;id&lt;/code&gt; of a &lt;code&gt;nil&lt;/code&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Called id for nil, which would mistakenly be 4 -- if you really wanted
the id of nil, use object_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are not limited to specifying the error messages with single-line strings, we can also use &lt;a href="https://www.rubyguides.com/2018/11/ruby-heredoc/"&gt;heredocs&lt;/a&gt; - a multiline string preformatted with line breaks. While quite a few people use heredocs in Ruby, there is one feature in them that often gets forgotten, namely, heredocs support the same string templating as double-quoted strings do! So, just as we can do this:&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;raise&lt;/span&gt; &lt;span class="no"&gt;CircuitOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Service &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@service&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was unresponsive"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can do the same in a longer chunk of text with a heredoc:&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;raise&lt;/span&gt; &lt;span class="no"&gt;CircuitOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;EOM&lt;/span&gt;&lt;span class="sh"&gt;
  The circuit breaker for the service &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; is open.

  This means that the service you are calling has been unavailable or has been timing out
  for some time, and the circuit breaker has opened to prevent your application from slowing down, as well as to protect the
  service you are calling from the thundering herd of requests once it recovers.
&lt;/span&gt;&lt;span class="no"&gt;EOM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how I use &lt;code&gt;inspect&lt;/code&gt; for the string templating so that the literal value of a variable is automatically displayed in quotes and is visually separated (delimited) from the rest of the message. This makes it easier to distinguish the dynamically generated part of the message from the written prose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing the Display with Structured Data
&lt;/h2&gt;

&lt;p&gt;Often, errors are caused by specific user input. Since we can include arbitrary programmatic strings in our error messages, nothing prevents us from having relevant user input in the error message (if we know that input can be useful). We can make this input fit for human consumption using something like &lt;code&gt;JSON.pretty_generate&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;raise&lt;/span&gt; &lt;span class="no"&gt;MyError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;EOM&lt;/span&gt;&lt;span class="sh"&gt;
  Fields were provided for validation which didn't match the schema. The input was:

  &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pretty_generate&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;:transaction_details&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, in one of WeTransfer's applications, we have a well-known failure condition that creates a specific pattern of invalid input. Since we know this error only emerges in the face of that input, we inject it into the error message. Here's what that error message in AppSignal then looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ly6KOXzo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-09/long-error-message.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ly6KOXzo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-09/long-error-message.png" alt="Long descriptive error message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Be careful with that pattern though: the data inside your exception message is subject to data protection laws, just like any other personally identifiable information.&lt;/p&gt;

&lt;p&gt;If you do template your input into the error messages, you might need to ensure that you are permitted to send it to services such as AppSignal. As the data is inside the error message, it can no longer be automatically anonymized or scrubbed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: The What, Where, and Why of Errors
&lt;/h2&gt;

&lt;p&gt;You want your errors to tell you &lt;strong&gt;what happens, where and why they happen&lt;/strong&gt;, in a form that requires as little investigation as possible.&lt;/p&gt;

&lt;p&gt;Long error messages are a great way to achieve that and provide you with considerable benefits at a small up-front cost. Just write that error message!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/julik"&gt;Julik Tarkhanov&lt;/a&gt; is a VFX artist gone rogue and Ruby. By day and night he toots the horn at the WeTransfer OSS repos like &lt;a href="https://github.com/WeTransfer/zip_tricks"&gt;ZipTricks&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WeTransfer is one of our long-time users. &lt;a href="https://www.appsignal.com/cases/wetransfer"&gt;Read more about the smart ways they've used AppSignal in our case study&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Fibers and Enumerators in Ruby: Turning Blocks Inside Out</title>
      <dc:creator>Julik Tarkhanov</dc:creator>
      <pubDate>Tue, 27 Nov 2018 14:01:36 +0000</pubDate>
      <link>https://forem.com/appsignal/fibers-and-enumerators-in-ruby-turning-blocks-inside-out-2d1a</link>
      <guid>https://forem.com/appsignal/fibers-and-enumerators-in-ruby-turning-blocks-inside-out-2d1a</guid>
      <description>&lt;p&gt;Ruby has various ways of performing iteration—loops, blocks and enumerators. Most Ruby programmers are at least familiar with loops and blocks but &lt;code&gt;Enumerator&lt;/code&gt; and &lt;code&gt;Fiber&lt;/code&gt; often stay in the dark. In this edition of Ruby Magic, guest author Julik shines a light on &lt;code&gt;Enumerable&lt;/code&gt; and &lt;code&gt;Fiber&lt;/code&gt; to explain flow controlling enumerables and turning blocks inside out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Suspending Blocks and Chained Iteration
&lt;/h2&gt;

&lt;p&gt;We've discussed &lt;a href="https://blog.appsignal.com/2018/05/29/ruby-magic-enumerable-and-enumerator.html" rel="noopener noreferrer"&gt;Enumerator&lt;/a&gt; in a previous edition of Ruby Magic, where we described how to return an &lt;code&gt;Enumerator&lt;/code&gt; from your own &lt;code&gt;#each&lt;/code&gt; method and what it can be used for. An even broader use case for &lt;code&gt;Enumerator&lt;/code&gt; and &lt;code&gt;Fiber&lt;/code&gt; is that they can "suspend a block" mid-flight. Not just the block given to &lt;code&gt;#each&lt;/code&gt; or the entire call to &lt;code&gt;#each&lt;/code&gt;, but any block!&lt;/p&gt;

&lt;p&gt;This is a very powerful construct, which can be used to implement shims for methods that work by using blocks as a bridge to callers that expect sequential calls instead of taking a block. For example, imagine we want to open a database handle and read each item that we have retrieved:&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_each_row_of_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql_stmt&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;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;row&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 API is great since it will potentially perform all kinds of cleanup for us when the block is terminated. However, some consumers might want to work with the database in this way:&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="vi"&gt;@cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;

&lt;span class="c1"&gt;# later:&lt;/span&gt;
&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_row&lt;/span&gt;
&lt;span class="n"&gt;send_row_to_event_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, it means we want to "suspend" the execution of the block "just for now" and carry on later within the block. Thus, the caller takes over the flow control instead of it being in the hands of the callee (the method performing the block).&lt;/p&gt;

&lt;h2&gt;
  
  
  Chaining Iterators
&lt;/h2&gt;

&lt;p&gt;One of the most common uses of this pattern is chaining multiple iterators together. When we do so, the methods we are used to for iteration (like &lt;code&gt;#each&lt;/code&gt;), return an Enumerator object instead, which we can use to "grab" the values that the block sends us using the &lt;code&gt;yield&lt;/code&gt; statement:&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;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="n"&gt;each_enum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; &amp;lt;Enumerator...&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The enumerators can then be &lt;em&gt;chained&lt;/em&gt; which allows us to perform operations like "any iteration but with the index". In this example, we're calling &lt;code&gt;#map&lt;/code&gt; on a range to get an &lt;code&gt;Enumerable&lt;/code&gt; object. We then chain &lt;code&gt;#with_index&lt;/code&gt; to iterate over the range with an index:&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_index&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;element_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;element_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [[1, 0], [2, 1], [3, 2]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be very useful, especially if your system uses events. Ruby provides a built-in method for wrapping any method with an Enumerator generator, which allows us to accomplish exactly this. Imagine we want to "pull" rows one by one from our &lt;code&gt;with_each_row_of_result&lt;/code&gt;, instead of the method yielding them to us.&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="vi"&gt;@cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_enum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:with_each_row_of_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql_stmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;schedule_for_later&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;
    &lt;span class="n"&gt;send_row_to_event_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;StopIteration&lt;/span&gt; &lt;span class="c1"&gt;# the block has ended and the cursor is empty, the cleanup has taken place&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;If we were to implement this ourselves, this is how it would likely come about:&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;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Enumerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&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;yielder&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_each_row_of_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql_stmt&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;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;yielder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;row&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;h2&gt;
  
  
  Turning Blocks Inside Out
&lt;/h2&gt;

&lt;p&gt;Rails allows us to assign the response body to also be an Enumerator. It will call &lt;code&gt;next&lt;/code&gt; on the Enumerator we assign as the response body and expect the returned value to be a string—which will be written out into the Rack response. For example, we can return a call to the &lt;code&gt;#each&lt;/code&gt; method of a Range as a Rails response body:&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;MyController&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;index&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="s1"&gt;'z'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&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;This is what I call &lt;em&gt;turning a block inside out.&lt;/em&gt; In essence, it is a control flow helper that allows us to "freeze time" in a block (or a loop, which is also a block in Ruby) mid-flight.&lt;/p&gt;

&lt;p&gt;However, Enumerators have a limiting property that makes them slightly less useful. Imagine we want to do something like this:&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;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'output.tmp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'wb'&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;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# Yield file for writing, continuously&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&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;Let's wrap it with an enumerator, and write into it&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;writer_enum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_enum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'output.tmp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'wb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;
&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;more_data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything works great. However, there is a hitch—how do we tell the enumerator that we are done writing, so that it can "finish" the block, close the file and exit? This will perform a number of important steps—for example, resource cleanup (the file will be closed), as well as ensuring all the buffered writes are flushed to disk. We do have access to the &lt;code&gt;File&lt;/code&gt; object, and we can close it ourselves, but we would like the enumerator to manage the closing for us; we have to let the enumerator proceed past the block.&lt;/p&gt;

&lt;p&gt;Another hurdle is that sometimes we want to pass arguments of what is happening within the suspended block. Imagine we have a block-accepting method with the following semantics:&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;write_file_through_encryptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&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;writable&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;writable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"Some data"&lt;/span&gt;
  &lt;span class="n"&gt;writable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"Some more data"&lt;/span&gt;
  &lt;span class="n"&gt;writable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"Even more data"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but in our calling code we want to use it like this:&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;writable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;write_file_through_encryptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;writable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"Some data"&lt;/span&gt;
&lt;span class="c1"&gt;# ...later on&lt;/span&gt;
&lt;span class="n"&gt;writable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"Some more data"&lt;/span&gt;
&lt;span class="n"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally, we would wrap our method call into some structure that would permit us the following trick:&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;write_file_through_encryptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&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;writable&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;yield_and_wait_for_next_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Then we somehow break out of this loop to let the block complete&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;What if we were to wrap our writes like this?&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;deferred_writable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;write_file_through_encryptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;deferred_writable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Some data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;deferred_writable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Some more data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;deferred_writable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Even more data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;deferred_writable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:terminate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we will use the &lt;code&gt;:terminate&lt;/code&gt; as a magic value that will tell our method that it can finish the block and return. &lt;strong&gt;This&lt;/strong&gt; is where &lt;code&gt;Enumerator&lt;/code&gt; won't really help us because we can't pass any arguments to &lt;code&gt;Enumerator#next&lt;/code&gt;. If we could, we would be able to do:&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;deferred_writable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;write_file_through_encryptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;deferred_writable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Some data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;deferred_writable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:terminate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enter Ruby's Fibers
&lt;/h2&gt;

&lt;p&gt;This is exactly what Fibers permit. A Fiber allows you to &lt;em&gt;accept arguments on each reentry&lt;/em&gt;, so we can implement our wrapper 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;deferred_writable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&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;data_to_write_or_termination&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;write_file_through_encryptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&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;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
     &lt;span class="c1"&gt;# Here we enter the block context of the fiber, reentry will be to the start of this block&lt;/span&gt;
    &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="c1"&gt;# When we call Fiber.yield our fiber will be suspended—we won't reach the&lt;/span&gt;
      &lt;span class="c1"&gt;# "data_to_write_or_termination = " assignment before our fiber gets resumed&lt;/span&gt;
      &lt;span class="n"&gt;data_to_write_or_termination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&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;This is how it works: When you first call &lt;code&gt;.resume&lt;/code&gt; on your &lt;code&gt;deferred_writable&lt;/code&gt;, it enters the fiber and goes all the way to the first &lt;code&gt;Fiber.yield&lt;/code&gt; statement or to the end of the outermost Fiber block, whichever comes first. When you call &lt;code&gt;Fiber.yield&lt;/code&gt;, it gives you back control. Remember the Enumerator? The block is going to be &lt;em&gt;suspended&lt;/em&gt;, and the next time you call &lt;code&gt;.resume&lt;/code&gt;, the argument to &lt;code&gt;resume&lt;/code&gt; becomes the new &lt;code&gt;data_to_write&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;deferred_writes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&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;data_to_write&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vg"&gt;$stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Received &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;data_to_write&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to work with"&lt;/span&gt;
    &lt;span class="n"&gt;data_to_write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; #&amp;lt;Fiber:0x007f9f531783e8&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;deferred_writes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Received Hello to work with&lt;/span&gt;
&lt;span class="n"&gt;deferred_writes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Goodbye"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Received Goodbye to work with&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, within the Fiber, the code flow is &lt;em&gt;started&lt;/em&gt; on the first call to &lt;code&gt;Fiber#resume&lt;/code&gt;, suspended at the first call to &lt;code&gt;Fiber.yield&lt;/code&gt;, and then &lt;em&gt;continued&lt;/em&gt; on subsequent calls to &lt;code&gt;Fiber#resume&lt;/code&gt;, with the return value of &lt;code&gt;Fiber.yield&lt;/code&gt; being the arguments to &lt;code&gt;resume&lt;/code&gt;. The code continues running from the point where &lt;code&gt;Fiber.yield&lt;/code&gt; was last called.&lt;/p&gt;

&lt;p&gt;This is a bit of a quirk of Fibers in that the initial arguments to the fiber will be passed to you as the block arguments, not via the return value of &lt;code&gt;Fiber.yield&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that in mind, we know that by passing a special argument to &lt;code&gt;resume&lt;/code&gt;, we can decide within the Fiber whether we should stop or not. Let's try that:&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;deferred_writes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&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;data_to_write&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vg"&gt;$stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Received &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;data_to_write&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to work with"&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data_to_write&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:terminate&lt;/span&gt; &lt;span class="c1"&gt;# Break out of the loop, or...&lt;/span&gt;
    &lt;span class="n"&gt;write_to_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_to_write&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# ...write to the output&lt;/span&gt;
    &lt;span class="n"&gt;data_to_write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt;          &lt;span class="c1"&gt;# suspend ourselves and wait for the next `resume`&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# We end up here if we break out of the loop above. There is no Fiber.yield&lt;/span&gt;
  &lt;span class="c1"&gt;# statement anywhere, so the Fiber will terminate and become "dead".&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;deferred_writes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Received Hello to work with&lt;/span&gt;
&lt;span class="n"&gt;deferred_writes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Goodbye"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Received Goodbye to work with&lt;/span&gt;
&lt;span class="n"&gt;deferred_writes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:terminate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;deferred_writes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Some more data after close"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# FiberError: dead fiber called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a number of situations where these facilities can be very useful. Since a Fiber contains a suspended block of code that can be manually resumed, Fibers can be used for implementing event reactors and for dealing with concurrent operations within a single thread. They are lightweight, so you can implement a server using Fibers by assigning a single client to a single Fiber and switching between these Fiber objects as necessary.&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;client_fiber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&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;socket&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;received_from_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_nonblock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="n"&gt;sent_to_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_nonblock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt; &lt;span class="c1"&gt;# Return control back to the caller and wait for it to call 'resume' on us&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;client_fibers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;client_fiber&lt;/span&gt;

&lt;span class="c1"&gt;# and then in your main webserver loop&lt;/span&gt;
&lt;span class="n"&gt;client_fibers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;client_fiber&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;client_fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt; &lt;span class="c1"&gt;# Receive data from the client if any, and send it an OK&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby has an additional standard library called &lt;code&gt;fiber&lt;/code&gt; which allows you to explicitly transfer control from one fiber to another, which can be a bonus facility for these uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controlling Data Emission Rates
&lt;/h2&gt;

&lt;p&gt;Another great use for fibers and enumerators can arise when you want to be able to control the rate at which a Ruby block emits data. For example, in &lt;a href="https://github.com/WeTransfer/zip_tricks" rel="noopener noreferrer"&gt;zip_tricks&lt;/a&gt; we support the following block use as the primary way of using the library:&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;ZipTricks&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Streamer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_io&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;z&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_deflated_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"big.csv"&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;destination&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;col&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
     &lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;column&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;We therefore allow "push" control on the part of the code that creates the ZIP archive, and it is impossible to control how much data it outputs and how often. If we want to write our ZIP in chunks of, say, 5 MB—which would be a limitation on AWS S3 object storage—we would have to create a custom &lt;code&gt;output_io&lt;/code&gt; object which would somehow "refuse" to accept &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; method calls when the segment needs to be split off into an S3 multipart part. We can, however, invert the control and make it "pull". We will still use the same block for writing our big CSV file, but we will be resuming and halting it based on the output it provides. We therefore make the following use possible:&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;output_enum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ZipTricks&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Streamer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;output_enum&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;z&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_deflated_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"big.csv"&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;destination&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;col&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
     &lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;column&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;span class="c1"&gt;# At this point nothing has been generated or written yet&lt;/span&gt;
&lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output_enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="c1"&gt;# Create an Enumerator&lt;/span&gt;
&lt;span class="n"&gt;bin_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt; &lt;span class="c1"&gt;# Let the block generate some binary data and then suspend it&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bin_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Our block is suspended and waiting for the next invocation of `next`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to control at which rate our ZIP file generator emits data.&lt;/p&gt;

&lt;p&gt;Enumerator and Fiber are, therefore, a &lt;strong&gt;control flow mechanism&lt;/strong&gt; for turning "push" blocks into "pull" objects that accept method calls.&lt;/p&gt;

&lt;p&gt;There is only one pitfall with Fibers and Enumerators—if you have something like &lt;code&gt;ensure&lt;/code&gt; in your block, or something that needs to be done after the block completes, it is now up to the caller to call you enough times. In a way, it is comparable to the constraints you have when using Promises in JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This concludes our look into flow-controlled enumerables in Ruby. Along the way, Julik shone light on the similarities and differences between the &lt;code&gt;Enumerable&lt;/code&gt; and &lt;code&gt;Fiber&lt;/code&gt; classes, and dove into examples where caller determined the flow of data.  We’ve also learned about &lt;code&gt;Fiber&lt;/code&gt;’s additional magic to allow passing arguments on each block reentry. Happy flow-controlling!&lt;/p&gt;

&lt;p&gt;To get a steady dose of magic, &lt;a href="https://appsignal.com/ruby-magic" rel="noopener noreferrer"&gt;subscribe&lt;/a&gt; to Ruby Magic and we'll deliver our monthly edition straight to your inbox.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Guest Author &lt;a href="https://www.imdb.com/name/nm3127224/" rel="noopener noreferrer"&gt;Julik Tarkhanov&lt;/a&gt; is a VFX artist gone rogue and Ruby. By day and night he toots the horn at the WeTransfer OSS repos like &lt;a href="https://github.com/WeTransfer/zip_tricks" rel="noopener noreferrer"&gt;ZipTricks&lt;/a&gt; and &lt;a href="https://github.com/WeTransfer/image_vise" rel="noopener noreferrer"&gt;ImageVise&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
