<?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>DEV Community: Abiodun Olowode</title>
    <description>The latest articles on DEV Community by Abiodun Olowode (@tripplea).</description>
    <link>https://dev.to/tripplea</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%2F344893%2Fd048e2cf-8be4-4f36-90d4-e804ad80b501.jpeg</url>
      <title>DEV Community: Abiodun Olowode</title>
      <link>https://dev.to/tripplea</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tripplea"/>
    <language>en</language>
    <item>
      <title>Ruby’s hidden gems: Sorbet</title>
      <dc:creator>Abiodun Olowode</dc:creator>
      <pubDate>Wed, 02 Oct 2024 12:24:57 +0000</pubDate>
      <link>https://dev.to/appsignal/rubys-hidden-gems-sorbet-3k7e</link>
      <guid>https://dev.to/appsignal/rubys-hidden-gems-sorbet-3k7e</guid>
      <description>&lt;p&gt;The debate between static and dynamically typed languages has long been a subject of contention among developers. Each approach offers its own set of advantages and disadvantages, significantly influencing the software development process.&lt;/p&gt;

&lt;p&gt;Dynamically typed languages like Ruby provide flexibility by allowing variables to be declared without corresponding types. This approach fosters rapid development and promotes an agile process.&lt;/p&gt;

&lt;p&gt;Yet, the absence of strict typing can lead to challenges, such as runtime errors that may be harder to debug and maintain in larger codebases. For example, in a dynamically typed language like Ruby, attempting to divide an array by a string only results in an error when the code is executed, making it potentially harder to identify and fix such issues.&lt;/p&gt;

&lt;p&gt;In this article, we explore Sorbet, a type checker for Ruby, which addresses the challenges of dynamic typing in Ruby, enhancing code reliability and maintainability without sacrificing the language's flexibility and expressiveness.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Sorbet for Ruby?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/sorbet/sorbet" rel="noopener noreferrer"&gt;Sorbet&lt;/a&gt;, implemented in C++, is a Ruby gem designed to harmonize the dynamism of Ruby with the reliability and predictability of static typing. As Ruby projects scale in size and complexity, maintaining code quality and preventing errors becomes increasingly challenging. A primary culprit is the absence of static typing, which often necessitates heavy reliance on extensive testing and runtime checks to ensure code correctness, resulting in more frequent bugs slipping into production.&lt;/p&gt;

&lt;p&gt;Developed by Stripe, Sorbet seeks to tackle these challenges by introducing static typing to Ruby. It functions as a type checker and gradual type system for Ruby, enabling the annotation of code with type information and the detection of errors at compile time (rather than runtime).&lt;/p&gt;

&lt;p&gt;Key features and benefits of Sorbet include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type Declaration:&lt;/strong&gt; Sorbet allows for the declaration of types for variables, method parameters, and return values. This facilitates early error detection and enhances code readability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradual Typing:&lt;/strong&gt; Unlike statically typed languages where typing is mandatory, Sorbet permits the incremental introduction of type annotations. This means existing Ruby codebases can transition gradually to a statically typed workflow without needing a complete rewrite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instantaneous Feedback:&lt;/strong&gt; During development, Sorbet provides instant insights into method definitions and usage. Clicking on a method reveals its definition, while hovering over it displays information about its input and return types. This real-time feedback accelerates development and enhances code navigation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Inference:&lt;/strong&gt; Sorbet employs a straightforward type inference algorithm to deduce types where explicit annotations are absent. This minimizes the need for manual type annotations and facilitates the adoption of static typing in Ruby projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooling Integration:&lt;/strong&gt; Sorbet seamlessly integrates with popular Ruby development tools, including editors, IDEs, and build systems. This ensures a seamless developer experience and promotes the adoption of static typing practices within Ruby development workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started with Sorbet
&lt;/h2&gt;

&lt;p&gt;Before diving into Sorbet's integration into your codebase, it's helpful to explore a Sorbet playground, which provides a sandbox environment to experiment with Sorbet's features. &lt;a href="https://sorbet.run/#%23%20typed%3A%20true%0Arequire%20'sorbet-runtime'%0A%0Aclass%20A%0A%20%20extend%20T%3A%3ASig%0A%0A%20%20sig%20%7Bparams%28x%3A%20Integer%29.returns%28String%29%7D%0A%20%20def%20bar%28x%29%0A%20%20%20%20x.to_s%0A%20%20end%0Aend%0A%0Adef%20main%0A%20%20A.new.barr%2891%29%20%20%20%23%20error%3A%20Typo!%0A%20%20A.new.bar%28%2291%22%29%20%20%23%20error%3A%20Type%20mismatch!%0Aend" rel="noopener noreferrer"&gt;Here's a Sorbet playground&lt;/a&gt; that allows you to tweak code and see how Sorbet responds to various changes.&lt;/p&gt;

&lt;p&gt;To understand Sorbet and its functionality, before applying it to an existing codebase, let's start a new Ruby project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new directory:
Begin by creating a new directory named &lt;code&gt;sorbet-test&lt;/code&gt; where we'll set up our Sorbet testing environment.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;sorbet-test
&lt;span class="nb"&gt;cd &lt;/span&gt;sorbet-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set up the Gemfile:
If you don't already have a Gemfile in your sorbet-test directory, create one using the following command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;Gemfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, add the Sorbet gem to your Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s1"&gt;'https://rubygems.org'&lt;/span&gt;

gem &lt;span class="s1"&gt;'sorbet'&lt;/span&gt;, group: :development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install Sorbet:
Install the Sorbet gem using Bundler:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Verify the installation:
Check that Sorbet is installed correctly by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;exec &lt;/span&gt;srb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should give an output that indicates that no &lt;code&gt;sorbet/&lt;/code&gt; directory was found and prompts us to initialize our directory with &lt;code&gt;'srb init'&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Ruby file:
Now, let's create a new Ruby file named &lt;code&gt;person.rb&lt;/code&gt; in our &lt;code&gt;sorbet-test&lt;/code&gt; directory, and add the following code:
&lt;/li&gt;
&lt;/ul&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;Person&lt;/span&gt;
 &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_name&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nam&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'John'&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'This person is John'&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'This person is not John'&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;ul&gt;
&lt;li&gt;Initialize Sorbet:
Initialize your directory with Sorbet by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;srb init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a sorbet folder with an &lt;code&gt;rbi&lt;/code&gt; subfolder in our directory. You'll also find the &lt;code&gt;# typed: false&lt;/code&gt; sigil appended to the &lt;code&gt;person.rb&lt;/code&gt; file.&lt;br&gt;
This sigil indicates to Sorbet what errors to report and which to silence (&lt;code&gt;# typed: ignore&lt;/code&gt; being the least, as it causes Sorbet to ignore the file, and &lt;code&gt;# typed: strong&lt;/code&gt; being the strictest, as all errors in this file are reported). &lt;a href="https://sorbet.org/docs/static" rel="noopener noreferrer"&gt;Read more detailed information about these sigils&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update typed sigil:
Update the &lt;code&gt;# typed&lt;/code&gt; sigil in the &lt;code&gt;person.rb&lt;/code&gt; file to &lt;code&gt;true&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# typed: true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Run Sorbet:
Finally, run Sorbet with &lt;code&gt;bundle exec srb&lt;/code&gt; to check your Ruby file for type errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following error should ensue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;person.rb:10: Method nam does not exist on Person https://srb.help/7003
    10 |    &lt;span class="k"&gt;if &lt;/span&gt;nam &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'John'&lt;/span&gt;
               ^^^
  Did you mean name? Use &lt;span class="nt"&gt;-a&lt;/span&gt; to autocorrect
    person.rb:10: Replace with name
    10 |    &lt;span class="k"&gt;if &lt;/span&gt;nam &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'John'&lt;/span&gt;
               ^^^
    person.rb:3: Defined here
     3 | attr_accessor :name
         ^^^^^^^^^^^^^^^^^^^
Errors: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correcting this typo to &lt;code&gt;name&lt;/code&gt; and rerunning the command should output the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;No errors! Great job.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Type Mismatches in Sorbet
&lt;/h3&gt;

&lt;p&gt;Let's see another example of how Sorbet detects a type mismatch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add this new method to &lt;code&gt;person.rb&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ul&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;name_length&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Within the file, call this method as such:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;name_length&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run Sorbet with &lt;code&gt;bundle exec srb&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We do not get any errors. However, when we run this code, we get the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/.../person.rb:18:in &lt;span class="sb"&gt;`&lt;/span&gt;name_length&lt;span class="s1"&gt;': undefined method `length'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;8:Integer &lt;span class="o"&gt;(&lt;/span&gt;NoMethodError&lt;span class="o"&gt;)&lt;/span&gt;

    puts name.length
             ^^^^^^^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sorbet should ideally catch this error, but it fails because it lacks the necessary information about the expected &lt;code&gt;name&lt;/code&gt; type. To provide Sorbet with this information, we need to add type signatures to our methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Type Signatures
&lt;/h3&gt;

&lt;p&gt;We can enable type signatures in our code by extending the &lt;code&gt;T::Sig&lt;/code&gt; module and adding signatures to methods. Let's add a signature to the initialize 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;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Sig&lt;/span&gt;

  &lt;span class="n"&gt;sig&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;name: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;void&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&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 signature is basically telling Sorbet that this method takes a parameter &lt;code&gt;name&lt;/code&gt; of type &lt;code&gt;String&lt;/code&gt; and returns nothing.&lt;/p&gt;

&lt;p&gt;Now, running &lt;code&gt;bundle exec srb&lt;/code&gt; outputs the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;person.rb:24: Expected String but found Integer&lt;span class="o"&gt;(&lt;/span&gt;8&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;argument name https://srb.help/7002
    24 |Person.new&lt;span class="o"&gt;(&lt;/span&gt;8&lt;span class="o"&gt;)&lt;/span&gt;.name_length
                   ^
  Expected String &lt;span class="k"&gt;for &lt;/span&gt;argument name of method Person#initialize:
    person.rb:6:
     6 |  sig &lt;span class="o"&gt;{&lt;/span&gt;params&lt;span class="o"&gt;(&lt;/span&gt;name: String&lt;span class="o"&gt;)&lt;/span&gt;.void&lt;span class="o"&gt;}&lt;/span&gt;
                      ^^^^
  Got Integer&lt;span class="o"&gt;(&lt;/span&gt;8&lt;span class="o"&gt;)&lt;/span&gt; originating from:
    person.rb:24:
    24 |Person.new&lt;span class="o"&gt;(&lt;/span&gt;8&lt;span class="o"&gt;)&lt;/span&gt;.name_length
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sorbet successfully detects the error due to the addition of type signatures. Updating the sigil to &lt;code&gt;# typed: strict&lt;/code&gt;, requires all methods to possess a type signature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sorbet Runtime Support
&lt;/h2&gt;

&lt;p&gt;Although Sorbet is able to carry out its checks successfully, we're not able to run this piece of code. We get the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/.../person.rb:3:in &lt;span class="sb"&gt;`&lt;/span&gt;&amp;lt;class:Person&amp;gt;&lt;span class="s1"&gt;': uninitialized constant Person::T (NameError)

  extend T::Sig
          ^^^^^
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error indicates that Sorbet's type annotations, represented by the &lt;code&gt;T&lt;/code&gt; module, were not found. Our piece of code requires access to the necessary runtime support for Sorbet's type annotations, which allows it to run correctly alongside Sorbet's static type checking.&lt;/p&gt;

&lt;p&gt;To resolve this, we need to add the &lt;code&gt;sorbet-runtime&lt;/code&gt; gem to our project:&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;gem&lt;/span&gt; &lt;span class="s1"&gt;'sorbet-runtime'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;group: :development&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running &lt;code&gt;bundle install&lt;/code&gt;, require &lt;code&gt;sorbet-runtime&lt;/code&gt; in our file:&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;# person.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'sorbet-runtime'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;sorbet-runtime&lt;/code&gt; loaded, our code runs correctly, and Sorbet effectively enforces type safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sorbet IDE Integration
&lt;/h2&gt;

&lt;p&gt;Running &lt;code&gt;bundle exec srb tc&lt;/code&gt; frequently to typecheck code can be tedious and time-consuming. To streamline the development process, Sorbet offers a VSCode extension that provides various features to enhance your coding experience. These features include autocomplete, jump to definition, type information and documentation on hover, sig suggestion, quick fixes, autocorrection, static error displays, and more. &lt;a href="https://sorbet.org/docs/vscode" rel="noopener noreferrer"&gt;Find out more about how to install and use the VSCode extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For developers using editors other than VS Code, Sorbet does not have official integrations or extensions. However, some editors support the Language Server Protocol (LSP), allowing language servers like Sorbet to integrate with them. JetBrains IDEs, including IntelliJ IDEA, PyCharm, and RubyMine, have built-in support for the LSP. Additionally, there are plugins available for Sublime Text and Atom that enable LSP support. While these solutions may not offer the same level of integration and features as the dedicated Sorbet extension for VSCode, they can still provide basic functionality, such as syntax highlighting, autocompletion, and error checking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring Tapioca
&lt;/h2&gt;

&lt;p&gt;We've gone through the process of adding types to a new project. However, what about projects already in existence? Typically, these projects come with pre-installed gems, and these third-party services include methods invoked within our project. How do we guarantee that we're passing the correct types of parameters to these methods, or that they're being invoked on appropriate types? The answer to this is by using &lt;a href="https://github.com/Shopify/tapioca" rel="noopener noreferrer"&gt;Tapioca&lt;/a&gt;. To understand what Tapioca does, it's important to first understand what Ruby Interface (RBI) files are.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are Ruby Interface (RBI) Files?
&lt;/h3&gt;

&lt;p&gt;RBI means Ruby Interface, and RBI files serve as interface files that provide documentation on type information for Ruby code. They contain declarations of types, method signatures, and other type-related metadata. They can either be created manually or autogenerated.&lt;/p&gt;

&lt;p&gt;While Sorbet is capable of inferring types to some extent, there are scenarios for which Sorbet lacks sufficient information. For example, when dealing with complex inheritance hierarchies, method overrides, external dependencies, or dynamic method calls, Sorbet may struggle to infer types accurately without explicit type annotations provided by RBI files.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Does Tapioca Do?
&lt;/h3&gt;

&lt;p&gt;Tapioca, a Ruby gem developed by Shopify, streamlines the creation of RBI files, which are essential for implementing gradual typing in Ruby projects. These RBI files can cover not only the gems used within the application but also the methods available within Rails, along with other DSLs and metaprogramming paradigms.&lt;/p&gt;

&lt;p&gt;To integrate Tapioca into an existing Rails project for use with Sorbet, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the Tapioca gem to your Gemfile, alongside existing gems such as &lt;code&gt;sorbet&lt;/code&gt; and &lt;code&gt;sorbet-runtime&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'tapioca'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;require: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;group: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:development&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Initialize the project for use with Sorbet by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;exec &lt;/span&gt;tapioca init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates a Sorbet folder structure within your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;├── config             &lt;span class="c"&gt;# Default options to be passed to Sorbet on every run&lt;/span&gt;
└── rbi/
  ├── annotations/     &lt;span class="c"&gt;# Type definitions pulled from the rbi-central repository&lt;/span&gt;
  ├── gems/            &lt;span class="c"&gt;# Autogenerated type definitions for your gems&lt;/span&gt;
  └── todo.rbi         &lt;span class="c"&gt;# Constants which were still missing after RBI generation&lt;/span&gt;
└── tapioca/
  ├── config.yml       &lt;span class="c"&gt;# Default options to be passed to Tapioca&lt;/span&gt;
  └── require.rb       &lt;span class="c"&gt;# A file where you can make requires from gems that might be needed for gem RBI generation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The terminal output provides valuable guidance on generating type definitions for DSLs in your application, performing type checking, and upgrading files from &lt;code&gt;# typed: false&lt;/code&gt; to &lt;code&gt;# typed: true&lt;/code&gt; using tools like &lt;a href="https://github.com/Shopify/spoom" rel="noopener noreferrer"&gt;Spoom&lt;/a&gt;. Take some time to review this information.&lt;/p&gt;

&lt;p&gt;Let's illustrate with a simple example involving a &lt;code&gt;Person&lt;/code&gt; model in your project. After adding &lt;code&gt;# typed: true&lt;/code&gt; to a file, attempting to call &lt;code&gt;Person.all&lt;/code&gt; triggers an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Method &lt;span class="sb"&gt;`&lt;/span&gt;all&lt;span class="sb"&gt;`&lt;/span&gt; does not exist on &lt;span class="sb"&gt;`&lt;/span&gt;T.class_of&lt;span class="o"&gt;(&lt;/span&gt;Person&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the Sorbet extension in your IDE, &lt;code&gt;Person.all&lt;/code&gt; is highlighted in red, indicating an error. This occurs because Sorbet lacks awareness of &lt;code&gt;Person&lt;/code&gt; as a model or the available Rails methods for the &lt;code&gt;Person&lt;/code&gt; class. To resolve this, we need to generate an RBI file for the &lt;code&gt;Person&lt;/code&gt; model. RBI files can be generated using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/tapioca dsl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Executing this command generates several RBI files, including &lt;code&gt;person.rbi&lt;/code&gt;, which defines methods available to the &lt;code&gt;Person&lt;/code&gt; class:&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;# typed: true&lt;/span&gt;

&lt;span class="c1"&gt;# DO NOT EDIT MANUALLY&lt;/span&gt;
&lt;span class="c1"&gt;# This is an autogenerated file for dynamic methods in `Book`.&lt;/span&gt;
&lt;span class="c1"&gt;# Please instead update this file by running `bin/tapioca dsl Book`.&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;GeneratedAttributeMethods&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;CommonRelationMethods&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;GeneratedRelationMethods&lt;/span&gt;

  &lt;span class="c1"&gt;# Other methods related to Person class are stated here...&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;GeneratedAssociationRelationMethods&lt;/span&gt;
    &lt;span class="n"&gt;sig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PrivateAssociationRelation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Additional methods related to associations are stated here...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# More methods related to Person class are stated here...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the RBI file in place, the error disappears, as Sorbet now recognizes all methods available to &lt;code&gt;Person&lt;/code&gt;. Any additions or changes to methods in &lt;code&gt;Person&lt;/code&gt; can be reflected in the RBI file by rerunning the &lt;code&gt;bin/tapioca dsl&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges with Sorbet
&lt;/h2&gt;

&lt;p&gt;Gradually adding types to a codebase often leads to a mixed scenario where some parts are typed, and others aren't. Sorbet helps by detecting errors during runtime with its &lt;code&gt;sorbet-runtime&lt;/code&gt; component. This is particularly valuable in identifying situations where types might be incorrect, such as when passing different types from an untyped section of code while expecting a specific type in a typed portion.&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;# typed: true&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'sorbet-runtime'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Sig&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;happy?&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;sig&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;statement: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;the_truth?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;the_truth?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;happy?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;bundle exec srb&lt;/code&gt; doesn't raise an error here because it's unaware of the &lt;code&gt;happy?&lt;/code&gt; return type, which prevents it from accurately assessing whether the passed value is not a string. However, Sorbet runtime detects this issue and raises an error during runtime without the execution of the &lt;code&gt;the_truth?&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Parameter &lt;span class="s1"&gt;'statement'&lt;/span&gt;: Expected &lt;span class="nb"&gt;type &lt;/span&gt;String, got &lt;span class="nb"&gt;type &lt;/span&gt;TrueClass &lt;span class="o"&gt;(&lt;/span&gt;TypeError&lt;span class="o"&gt;)&lt;/span&gt;
Caller: person.rb:37
Definition: person.rb:31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runtime feedback is invaluable for correcting our code and making necessary adjustments. Yet, it's essential to recognize that Sorbet runtime incurs a performance overhead, which may not be suitable for all production environments. According to the &lt;a href="https://sorbet.org/docs/runtime" rel="noopener noreferrer"&gt;Sorbet documentation on runtime&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[...]in some cases, especially when calling certain methods in tight loops or other latency-sensitive paths, the overhead of even doing&lt;br&gt;
the checks (regardless of what happens on failure) is prohibitively expensive. To handle these cases, Sorbet offers &lt;code&gt;.checked(...)&lt;/code&gt; which&lt;br&gt;
declares in what environments a sig should be checked.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sorbet provides various mechanisms to disable these runtime checks when needed. One such mechanism can be configured in &lt;code&gt;application.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;T::Configuration.default_checked_level &lt;span class="o"&gt;=&lt;/span&gt; :tests &lt;span class="c"&gt;# where :tests is the environment, it can also be set to :never&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For further details on Sorbet runtime checks, refer to the &lt;a href="https://sorbet.org/docs/runtime" rel="noopener noreferrer"&gt;Sorbet runtime documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we've looked at Sorbet, exploring its fundamental workings, benefits, and some considerations regarding performance. Sorbet stands as a robust tool for type checking in Ruby, offering the flexibility of gradual adoption. Its vibrant ecosystem and ease of implementation make it an invaluable asset for Ruby developers seeking to enhance code reliability and maintainability.&lt;/p&gt;

&lt;p&gt;For further exploration, consider diving into the resources provided below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stripe.com/blog/sorbet-stripes-type-checker-for-ruby" rel="noopener noreferrer"&gt;Sorbet: Stripe’s type checker for Ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learnxinyminutes.com/docs/sorbet/" rel="noopener noreferrer"&gt;Learn Sorbet in Y minutes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=Gdx6by6tcvw" rel="noopener noreferrer"&gt;Gradual typing for Ruby at Scale with Sorbet (video)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sorbet.org/docs" rel="noopener noreferrer"&gt;Sorbet documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic" rel="noopener noreferrer"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>sorbet</category>
    </item>
    <item>
      <title>AnyCable for Ruby on Rails: How Does it Improve over Action Cable?</title>
      <dc:creator>Abiodun Olowode</dc:creator>
      <pubDate>Thu, 16 May 2024 11:13:16 +0000</pubDate>
      <link>https://dev.to/appsignal/anycable-for-ruby-on-rails-how-does-it-improve-over-action-cable-1ig3</link>
      <guid>https://dev.to/appsignal/anycable-for-ruby-on-rails-how-does-it-improve-over-action-cable-1ig3</guid>
      <description>&lt;p&gt;In modern web applications, real-time communication has become more than a feature: it's gradually evolved into a necessity. Users expect instant updates, live interactions, and dynamic content.&lt;/p&gt;

&lt;p&gt;In Rails applications, Action Cable has long been the go-to solution, harnessing WebSockets to fulfill these demands. In this article, we introduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The basics of WebSockets&lt;/li&gt;
&lt;li&gt;How Action Cable enables real-time communication via WebSockets&lt;/li&gt;
&lt;li&gt;Why AnyCable was created&lt;/li&gt;
&lt;li&gt;How to get AnyCable up and running&lt;/li&gt;
&lt;li&gt;The improvements AnyCable brings to the table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  The WebSocket Technology
&lt;/h2&gt;

&lt;p&gt;The WebSocket technology was introduced in 2011 with the publication of &lt;a href="https://datatracker.ietf.org/doc/html/rfc6455"&gt;RFC 6455&lt;/a&gt;, titled "The WebSocket Protocol". This standardized protocol facilitated the establishment of persistent, real-time communication channels between web browsers and servers, allowing for more efficient and continuous data exchange compared to traditional HTTP.&lt;/p&gt;

&lt;p&gt;The WebSocket technology enables continuous bi-directional communication between clients and servers, a stark departure from the request-response model of HTTP (where connections terminate after each exchange). This persistence in communication proves pivotal for real-time applications like chat platforms and gaming systems.&lt;/p&gt;

&lt;p&gt;Before the introduction of WebSocket technology, many applications often relied on a technique called polling. Polling involves the client repeatedly sending requests to the server at specified intervals to check for updates or new information. While this method allowed for some level of real-time updates, it was less efficient than WebSocket communication. Polling could lead to unnecessary server load and increased latency, as the client had to initiate a new request each time it wanted to retrieve updated data.&lt;/p&gt;

&lt;p&gt;WebSocket technology addressed these limitations by establishing a persistent connection between the client and the server, enabling more immediate and efficient data transmission without the need for constant polling. This shift significantly improved the responsiveness and real-time capabilities of applications, providing a seamless and dynamic experience for users, particularly in scenarios demanding instant, up-to-the-moment information.&lt;/p&gt;

&lt;p&gt;Both Action Cable and AnyCable harness the power of web sockets to furnish Rails applications with real-time updates, liberating users from the cumbersome need to refresh pages or poll for the latest information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action Cable and Real-time Communication
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://edgeguides.rubyonrails.org/action_cable_overview.html"&gt;Action Cable&lt;/a&gt; enables the incorporation of real-time features into a Rails application via web sockets. This real-time communication happens between the client side (browser) and the Rails server. A few things are important to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;connection&lt;/em&gt; forms the foundation of this client-server relationship. For every WebSocket connection, Action Cable creates one connection instance.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;channel&lt;/em&gt; refers to a communication pathway that allows clients to subscribe to and receive updates about specific topics or categories of information, e.g., &lt;code&gt;BookChannel&lt;/code&gt;, &lt;code&gt;MovieChannel&lt;/code&gt;, &lt;code&gt;SportsChannel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;subscriber&lt;/em&gt; is a client subscribed to a channel to receive information.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Broadcasting&lt;/em&gt; is when the server publishes information that is transmitted by the relevant channel to the subscribed client/s.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;stream&lt;/em&gt; is the route or pathway through which broadcasts get to the subscribers. By using &lt;code&gt;stream_from&lt;/code&gt;, subscriptions can be made for broadcasting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a connection is established between the client and the server, Action Cable creates a connection object for that connection. With this connection, the client can subscribe to one or more channels. Action Cable broadcasts updates to all subscribed clients via their respective channels.&lt;/p&gt;

&lt;p&gt;A simple example is a browser view showing a library book list that updates when a book is added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create the rails application&lt;/span&gt;
rails new book_list
&lt;span class="nb"&gt;cd &lt;/span&gt;book_list
&lt;span class="c"&gt;# create the needed controller and model&lt;/span&gt;
rails g controller books index
rails g model Book name
rails db:migrate
&lt;span class="c"&gt;# change the development Action Cable adapter from Async (the default one) to Redis&lt;/span&gt;
rails turbo:install:redis
&lt;span class="c"&gt;# start the redis server&lt;/span&gt;
redis-server
&lt;span class="c"&gt;# start the rails server&lt;/span&gt;
rails s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get a real-time update on the book model, we employ Action Cable for broadcasting the updates. We set up Action Cable to broadcast these updates to a channel called "books". Via Turbo Streams, we create the subscriber to this channel and deliver the updates.&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/models/books.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;broadcasts_to&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;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:books&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# Broadcasts all the activities on this model -&amp;gt; create, update, destroy.&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 can now render the list of books:&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/views/rooms/_book.html.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= dom_id(book) %&amp;gt;&amp;gt; &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt; &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/views/books/index.html.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Books&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&amp;gt;
&amp;lt;%= turbo_stream_from "books" %&amp;gt;
&amp;lt;div id="books"&amp;gt;
  &amp;lt;%= render @books %&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As seen below, different actions trigger a broadcast, and Action Cable broadcasts to the channel specified — "books". The view is also subsequently updated to reflect these updates.&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;irb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Book&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="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"The Avatar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Rendered&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;_book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;erb&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Allocations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ActionCable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="no"&gt;Broadcasting&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="ss"&gt;books: &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;turbo-stream action=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;append&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; target=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;books&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;template&amp;gt;&amp;lt;div&amp;gt; The Avatar &amp;lt;/div&amp;gt;&amp;lt;/template&amp;gt;&amp;lt;/turbo-stream&amp;gt;"&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;Book:0x0000000107c13358&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;irb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;book_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;irb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;book_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"The Demo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Rendered&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;_book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;erb&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Allocations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ActionCable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="no"&gt;Broadcasting&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="ss"&gt;books: &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;turbo-stream action=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;replace&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; target=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;book_1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;template&amp;gt;&amp;lt;div&amp;gt; The Demo &amp;lt;/div&amp;gt;&amp;lt;/template&amp;gt;&amp;lt;/turbo-stream&amp;gt;"&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="n"&gt;irb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;book_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ActionCable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="no"&gt;Broadcasting&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="ss"&gt;books: &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;turbo-stream action=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;remove&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; target=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;book_7&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/turbo-stream&amp;gt;"&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;Book:0x0000000107bd2948&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;In Rails, integrating Action Cable (and, as a result, real-time updates into an application) is quite easy: even a beginner can get up and running in minutes. With Action Cable, we can manage connections, channels, and broadcasting to clients without worrying about the underlying WebSocket details. It also uses the same connection for multiple subscriptions, reducing the overhead of creating new connections for each new real-time update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AnyCable for Ruby on Rails?
&lt;/h2&gt;

&lt;p&gt;Action Cable is built on Ruby and has significant differences from other servers written in Golang and Erlang. &lt;a href="https://evilmartians.com/chronicles/anycable-actioncable-on-steroids"&gt;Check out a detailed summary of AnyCable vs. Action Cable benchmark findings&lt;/a&gt;. Here are the key results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Action Cable consumes approximately 4 times more memory when handling 20 thousand idle clients without subscriptions or transmissions.&lt;/li&gt;
&lt;li&gt;Action Cable requires much higher CPU usage.&lt;/li&gt;
&lt;li&gt;Action Cable takes around 10 times longer to broadcast to 10,000 clients. Starting at 1 second for a thousand clients, the time required increases by 1 second for every additional thousand clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://anycable.io/"&gt;AnyCable WebSocket Server&lt;/a&gt; was created to combine the beauty of Action Cable with the performance benefits gained from Golang. AnyCable handles WebSockets on a different server called &lt;a href="https://github.com/anycable/anycable-go"&gt;AnyCable-Go&lt;/a&gt;, effectively reducing the burden on your primary web application.&lt;/p&gt;

&lt;p&gt;In addition to performance advantages, AnyCable offers &lt;strong&gt;resumability&lt;/strong&gt;. If a client becomes disconnected from the server (e.g., due to network issues leading to an offline status), upon reconnection, the client will receive all the messages missed during the disconnection. They are also not required to re-subscribe as sessions are recovered. &lt;a href="https://youtu.be/4n1SxfK0Uqg?t=916"&gt;See a brief demo of the resumability feature&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;View detailed information on the &lt;a href="https://evilmartians.com/chronicles/enter-anycable-v1-4-reliable-real-time-features-for-apps-of-any-size"&gt;recent enhancements to AnyCable&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating AnyCable
&lt;/h2&gt;

&lt;p&gt;Let's replace Action Cable with AnyCable in our &lt;code&gt;BookList&lt;/code&gt; example.&lt;/p&gt;

&lt;p&gt;Install the gem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# add the gem to your gemfile&lt;/span&gt;
gem &lt;span class="s2"&gt;"anycable-rails"&lt;/span&gt;

&lt;span class="c"&gt;# install it&lt;/span&gt;
bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, run the setup generator.&lt;/p&gt;

&lt;p&gt;This generator is interactive and asks questions about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The type of development environment in use.&lt;/li&gt;
&lt;li&gt;How you would like to install the &lt;code&gt;AnyCable-Go&lt;/code&gt; websocket server.&lt;/li&gt;
&lt;li&gt;If Heroku is being used for deployment.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;JWT&lt;/code&gt; is the intended means of authentication.&lt;/li&gt;
&lt;li&gt;If you want a &lt;code&gt;Procfile.dev&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this information helps to reduce the amount of manual work you need to do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g anycable:setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you did say "yes" to the generation of a &lt;code&gt;Procfile.dev&lt;/code&gt;, it would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Procfile.dev

web: bin/rails s
anycable: bundle exec anycable
ws: bin/anycable-go --port=8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hence, you do not need to manually run the rest of the commands below.&lt;/p&gt;

&lt;p&gt;Run the AnyCable RPC server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;exec &lt;/span&gt;anycable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the WebSocket server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anycable-go &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the Rails server.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;cable.yml&lt;/code&gt; file, you'll find that the adapter changes to &lt;code&gt;any_cable&lt;/code&gt; if the &lt;code&gt;ACTION_CABLE_ADAPTER&lt;/code&gt; environment variable is not set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adapter: &amp;lt;%&lt;span class="o"&gt;=&lt;/span&gt; ENV.fetch&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ACTION_CABLE_ADAPTER"&lt;/span&gt;, &lt;span class="s2"&gt;"any_cable"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In all the &lt;code&gt;$env.rb&lt;/code&gt; files, you'll find the Action Cable URL set as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;config.action_cable.url &lt;span class="o"&gt;=&lt;/span&gt; ActionCable.server.config.url &lt;span class="o"&gt;=&lt;/span&gt; ENV.fetch&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"CABLE_URL"&lt;/span&gt;, &lt;span class="s2"&gt;"/cable"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;AnyCable::Rails.enabled?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;any_cable.yml&lt;/code&gt; file, the Redis channel is set to &lt;code&gt;__anycable__&lt;/code&gt;. You can confirm this in the terminal via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;redis-cli PUBSUB CHANNELS
&lt;span class="c"&gt;# 1) "__anycable__"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read more about the &lt;a href="https://docs.anycable.io/ruby/configuration"&gt;AnyCable configuration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the Rails server, you'll find a routing error related to the &lt;code&gt;/cable&lt;/code&gt; endpoint. You'll also find that, when you filter by &lt;code&gt;websockets&lt;/code&gt; in the &lt;code&gt;Network&lt;/code&gt; tab of the browser, the &lt;code&gt;Request URL&lt;/code&gt; for &lt;code&gt;cable&lt;/code&gt; is &lt;code&gt;ws://localhost:3000/cable&lt;/code&gt;, which is wrong. We should be attempting to connect to &lt;code&gt;ws://localhost:8080/cable&lt;/code&gt;, the value of &lt;code&gt;config.action_cable.url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To fix this, the &lt;code&gt;action_cable_meta_tag&lt;/code&gt; has to be added to the &lt;code&gt;application.html.erb&lt;/code&gt; file.&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;# application.html.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= action_cable_meta_tag %&amp;gt;
# ...
&amp;lt;/head&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This resets the &lt;code&gt;url&lt;/code&gt; to the set value. At this point, any broadcasted stream will show up!&lt;/p&gt;

&lt;h2&gt;
  
  
  Swapping Action Cable for AnyCable in Ruby
&lt;/h2&gt;

&lt;p&gt;To enable resumability, AnyCable utilizes the &lt;a href="https://docs.anycable.io/misc/action_cable_protocol?id=action-cable-extended-protocol"&gt;Action Cable Extended Protocol&lt;/a&gt;, which is implemented by the AnyCable server version 1.4 and later. This protocol is inherently supported by the &lt;a href="https://github.com/anycable/anycable-client/tree/master?tab=readme-ov-file"&gt;AnyCable JS client&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To integrate this with Turbo Streams, it's necessary to swap out the default Action Cable client for the AnyCable client. However, when using the &lt;code&gt;@hotwired/turbo-rails&lt;/code&gt; package, the Action Cable &lt;code&gt;getConsumer&lt;/code&gt; function is invoked before the &lt;code&gt;setConsumer&lt;/code&gt; function during the initial page load, leading to the consumer not being overridden, and two web socket connections being opened. The &lt;a href="https://github.com/anycable/anycable-client/blob/master/packages/turbo-stream/README.md"&gt;@anycable/turbo-stream&lt;/a&gt; package was created to handle this (&lt;a href="https://anycable.io/blog/anycasts-using-anycable-client/"&gt;read additional details about this issue&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Here's the procedure for this swap.&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;@anycable/web&lt;/code&gt; and &lt;code&gt;@anycable/turbo-stream&lt;/code&gt; packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/importmap pin @anycable/web @anycable/turbo-stream
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pins the stated packages, along with &lt;code&gt;@anycable/core&lt;/code&gt;, &lt;code&gt;@hotwired/turbo&lt;/code&gt;, and &lt;code&gt;nanoevents&lt;/code&gt; to your &lt;code&gt;importmap.rb&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Create the &lt;code&gt;anycable&lt;/code&gt; client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/cable.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@anycable/web&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;createCable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refer to this as the new "cable" client:&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;# config/importmap.rb&lt;/span&gt;
&lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="s2"&gt;"cable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s2"&gt;"cable.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;preload: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the new cable client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/application.js&lt;/span&gt;
&lt;span class="c1"&gt;// We no longer need @hotwired/turbo-rails&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hotwired/turbo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@anycable/turbo-stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cable&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, we can restart our server, and the consumer/client is replaced with AnyCable.&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://github.com/anycable/anycable-client/blob/master/packages/turbo-stream/README.md"&gt;advanced configurations for the Turbo Streams package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the client replaced, &lt;a href="https://docs.anycable.io/anycable-go/reliable_streams?id=quick-start"&gt;resumability can be implemented&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With resumable sessions, the supported adapters are &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;redisx&lt;/code&gt; (if you use Redis). It is, however, important to note that the broker &lt;code&gt;preset&lt;/code&gt; command automatically sets the adapter as &lt;code&gt;http&lt;/code&gt;. Hence, when using &lt;code&gt;redisx&lt;/code&gt;, it must specified explicitly like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;anycable-go &lt;span class="nt"&gt;--broker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;memory &lt;span class="nt"&gt;--broadcast_adapter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;redisx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There you go! You should have resumable sessions working!&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Adoption and Feedback
&lt;/h2&gt;

&lt;p&gt;AnyCable has existed for several years and has gained widespread adoption among numerous organizations. Adopters have cited several compelling reasons for its attractiveness, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seamless migration without the need for architectural changes.&lt;/li&gt;
&lt;li&gt;No requirement to modify Action Cable channels or connections.&lt;/li&gt;
&lt;li&gt;Utilization of a dedicated server for WebSockets.&lt;/li&gt;
&lt;li&gt;Ability to handle a substantial number of concurrent connections without encountering any problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/orgs/anycable/discussions/68"&gt;See a few testimonials from satisfied users&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we've seen that AnyCable offers performance enhancements to Action Cable and can be used as an alternative when dealing with a large number of connections or high traffic.&lt;/p&gt;

&lt;p&gt;With recent improvements to AnyCable (such as the introduction of &lt;strong&gt;reliable streams&lt;/strong&gt;), AnyCable has become a &lt;strong&gt;solid choice regardless of performance concerns&lt;/strong&gt;. It supports broadcasting and subscription confirmation, thereby making it consistent and reliable. It can also be used in &lt;a href="https://docs.anycable.io/ruby/non_rails"&gt;non-Rails applications&lt;/a&gt;, and to &lt;a href="https://docs.anycable.io/guides/serverless"&gt;power serverless JavaScript applications&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Overall, &lt;a href="https://anycable.io/"&gt;AnyCable&lt;/a&gt; is a reliable choice for modern web applications and supports a variety of servers, allowing for greater flexibility in development.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>How to Use the rodauth-omniauth Gem in Ruby</title>
      <dc:creator>Abiodun Olowode</dc:creator>
      <pubDate>Wed, 12 Apr 2023 10:52:58 +0000</pubDate>
      <link>https://dev.to/appsignal/how-to-use-the-rodauth-omniauth-gem-in-ruby-7g2</link>
      <guid>https://dev.to/appsignal/how-to-use-the-rodauth-omniauth-gem-in-ruby-7g2</guid>
      <description>&lt;p&gt;Setting up authentication for your Ruby app is important to ensure it is secure. In this post, we'll explore the &lt;code&gt;rodauth-omniauth&lt;/code&gt; gem for Ruby to implement authentication in your app via third-party providers.&lt;/p&gt;

&lt;p&gt;First, let's quickly define authentication before exploring the benefits of OmniAuth and setting up the rodauth-omniauth gem for a Rails app.&lt;/p&gt;

&lt;p&gt;Let's dive straight in!&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Authentication?
&lt;/h2&gt;

&lt;p&gt;Authentication is the act of verifying that someone or something is who or what they say they are to grant them access to requested resources. So if user &lt;code&gt;A&lt;/code&gt; wants access to an application, they have to provide identification that is acceptable and confirmable by the system to be granted access.&lt;/p&gt;

&lt;p&gt;Authentication can take many forms — for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Providing a password that matches an account's password as stored in an app's database.&lt;/li&gt;
&lt;li&gt;Using facial recognition or fingerprints.&lt;/li&gt;
&lt;li&gt;The use of a code sent to a registered channel, like an email or phone number, or an authenticator app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are various descriptions of authentication — we have &lt;code&gt;single-factor authentication&lt;/code&gt;, &lt;code&gt;two-factor authentication&lt;/code&gt;, and &lt;code&gt;multi-factor authentication&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does OmniAuth Bring to the Table?
&lt;/h2&gt;

&lt;p&gt;Authentication can be tiring for a user, especially when they have to constantly remember their various usernames and passwords for various applications. One of the solutions &lt;a href="https://oauth.net/"&gt;OAuth&lt;/a&gt; (Open Authentication) provides is to allow one service provider to authenticate a user using their identity with another service provider. This means that as a user, you only need to remember one username and password. Then every other application you need access to can delegate your authentication to your preferred OAuth provider.&lt;/p&gt;

&lt;p&gt;Also, you do not need to give your password to the application that requires authentication. Instead, you are redirected to your provider, where you can be authenticated, and then redirected to the application you intend to access (thereby keeping your credentials safe and secure). Your provider, on the other hand, then shares the relevant information about you with the requesting application. Some common OAuth service providers are  Google, Facebook, Twitter, and GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/omniauth/omniauth"&gt;OmniAuth&lt;/a&gt; is a library that standardizes multi-provider authentication for web applications. It uses a strategy pattern for the different providers, and these strategies are generally released individually as Ruby gems. The &lt;code&gt;rodauth-omniauth&lt;/code&gt; gem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Offers login and registration via multiple external providers using OmniAuth, together with the persistence of external identities.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/janko/rodauth-omniauth"&gt;rodauth-omniauth GitHub repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up rodauth-omniauth for a Rails Application
&lt;/h2&gt;

&lt;p&gt;Let's start by creating a new Rails project titled "rodauth_test_app".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rails new rodauth_test_app
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;rodauth_test_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to note that the &lt;a href="https://github.com/janko/rodauth-omniauth"&gt;rodauth-omniauth&lt;/a&gt; gem is an extension to the &lt;a href="https://github.com/jeremyevans/rodauth"&gt;rodauth&lt;/a&gt; gem, and as such, we need to have already set up Rodauth to use it. For a Rails app, a quick way to do that is to run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;bundle add rodauth-rails
&lt;span class="nv"&gt;$ &lt;/span&gt;rails generate rodauth:install
&lt;span class="nv"&gt;$ &lt;/span&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To have access to the views &lt;code&gt;rodauth&lt;/code&gt; provides, you can run either of the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rails g rodauth:views &lt;span class="c"&gt;# to have access to all the views that rodauth provides&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;rails g rodauth:views login &lt;span class="c"&gt;# to access only the login page which is what we need&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rails rodauth:routes&lt;/code&gt; command gives us a list of the routes handled by the RodauthApp, making it possible for us to get to the different pages we're interested in.&lt;/p&gt;

&lt;p&gt;Among the tables created during our installation, the most important one to us is the &lt;code&gt;Accounts&lt;/code&gt; table. This is the table where all user accounts are stored and will be the reference for the &lt;code&gt;AccountIdentities&lt;/code&gt; table we will create.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an Account Identities Table
&lt;/h3&gt;

&lt;p&gt;A person can have a driver's license, international passport, and voter's card. All these means of identification point to the same person and can be used interchangeably.&lt;/p&gt;

&lt;p&gt;Similarly, a user can have several means of identification (account identities), and this user may choose to log in at any time using any of these identities. That is why we'll create an &lt;code&gt;AccountIdentities&lt;/code&gt; table to store all the identities of a specific user account, identified by a unique email address.&lt;/p&gt;

&lt;p&gt;With a quick look in our &lt;code&gt;db&lt;/code&gt; folder at the &lt;code&gt;..._create_rodauth.rb&lt;/code&gt; file, we find the schema for the accounts table 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;create_table&lt;/span&gt; &lt;span class="ss"&gt;:accounts&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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&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;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&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;unique: &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;where: &lt;/span&gt;&lt;span class="s2"&gt;"status IN (1, 2)"&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:password_hash&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 create a table to store our account identities, we run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rails g migration CreateAccountIdentities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We find the generated file in the &lt;code&gt;db/migrate&lt;/code&gt; folder. Let's go ahead and define its schema.&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;CreateAccountIdentities&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;7.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:account_identities&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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;references&lt;/span&gt; &lt;span class="ss"&gt;:account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;foreign_key: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;on_delete: :cascade&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uid&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;unique: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="c1"&gt;# timestamps are not included -&amp;gt; to be explained later&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;Within this table, we ensure the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every identity must belong to an account.&lt;/li&gt;
&lt;li&gt;When an account is deleted, all associated identities also get deleted.&lt;/li&gt;
&lt;li&gt;The provider must be declared (e.g., Google, Twitter, Facebook, GitHub).&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;uid&lt;/code&gt; must be present.&lt;/li&gt;
&lt;li&gt;The combination of the provider and the &lt;code&gt;uid&lt;/code&gt; must be unique to ensure that all account identities are unique for each provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is also very important to have a homepage — a root route — to redirect users to after they are successfully authenticated. To ensure that, create a home controller with an index method that serves as the homepage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rails g controller home index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;config/routes.rb&lt;/code&gt; file, we add the root route 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;root&lt;/span&gt; &lt;span class="s2"&gt;"home#index"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting the OAuth Provider
&lt;/h3&gt;

&lt;p&gt;We'll use GitHub as the OAuth provider for this app, so install the GitHub OAuth gem and the rodauth-omniauth gem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;bundle add rodauth-omniauth omniauth-github
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having installed these gems, enable the omniauth feature and register your OAuth provider in the Rodauth configuration. The config file can be found at &lt;code&gt;app/misc/rodauth_main.rb&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;configure&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;enable&lt;/span&gt; &lt;span class="ss"&gt;:omniauth&lt;/span&gt;
  &lt;span class="n"&gt;omniauth_provider&lt;/span&gt; &lt;span class="ss"&gt;:github&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"GITHUB_CLIENT_ID"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"GITHUB_CLIENT_SECRET"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;name: :github&lt;/span&gt;
  &lt;span class="c1"&gt;# name is only important here if it's different from the provider e.g. :google_oauth2 with name: :google&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;To get the client id and client secret for this app, we need to &lt;a href="https://github.com/settings/developers"&gt;create the OAuth app&lt;/a&gt; and set the callback URL to &lt;code&gt;{root_url}/auth/{provider}/callback&lt;/code&gt;. In our case, this translates to &lt;code&gt;https://localhost:3000/auth/github/callback&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point, we're all set to start our server and visit our homepage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rails s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visiting &lt;code&gt;/login&lt;/code&gt;, as confirmed from our rodauth routes, leads to the login page. It is on this page that we add our &lt;code&gt;Login via GitHub&lt;/code&gt; button.&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/views/rodauth/_login_form_footer.html.erb&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= button_to "Login via GitHub", rodauth.omniauth_request_path(:github), method: :post, data: { turbo: false } %&amp;gt;&amp;lt;/li&amp;gt;
# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authentication and Redirection with Rodauth
&lt;/h3&gt;

&lt;p&gt;On clicking the &lt;code&gt;Login via GitHub&lt;/code&gt; button, we are redirected to the GitHub authentication page, assuming that the providers are properly configured. On this page, we are required to provide some details for authentication and after a successful authentication, we're redirected to the root URL of our application.&lt;/p&gt;

&lt;p&gt;Between authentication and redirection to the homepage, Rodauth does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates an account, along with an account identity, if no account with that email previously existed.&lt;/li&gt;
&lt;li&gt;Assigns an identity to an account if an account with that email already exists.&lt;/li&gt;
&lt;li&gt;Sets the status of a newly created account to "verified", because it assumes that the external provider verified the email address.&lt;/li&gt;
&lt;li&gt;Returns an error response during the callback phase if an account associated with the external identity already exists and is unverified (e.g., it is created through normal registration), as only verified accounts can be logged into.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Accessing Account Identities
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;rodauth-omniauth&lt;/code&gt; version 0.3.3 and above, you can access the identities related to an account without any further setup, as shown below:&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;Account:0x00007f236b455a70 id: 1, status: "verified", email: "biodun9@gmail.com", password_hash: nil&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;identities&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Account::Identity:0x00007fe90a0612d8&lt;/span&gt;
  &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;account_id: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;provider: &lt;/span&gt;&lt;span class="s2"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;uid: &lt;/span&gt;&lt;span class="s2"&gt;"52589264"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is possible because during the installation of &lt;code&gt;rodauth-rails&lt;/code&gt;, &lt;a href="https://github.com/janko/rodauth-model"&gt;rodauth-model&lt;/a&gt; is automatically configured within the &lt;code&gt;account&lt;/code&gt; model, defining an &lt;code&gt;identities&lt;/code&gt; one-to-many association on the account model.&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;Account&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Rodauth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt; &lt;span class="c1"&gt;# the rodauth-model is included here&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;unverified: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;verified: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;closed: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For versions below &lt;code&gt;0.3.3&lt;/code&gt;, this isn't the case &lt;a href="https://github.com/janko/rodauth-omniauth/issues/10"&gt;due to a bug&lt;/a&gt;. So to access &lt;code&gt;identities&lt;/code&gt;, you can either choose to upgrade your version of &lt;code&gt;rodauth-omniauth&lt;/code&gt;, or define the relationship manually 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="c1"&gt;# ... app/models/account_identity.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountIdentity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:account&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ... app/models/account.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:identities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s2"&gt;"AccountIdentity"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding Extra Columns
&lt;/h3&gt;

&lt;p&gt;During the &lt;code&gt;identities&lt;/code&gt; table migration, the &lt;code&gt;timestamps&lt;/code&gt; column was missing. This is because that column is not originally included in the implementation provided by the gem.&lt;/p&gt;

&lt;p&gt;However, we can manually add that column (as well as any other columns) and any logic required to configure the gem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rails g migration add_timestamps_to_account_identities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;AddTimestampsToAccountIdentities&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;7.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;add_timestamps&lt;/span&gt; &lt;span class="ss"&gt;:account_identities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="c1"&gt;# allowing this field to be nullable due to already existing records&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;Within the configuration, we have to implement the logic to add these columns when creating an account identity.&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/misc/rodauth_main.rb&lt;/span&gt;
&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="c1"&gt;# omniauth_identity_insert_hash handles account identity creation&lt;/span&gt;
  &lt;span class="n"&gt;omniauth_identity_insert_hash&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;created_at: &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# omniauth_identity_update_hash handles account identity updates&lt;/span&gt;
  &lt;span class="n"&gt;omniauth_identity_update_hash&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;updated_at: &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On deleting the previous account identity and creating a new one, we get 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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;AccountIdentity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;AccountIdentity:0x00007f28533b83c8&lt;/span&gt;
 &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;account_id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;provider: &lt;/span&gt;&lt;span class="s2"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;uid: &lt;/span&gt;&lt;span class="s2"&gt;"52589264"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;created_at: &lt;/span&gt;&lt;span class="no"&gt;Sat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="no"&gt;Feb&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;10.069443000&lt;/span&gt; &lt;span class="no"&gt;UTC&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;updated_at: &lt;/span&gt;&lt;span class="no"&gt;Sat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="no"&gt;Feb&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;10.069430000&lt;/span&gt; &lt;span class="no"&gt;UTC&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it becomes necessary to access the auth hash or any other additional information, this gem provides us with some helper methods, like &lt;code&gt;omniauth_provider&lt;/code&gt;, &lt;code&gt;omniauth_email&lt;/code&gt;, and many more. Hooks are also available to implement the logic required at certain phases like &lt;code&gt;omniauth_before_callback_phase&lt;/code&gt;, &lt;code&gt;omniauth_on_failure&lt;/code&gt;, etc. A comprehensive list of these helper methods and hooks can be found in the &lt;a href="https://github.com/janko/rodauth-omniauth"&gt;rodauth-omniauth documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on Compatibility and Versioning
&lt;/h2&gt;

&lt;p&gt;When installing the &lt;code&gt;omniauth-google_oauth2&lt;/code&gt; gem, a versioning error is encountered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Bundler could not find compatible versions &lt;span class="k"&gt;for &lt;/span&gt;gem &lt;span class="s2"&gt;"omniauth"&lt;/span&gt;:
  In Gemfile:
    omniauth-google_oauth2 was resolved to 0.1.5, which depends on
      omniauth &lt;span class="o"&gt;(&lt;/span&gt;~&amp;gt; 1.0&lt;span class="o"&gt;)&lt;/span&gt;

    rodauth-omniauth was resolved to 0.3.1, which depends on
      omniauth &lt;span class="o"&gt;(&lt;/span&gt;~&amp;gt; 2.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the &lt;code&gt;omniauth&lt;/code&gt; version dependencies for both gems are not compatible. &lt;a href="https://rubygems.org/gems/omniauth-google_oauth2"&gt;&lt;code&gt;omniauth-google_oauth2&lt;/code&gt;&lt;/a&gt; depends on &lt;code&gt;omniauth ~&amp;gt; 1.0&lt;/code&gt;, while the &lt;a href="https://rubygems.org/gems/rodauth-omniauth"&gt;&lt;code&gt;rodauth-omniauth&lt;/code&gt;&lt;/a&gt; gem depends on &lt;code&gt;omniauth ~&amp;gt; 2.0&lt;/code&gt;. A more compatible &lt;code&gt;omniauth-google&lt;/code&gt; gem would be &lt;a href="https://rubygems.org/gems/omniauth-google-oauth2"&gt;&lt;code&gt;omniauth-google-oauth2&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Any &lt;code&gt;OAuth&lt;/code&gt; provider gem used alongside &lt;code&gt;rodauth-omniauth&lt;/code&gt; must be compatible with &lt;code&gt;OmniAuth 2.0&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  rodauth-omniauth: Now and Future Plans
&lt;/h2&gt;

&lt;p&gt;In summary, the &lt;code&gt;rodauth-omniauth&lt;/code&gt; gem, though powerful, cannot be employed independently as it is a Rodauth extension. However, when employed with Rodauth, it saves you time that would have been spent manually implementing OmniAuth logic.&lt;/p&gt;

&lt;p&gt;The gem handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routing a request to a third-party provider.&lt;/li&gt;
&lt;li&gt;The response (by creating the required accounts/identities or validating existing ones).&lt;/li&gt;
&lt;li&gt;Outputting errors when necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the login is successful, it also redirects the request to the root URL.&lt;/p&gt;

&lt;p&gt;In the future, the plan is for the &lt;code&gt;rodauth-omniauth&lt;/code&gt; gem to offer services like allowing users to connect or remove external identities when signed in. So you will have the option to connect your Google account to an application (even if you're signed in with GitHub) or disconnect an already existing identity from an application while signed in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we looked briefly at the usefulness of OmniAuth before setting up rodauth-omniauth for a Rails application. We also defined authentication and explained why it is so important for your Ruby app.&lt;/p&gt;

&lt;p&gt;I hope you've found this introduction to the rodauth-omniauth gem useful.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>A Guide to Memoization in Ruby</title>
      <dc:creator>Abiodun Olowode</dc:creator>
      <pubDate>Wed, 11 Jan 2023 12:37:52 +0000</pubDate>
      <link>https://dev.to/appsignal/a-guide-to-memoization-in-ruby-42o6</link>
      <guid>https://dev.to/appsignal/a-guide-to-memoization-in-ruby-42o6</guid>
      <description>&lt;p&gt;Memoization is a caching technique to make your Ruby application run more efficiently and faster.&lt;/p&gt;

&lt;p&gt;In this post, we'll look at the benefits of memoization and when to use it in your Ruby application. We'll also look at some memoization mistakes to avoid.&lt;/p&gt;

&lt;p&gt;Let's first start by looking at code optimization — what it is and some of the different optimization techniques available.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Code Optimization?
&lt;/h2&gt;

&lt;p&gt;Code optimization is the process of improving code quality to make a piece of code or program more efficient and functional. Its advantages include — but are not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less memory consumption during an expensive calculation&lt;/li&gt;
&lt;li&gt;Much faster execution&lt;/li&gt;
&lt;li&gt;Sometimes, less space in terms of codebase size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The need to achieve some of the goals mentioned above arises at one time or another during an app's life cycle. If you're at a loss about where to begin with code optimization, try profiling!&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Profiling?
&lt;/h2&gt;

&lt;p&gt;Profiling refers to analyzing a program to measure its space and time complexities. Via profiling, we can obtain information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The frequency and duration of function calls&lt;/li&gt;
&lt;li&gt;The percentage of program execution time spent in a function in comparison with other functions&lt;/li&gt;
&lt;li&gt;The call stack of every function&lt;/li&gt;
&lt;li&gt;How many database calls are made for an HTML page to successfully load and how long it takes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This information can guide us to where code optimization is highly required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Optimization Methods for Ruby and Rails
&lt;/h2&gt;

&lt;p&gt;A few Ruby and Rails code optimization techniques include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Getting rid of N+1 queries&lt;/strong&gt; - This can help improve the speed of an app. The &lt;a href="https://rubygems.org/gems/bullet/versions/6.1.0" rel="noopener noreferrer"&gt;Bullet&lt;/a&gt; or &lt;a href="https://rubygems.org/gems/prosopite/versions/1.0.8" rel="noopener noreferrer"&gt;Prosopite&lt;/a&gt; gems can give a lending hand here. &lt;a href="https://labs.factorial.dev/posts/bullet-or-prosopite-for-nplus1" rel="noopener noreferrer"&gt;The N+1 Dilemma — Bullet or Prosopite?&lt;/a&gt; entails a brief comparison of both.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using static code analyzers&lt;/strong&gt; - These reduce memory consumption and codebase size, as we're alerted to code duplication, unused variables or method arguments, and the like. Examples include &lt;a href="https://rubocop.org/" rel="noopener noreferrer"&gt;Rubocop&lt;/a&gt; and &lt;a href="https://blog.appsignal.com/2022/10/19/improve-code-in-your-ruby-application-with-rubycritic.html" rel="noopener noreferrer"&gt;RubyCritic&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Caching&lt;/strong&gt; - Stores the content generated during the request-response cycle and reuses it when responding to similar requests, to improve the speed of an application. In Rails, we have page caching, fragment caching, action caching, low-level caching, and many more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choosing appropriate data structures&lt;/strong&gt; - Some data structures perform better in certain situations than others. As a result, a good way to optimize code is to use the most appropriate data structures for each situation, considering their space and time complexities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memoization&lt;/strong&gt; - Improves speed by reducing the number of times certain computations are carried out.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's now turn our focus to memoization.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Introduction to Memoization in Ruby
&lt;/h2&gt;

&lt;p&gt;Memoization is the act of caching a method's result so that the next time that method is called, the previous result is returned (as opposed to carrying out a recomputation). This helps save time when running a program.&lt;/p&gt;

&lt;p&gt;Let's look at an example involving anagrams.&lt;/p&gt;

&lt;p&gt;In the class below, we create a dictionary to pass in any word as an argument, and find the anagram.&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;Dictionary&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;words&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"creating my dictionary"&lt;/span&gt;
    &lt;span class="n"&gt;words&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;readlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/usr/share/dict/words'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
    &lt;span class="n"&gt;words&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;word&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chomp&lt;/span&gt;
      &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&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;word&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;""&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing the class above, we obtain:&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;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'rasp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# creating my dictionary&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"spar"&lt;/span&gt;
&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kame'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# creating my dictionary&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"make"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that every time we call the check method, we create the dictionary again. This is definitely not optimal, as the dictionary does not change.&lt;/p&gt;

&lt;p&gt;What if we created the dictionary once and used it whenever it was needed? We can; using memoization. Memoization allows us to cache the dictionary if it has been previously created.&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;words&lt;/span&gt;
  &lt;span class="vi"&gt;@words&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"creating my dictionary"&lt;/span&gt;
    &lt;span class="n"&gt;words&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;readlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/usr/share/dict/words'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
    &lt;span class="n"&gt;words&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;word&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chomp&lt;/span&gt;
      &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&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;word&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;dictionary&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;Testing the above, we obtain:&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;dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'eat'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#creating my dictionary&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"tea"&lt;/span&gt;
&lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kame'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"make"&lt;/span&gt;
&lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'live'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"vile"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the dictionary is created once and the cached version is used after that.&lt;/p&gt;

&lt;p&gt;Let's take a benchmark to see how much faster our program has become because of memoization. Name the memoized version &lt;code&gt;memoized_check&lt;/code&gt;, and the non-memoized version &lt;code&gt;check&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"benchmark"&lt;/span&gt;

&lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&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="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'rasp'&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="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&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="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memoized_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'rasp'&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;We get the following result:&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="mf"&gt;5.771061&lt;/span&gt;   &lt;span class="mf"&gt;0.044656&lt;/span&gt;   &lt;span class="mf"&gt;5.815717&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mf"&gt;5.836218&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.563966&lt;/span&gt;   &lt;span class="mf"&gt;0.000016&lt;/span&gt;   &lt;span class="mf"&gt;0.563982&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mf"&gt;0.564909&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows us that the unmemoized version takes &lt;code&gt;5.83&lt;/code&gt; seconds while the memoized one takes &lt;code&gt;0.56&lt;/code&gt; seconds, making it about ten times faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memoization Mistakes to Avoid in Your Ruby Application
&lt;/h2&gt;

&lt;p&gt;Let's now look at some mistakes to avoid when using memoization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring the False Or Nil Return
&lt;/h3&gt;

&lt;p&gt;In cases where a method's computation returns a false or nil, each call leads to a recomputation whenever the method is called (even though memoized). This is because the comparison is made using an &lt;code&gt;or&lt;/code&gt; — and in Ruby, both &lt;code&gt;nil&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; are &lt;code&gt;false&lt;/code&gt; values.&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="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Memoization using &lt;code&gt;||=&lt;/code&gt; doesn't consider false/nil return values, so such cases should be handled.&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;do_computation&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I am computing"&lt;/span&gt;
  &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;
  &lt;span class="vi"&gt;@check&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;do_computation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling the check method, we get the following results:&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;check&lt;/span&gt;
&lt;span class="c1"&gt;# I am computing&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;check&lt;/span&gt;
&lt;span class="c1"&gt;# I am computing&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;To deal with this, we can ascertain if the variable has been defined. If so, we return early before proceeding to the computation.&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;check&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@check&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@check&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@check&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;do_computation&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 results in:&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;check&lt;/span&gt;
&lt;span class="c1"&gt;# I am computing&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;check&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;h3&gt;
  
  
  Passing Parameters to a Method
&lt;/h3&gt;

&lt;p&gt;Another common mistake is assuming memoization will work differently when parameters are passed to a method.&lt;/p&gt;

&lt;p&gt;Let's say that a method's result is memoized using &lt;code&gt;||=&lt;/code&gt;, but that result depends on parameters. If these parameters change, the result does not change.&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;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@params&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;num&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 see what happens here:&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;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&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;4&lt;/span&gt;
&lt;span class="n"&gt;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result does not change just because we changed the parameters, and frankly, this is what is expected, considering how memoization works in its basic form.&lt;/p&gt;

&lt;p&gt;To deal with cases like this, you have to be comfortable with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;storing the parameters as keys in a hash&lt;/li&gt;
&lt;li&gt;using a gem or module that takes all the different cases to be handled into consideration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a hash:&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;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@params_hash&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@params_hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="vi"&gt;@params_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'creating a new key-value pair'&lt;/span&gt;
    &lt;span class="vi"&gt;@params_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;num&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;Trying this out, we have:&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;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;creating&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;creating&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="n"&gt;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&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;4&lt;/span&gt;
&lt;span class="n"&gt;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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;8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to rewrite this would be as the following:&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;change_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@params_hash&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="vi"&gt;@params_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you can use the gem &lt;a href="https://github.com/matthewrudy/memoist" rel="noopener noreferrer"&gt;Memoist&lt;/a&gt;. It handles caching the method's results, considering the arguments passed. It also provides a way to flush the current value or the entire memoization cache for an object.&lt;/p&gt;

&lt;h2&gt;
  
  
  When To Memoize — and When Not To
&lt;/h2&gt;

&lt;p&gt;To decide when to memoize, take note of the following:&lt;/p&gt;

&lt;h3&gt;
  
  
  Expensive Operations
&lt;/h3&gt;

&lt;p&gt;Let's say that an expensive operation will most definitely be called multiple times within a class and return the same result.&lt;/p&gt;

&lt;p&gt;We can move this into an instance variable initialized upon object instantiation (the expensive operation is also done at this time).&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;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:result&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
  &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_expensive_calculation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;result&lt;/code&gt; is available throughout the lifecycle of the class instance, and the expensive calculation is only done once.&lt;/p&gt;

&lt;p&gt;In cases like this, we don't need a separate method to memoize the &lt;code&gt;do_expensive_calculation&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;An expensive calculation that might not happen — but if it does, might happen more than once (and return the same value) — is a good candidate for memoization. This means we only &lt;code&gt;do_expensive_calculation&lt;/code&gt; when needed and then cache the result.&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;expensive_calculation&lt;/span&gt;
  &lt;span class="vi"&gt;@expensive_calculation&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;do_expensive_calculation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Analysing Potential Performance Improvements
&lt;/h3&gt;

&lt;p&gt;Memoization may be seen as necessary only after we've carried out a performance analysis. We need to accurately ascertain that the implemented memoization actually improves performance (as we did within the &lt;code&gt;Dictionary&lt;/code&gt; class).&lt;/p&gt;

&lt;p&gt;Without this, we could add unnecessary complexities to the code base. Be sure that the space and code complexities incurred from memoization are low compared to the gain in run-time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing Parameters
&lt;/h3&gt;

&lt;p&gt;If the parameters used for computation are constantly changing, memoization is not a good option.&lt;/p&gt;

&lt;p&gt;Memoization is more suitable for pure functions whose return values are the same for the same set of arguments.&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;calculation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&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 the example above, let's say we do cache the method result. Every other time we call the method, our cached value is wrong because &lt;code&gt;Time.now&lt;/code&gt; changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we explored how memoization, like every caching technique, comes with its advantages and disadvantages. We first looked at a range of code optimization techniques before diving into an example involving memoization. Then we touched on some mistakes to look out for. Finally, we explored when memoization is beneficial and when to avoid it.&lt;/p&gt;

&lt;p&gt;When memoization is carried out on methods available to class instances, the memoized result is only available for the lifecycle of that object. Where this result is the same for multiple class instances (e.g., multiple web requests), class-level memoization is usually a go-to.&lt;/p&gt;

&lt;p&gt;However, this might add more complexities in terms of cache invalidation. Using a cache store might be a better alternative to caching and allow for better optimization.&lt;/p&gt;

&lt;p&gt;Before you decide to employ it, you must ascertain that the pros of memoization outweigh the cons for your specific use case.&lt;/p&gt;

&lt;p&gt;Happy memoizing!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic" rel="noopener noreferrer"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>sql</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>An Introduction to Ractors in Ruby</title>
      <dc:creator>Abiodun Olowode</dc:creator>
      <pubDate>Wed, 31 Aug 2022 11:22:52 +0000</pubDate>
      <link>https://dev.to/appsignal/an-introduction-to-ractors-in-ruby-1np6</link>
      <guid>https://dev.to/appsignal/an-introduction-to-ractors-in-ruby-1np6</guid>
      <description>&lt;p&gt;In this post, we'll dive into ractors in Ruby, exploring how to build a ractor. You'll send and receive messages in ractors, and learn about shareable and unshareable objects.&lt;/p&gt;

&lt;p&gt;But first, let's define the actor model and ractors, and consider when you should use ractors.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Actor Model?
&lt;/h2&gt;

&lt;p&gt;In computer science, the object-oriented model is very popular, and in the Ruby community, many people are used to the term 'everything is an object'.&lt;/p&gt;

&lt;p&gt;Similarly, let me introduce you to the actor model, within which 'everything is an actor'. The actor model is a mathematical model of concurrent computation in which the universal primitive/fundamental agent of computation is an &lt;strong&gt;actor&lt;/strong&gt;. An actor is capable of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receiving messages and responding to the sender&lt;/li&gt;
&lt;li&gt;Sending messages to other actors&lt;/li&gt;
&lt;li&gt;Determining how to respond to the next message received&lt;/li&gt;
&lt;li&gt;Creating several other actors&lt;/li&gt;
&lt;li&gt;Making local decisions&lt;/li&gt;
&lt;li&gt;Performing actions (e.g., mutating data in a database)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Actors communicate via messages, process one message at a time, and maintain their own private state. However, they can modify this state via messages received, eliminating the need for a lock or mutex.&lt;/p&gt;

&lt;p&gt;Received messages are processed one message at a time in the order of FIFO (first in, first out). The message sender is decoupled (isolated) from the sent communication, enabling asynchronous communication.&lt;/p&gt;

&lt;p&gt;A few examples of the actor model implementation are akka, elixir, pulsar, celluloid, and &lt;strong&gt;ractors&lt;/strong&gt;. A few examples of concurrency models include threads, processes, and futures.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Ractors in Ruby?
&lt;/h2&gt;

&lt;p&gt;Ractor is an actor-model abstraction that provides a parallel execution feature without &lt;a href="https://hacks.mozilla.org/2019/02/fearless-security-thread-safety/"&gt;thread-safety concerns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just like threads, ractors provide true parallelism. However, unlike threads, they do not share everything. Most objects are unshareable, and when they are made shareable, are protected by an interpreter or locking mechanism.&lt;/p&gt;

&lt;p&gt;Ractors are also unable to access any objects through variables not defined within their scope. This means that we can be free of the possibility of &lt;a href="https://www.techtarget.com/searchstorage/definition/race-condition"&gt;race conditions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In 2020, when Ruby 3.0.0 was released, these were the &lt;a href="https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/"&gt;words of Matz&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s multi-core age today. Concurrency is very important. With Ractor, along with Async Fiber, Ruby will be a real concurrent language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ractors do not claim to have solved all thread-safety problems. In the &lt;a href="https://docs.ruby-lang.org/en/3.0/ractor_md.html"&gt;Ractor documentation&lt;/a&gt;, the following is clearly stated:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are several blocking operations (waiting send, waiting yield, and waiting take) so you can make a program which has dead-lock and live-lock issues.&lt;/p&gt;

&lt;p&gt;Some kind of shareable objects can introduce transactions (STM, for example). However, misusing transactions will generate inconsistent state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without ractors, you need to trace all state mutations to debug thread-safety issues. However, the beauty of ractors is that we can concentrate our efforts on suspicious shared code.&lt;/p&gt;

&lt;h2&gt;
  
  
  When and Why Should I Use Ractors in Ruby?
&lt;/h2&gt;

&lt;p&gt;When you create a ractor for the first time, you'll get a warning like this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;internal:ractor&amp;gt;:267: warning: Ractor is experimental, and the behavior may change &lt;span class="k"&gt;in &lt;/span&gt;future versions of Ruby! Also there are many implementation issues.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, that does not mean that you should avoid using ractors. Due to parallel execution, ractors can complete processes way faster than when processes are carried out synchronously.&lt;/p&gt;

&lt;p&gt;In the Ruby 3.0.0 release notes, you'll find this benchmark example of the &lt;a href="https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/"&gt;Tak function&lt;/a&gt;, where it is executed sequentially four times, and four times in parallel with ractors:&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;tarai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tarai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tarai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&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;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="n"&gt;tarai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&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;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="n"&gt;tarai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&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;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'benchmark'&lt;/span&gt;
&lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bm&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;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# sequential version&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'seq'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;tarai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="c1"&gt;# parallel version with ractors&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'par'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;tarai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:take&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;The results are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Benchmark result:
          user     system      total        real
&lt;span class="nb"&gt;seq  &lt;/span&gt;64.560736   0.001101  64.561837 &lt;span class="o"&gt;(&lt;/span&gt; 64.562194&lt;span class="o"&gt;)&lt;/span&gt;
par  66.422010   0.015999  66.438009 &lt;span class="o"&gt;(&lt;/span&gt; 16.685797&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/"&gt;Ruby 3.0.0 release notes&lt;/a&gt; state:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The result was measured on Ubuntu 20.04, Intel(R) Core(TM) i7-6700 (4 cores, 8 hardware threads). It shows that the parallel version is 3.87 times faster than the sequential version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So if you need a faster process execution time that can run in parallel on machines with multiple cores, ractors are not a bad idea at all.&lt;/p&gt;

&lt;p&gt;Modifying class/module objects on multi-ractor programs can introduce race conditions and should be avoided as much as possible. However, most objects are unshareable, so the need to implement locks to prevent race conditions becomes obsolete. If objects are shareable, they are protected by an interpreter or locking mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Your First Ractor in Ruby
&lt;/h2&gt;

&lt;p&gt;Creating a ractor is as easy as creating any class instance. Call &lt;code&gt;Ractor.new&lt;/code&gt; with a block — &lt;code&gt;Ractor.new { block }&lt;/code&gt;. This block is run in parallel with every other ractor.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It is important to note that every example shown from this point onwards was performed in Ruby 3.1.2.&lt;/em&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"This is my first ractor"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# This is my first ractor&lt;/span&gt;

&lt;span class="c1"&gt;# create a ractor with a name&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'second_ractor'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"This is my second ractor"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# This is my second ractor&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "second_ractor"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arguments can also be passed to &lt;code&gt;Ractor.new&lt;/code&gt;, and these arguments become parameters for the ractor 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;my_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;my_array&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;arr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# 4&lt;/span&gt;
&lt;span class="c1"&gt;# 5&lt;/span&gt;
&lt;span class="c1"&gt;# 6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recall how we talked about ractors being unable to access objects defined outside their scope? Let's see an example of 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;outer_scope_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"I am an outer scope object"&lt;/span&gt;
&lt;span class="no"&gt;Ractor&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="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;outer_scope_object&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;internal:ractor&amp;gt;:267:in `new': can not isolate a Proc because it accesses outer variables (outer_scope_object). (ArgumentError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get an error on the invocation of &lt;code&gt;.new&lt;/code&gt;, related to a &lt;code&gt;Proc&lt;/code&gt; not being isolated. This is because &lt;code&gt;Proc#isolate&lt;/code&gt; is called at a ractor's creation to prevent sharing unshareable objects. However, objects can be passed to and from ractors via messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending and Receiving Messages in Ractors
&lt;/h2&gt;

&lt;p&gt;Ractors send messages via an &lt;em&gt;outgoing port&lt;/em&gt; and receive messages via an &lt;em&gt;incoming port&lt;/em&gt;. The incoming port can hold an infinite number of messages and runs on the FIFO principle.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.send&lt;/code&gt; method works the same way a mailman delivers a message in the mail. The mailman takes the message and drops it at the door (incoming port) of the ractor.&lt;/p&gt;

&lt;p&gt;However, dropping a message at a person's door is not enough to get them to open it. &lt;code&gt;.receive&lt;/code&gt; is then available for the ractor to open the door and receive whatever message has been dropped.&lt;/p&gt;

&lt;p&gt;The ractor might want to do some computation with that message and return a response, so how do we get it? We ask the mailman to &lt;code&gt;.take&lt;/code&gt; the response.&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;tripple_number_ractor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I will receive a message soon"&lt;/span&gt;
  &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I will return a tripple of what I receive"&lt;/span&gt;
  &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# I will receive a message soon&lt;/span&gt;
&lt;span class="n"&gt;tripple_number_ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# mailman takes message to the door&lt;/span&gt;
&lt;span class="c1"&gt;# I will return a tripple of what I receive&lt;/span&gt;
&lt;span class="n"&gt;tripple_number_ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt; &lt;span class="c1"&gt;# mailman takes the response&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 45&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As seen above, the return value of a ractor is also a sent message and can be received via &lt;code&gt;.take&lt;/code&gt;. Since this is an outgoing message, it goes to the outgoing port.&lt;/p&gt;

&lt;p&gt;Here's a simple example:&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides returning a message, a ractor can also send a message to its outgoing port via &lt;code&gt;.yield&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="n"&gt;squared&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="no"&gt;Ractor&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;squared&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I just sent a message out"&lt;/span&gt;
  &lt;span class="n"&gt;squared&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 50&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 75&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first message sent to the outgoing port is &lt;code&gt;squared*2&lt;/code&gt;, and the next message is &lt;code&gt;squared*3&lt;/code&gt;. Therefore, when we call &lt;code&gt;.take&lt;/code&gt;, we get &lt;code&gt;50&lt;/code&gt; first. We have to call &lt;code&gt;.take&lt;/code&gt; a second time to get &lt;code&gt;75&lt;/code&gt; as two messages are sent to the outgoing port.&lt;/p&gt;

&lt;p&gt;Let's put this all together in one example of customers sending their orders to a supermarket and receiving the fulfilled orders:&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;supermarket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"The supermarket is preparing &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt; &lt;span class="s2"&gt;"This is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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="n"&gt;customers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;supermarket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&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;supermarket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;supermarket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a pack of sugar for customer &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fulfilled_order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;supermarket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;fulfilled_order&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; received by customer &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 3
The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 2
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 3 received by customer 3
The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 1
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 2 received by customer 2
The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 0
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 1 received by customer 1
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 0 received by customer 0
The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 4
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 4 received by customer 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it a second time yields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 0
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 0 received by customer 0
The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 4
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 4 received by customer 4
The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 1
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 1 received by customer 1
The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 3
The supermarket is preparing a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 2
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 3 received by customer 3
This is a pack of sugar &lt;span class="k"&gt;for &lt;/span&gt;customer 2 received by customer 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output can most definitely be in a different order every time we run this (because ractors run concurrently, as we have established).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A few things to note about sending and receiving messages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Messages can also be sent using &lt;code&gt;&amp;lt;&amp;lt; msg&lt;/code&gt;, instead of &lt;code&gt;.send(msg)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can add a condition to a &lt;code&gt;.receive&lt;/code&gt; using &lt;code&gt;receive_if&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;.send&lt;/code&gt; is called on a ractor that is already terminated (not running), you get a &lt;code&gt;Ractor::ClosedError&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A ractor's outgoing port closes after &lt;code&gt;.take&lt;/code&gt; is called on it if it runs just once (not in a loop).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; #&amp;lt;Ractor:#61 (irb):120 running&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; #&amp;lt;Ractor:#61 (irb):120 terminated&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 5&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;internal:ractor&amp;gt;:583:in `send': The incoming-port is already closed (Ractor::ClosedError)&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;internal:ractor&amp;gt;:694:in `take': The outgoing-port is already closed (Ractor::ClosedError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Objects can be moved to a destination ractor via &lt;code&gt;.send(obj, move: true)&lt;/code&gt; or &lt;code&gt;.yield(obj, move: true)&lt;/code&gt;. These objects become inaccessible at the previous destination, raising a &lt;code&gt;Ractor::MovedError&lt;/code&gt; when you try to call any other methods on the moved objects.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;outer_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"outer"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outer_object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;move: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; #&amp;lt;Ractor:#3 (irb):7 terminated&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;outer_object&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"moved"&lt;/span&gt;
&lt;span class="c1"&gt;# `method_missing': can not send any methods to a moved object (Ractor::MovedError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Threads cannot be sent as messages using &lt;code&gt;.send&lt;/code&gt; and &lt;code&gt;.yield&lt;/code&gt;. Doing this results in a &lt;code&gt;TypeError&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;internal:ractor&amp;gt;:627:in `yield': allocator undefined for Thread (TypeError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Shareable and Unshareable Objects
&lt;/h2&gt;

&lt;p&gt;Shareable objects are objects that can be sent to and from a ractor without compromising thread safety. An immutable object is a good example because once created, it cannot be changed — e.g., numbers and booleans.&lt;/p&gt;

&lt;p&gt;You can check the shareability of an object via &lt;code&gt;Ractor.shareable?&lt;/code&gt; and make an object shareable via &lt;code&gt;Ractor.make_shareable&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="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable?&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As seen above, immutable objects are shareable and mutable ones aren't. In Ruby, we usually call the &lt;code&gt;.freeze&lt;/code&gt; method on a string to make it immutable. This is the same method ractors apply to make an object shareable.&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;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'string'&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frozen?&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_shareable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; [4]&lt;/span&gt;
&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frozen?&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Messages sent via ractors can either be shareable or unshareable. When shareable, the same object is passed around. However, when unshareable, ractors perform a full copy of the object by default and send the full copy instead.&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;SHAREABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'share'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "share"&lt;/span&gt;
&lt;span class="no"&gt;SHAREABLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 350840&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&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;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SHAREABLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 350840&lt;/span&gt;
&lt;span class="no"&gt;NON_SHAREABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'can not share me'&lt;/span&gt;
&lt;span class="no"&gt;NON_SHAREABLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 572460&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NON_SHAREABLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 610420&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As seen above, the shareable object is the same within and outside the ractor. However, the unshareable one isn't because the ractor has a different object, just identical to it.&lt;/p&gt;

&lt;p&gt;Another method to send an exact object when it is unshareable is the previously discussed &lt;code&gt;move: true&lt;/code&gt;. This moves an object to a destination without needing to perform a copy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A few things to note about sharing objects in ractors:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ractor objects are also shareable objects.&lt;/li&gt;
&lt;li&gt;Constants that are shareable, but defined outside the scope of a ractor, can be accessed by a ractor. Recall our &lt;code&gt;outer_scope_object&lt;/code&gt; example? Give it another try, defined as &lt;code&gt;OUTER_SCOPE_OBJECT = "I am an outer scope object".freeze&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Class and module objects are shareable, but instance variables or constants defined within them are not if assigned to unshareable values.
&lt;/li&gt;
&lt;/ul&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;C&lt;/span&gt;
  &lt;span class="no"&gt;CONST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="vi"&gt;@share_me&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'share me'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
  &lt;span class="vi"&gt;@keep_me&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'unaccessible'&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bark&lt;/span&gt;
   &lt;span class="s1"&gt;'barked'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="no"&gt;C&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;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CONST&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bark&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@share_me&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@keep_me&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# 5&lt;/span&gt;
&lt;span class="c1"&gt;# barked&lt;/span&gt;
&lt;span class="c1"&gt;# share me&lt;/span&gt;
&lt;span class="c1"&gt;# (irb):161:in `instance_variable_get': can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;An incoming port or outgoing port can be closed using &lt;code&gt;Ractor#close_incoming&lt;/code&gt; and &lt;code&gt;Ractor#close_outgoing&lt;/code&gt;, respectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap Up and Further Reading on Ractors
&lt;/h2&gt;

&lt;p&gt;In this article, we introduced the concept of ractors, including when and why to use them and how to get started. We also looked at how they communicate with one another, what objects are shareable and unshareable, and how to make objects shareable.&lt;/p&gt;

&lt;p&gt;Ractors go deeper than this. Many other public methods can be called on ractors, like &lt;code&gt;select&lt;/code&gt; to wait for the success of take, yield and receive, &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;current&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;To expand your knowledge about ractors, check out the &lt;a href="https://docs.ruby-lang.org/en/3.0/ractor_md.html"&gt;ractor documentation&lt;/a&gt;. &lt;a href="https://gist.github.com/Kukunin/960ccef0d3c0a2c4b28ff5345911c2a5"&gt;This GitHub gist&lt;/a&gt; might also interest you if you'd like to experimentally compare ractors with threads.&lt;/p&gt;

&lt;p&gt;Ractors are indeed experimental, but they certainly look like they have a bright future in Ruby's evolution.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Converting a String to an Integer without any in-built type conversion methods</title>
      <dc:creator>Abiodun Olowode</dc:creator>
      <pubDate>Fri, 31 Jul 2020 15:53:14 +0000</pubDate>
      <link>https://dev.to/tripplea/converting-a-string-to-an-integer-without-any-in-built-methods-2non</link>
      <guid>https://dev.to/tripplea/converting-a-string-to-an-integer-without-any-in-built-methods-2non</guid>
      <description>&lt;p&gt;This is the first article of a series I will be publishing to share tips on how to solve popular coding interview questions. If you are still wondering if you need to learn this, you can read my previous article on &lt;a href="https://medium.com/@biodun9/why-should-any-developer-solve-coding-challenges-a0903a6771c2"&gt;why developers should solve coding challenges&lt;/a&gt;. We would start off by learning how to convert a string to an integer without any in-built methods. A colleague of mine recently got this question in an interview. You may not have learned this yet, but it is pretty easy to solve, let me walk you through it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zOVDy7BV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1gg9rcaznwzl5fq4tp9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zOVDy7BV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1gg9rcaznwzl5fq4tp9z.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image above depicts what we are expected to achieve and how to achieve that using in-built methods in both JavaScript and Ruby. In order to achieve this without any of the above methods, we need to put into consideration how numbers are formed generally. Every number is a combination of several other numbers added in thousands, hundreds, tens, or units. This dear reader is the key to solving this challenge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iD0m9J7K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mirz723rawrcfelsuexo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iD0m9J7K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mirz723rawrcfelsuexo.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Considering the image above, we can see the several combinations that form the foundation of all numbers. Units are 10⁰, tens are 10¹, hundreds are 10², thousands are 10³, tens of thousands are 10⁴, and so on. Observing carefully, we can see that counting from the left, the power of 10 corresponding to every digit is equal to the difference between the length of the number(how many digits it contains) and the position of the digit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aLLuJBfW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f08palsujn89qgtigzph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aLLuJBfW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f08palsujn89qgtigzph.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we understand this dynamic, let us dive into writing the function that solves this problem. We’ll follow the steps below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an object that maps every string to its number equivalent, this is to enable us to get the right number during our multiplications and additions, or we can also use the ASCII equivalents. Whichever you prefer is fine, I would write solutions using both. It is worthy of note that the ASCII equivalents of numbers range from 48–57.&lt;/li&gt;
&lt;li&gt;Split the string into an array so that we can loop through it.&lt;/li&gt;
&lt;li&gt;Find out the length of the array, so we know how many digits make up the number. Subtract 1 from the length to make up for the fact that index positions in an array are 1 less than the original positions. This is because the digit at the first position will have an index of 0 and so on.&lt;/li&gt;
&lt;li&gt;Start the calculation to multiply every digit by 10 raised to the power of the difference between our length and the position of the digit as we have explained earlier.&lt;/li&gt;
&lt;li&gt;Refactor our code to make it shorter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yeP5_97f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/of6lr4ooa4fo9cawcmbi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yeP5_97f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/of6lr4ooa4fo9cawcmbi.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Ruby, this can be solved as shown below;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OU2yY6fl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/83eh5a9nvp95q7ri4fvh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OU2yY6fl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/83eh5a9nvp95q7ri4fvh.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the ASCII equivalents and the JavaScript reduce method, we have;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--con7xBhT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/t25k1ivkp0jjb8tf4xy9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--con7xBhT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/t25k1ivkp0jjb8tf4xy9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Refactoring the code using the ASCII equivalents and without the explicit use of multiplication by powers of 10 as suggested by my dear friend &lt;a href=""&gt;Olumide Akinbolajo&lt;/a&gt;, we have;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ehgf3Hgt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/29xzkqq75dczxhwgzafv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ehgf3Hgt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/29xzkqq75dczxhwgzafv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I encourage you to try this several times with different methods, hell yeah! you can even use recursion if that's your thing. If you do have any questions though, please do not hesitate to contact me via &lt;a href="https://twitter.com/abiodunajibade3"&gt;Twitter&lt;/a&gt;. Happy Coding!!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>One More Reason To Use Redux</title>
      <dc:creator>Abiodun Olowode</dc:creator>
      <pubDate>Tue, 07 Apr 2020 15:02:55 +0000</pubDate>
      <link>https://dev.to/tripplea/one-more-reason-to-use-redux-22g5</link>
      <guid>https://dev.to/tripplea/one-more-reason-to-use-redux-22g5</guid>
      <description>&lt;p&gt;Let's take a moment to imagine how messages were passed in time past. So, great-grandpa wanted to send a message to his great-grandson but every man only has access to the son he gave birth to, you can imagine how long the message-tree will be. I can imagine it would go something like this;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;great-grandpa -&amp;gt; grandpa -&amp;gt; pa -&amp;gt; son&lt;span class="o"&gt;(&lt;/span&gt;great grand-son of great grand-pa&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That feeling of 'What the hell?' you probably have right now, is the feeling I get every time I have to pass props around in React through components that necessarily don't need them but are serving as what I call 'carrier-agents'. For me, it is exhausting, to say the least. This is one of the major reasons I use Redux. In this article, I shall explain what 'mapStateToProps' means and how it saves us the stress of using 'carrier-agents'.&lt;/p&gt;

&lt;p&gt;To get the basics about how reducers are used to manage state, you can check out my previous article &lt;a href="https://dev.to/tripplea/the-coolest-thing-about-redux-store-in-react-4eo3"&gt;here&lt;/a&gt;. This article is a sequel to that, hence, I would proceed with the previously created store. The store.getState() command yields;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;state &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
books: &lt;span class="o"&gt;[&lt;/span&gt;book1,book2],
filter: &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Book1 and Book2 are book objects but I chose to represent them as such for easy readability. We are going to create a component that needs access to some items in the store and I would walk you through accessing those items. &lt;br&gt;
But first, we would recreate something similar to the great-grand-pa, great-grand-son relationship we stated above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;const App &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
  &amp;lt;div&amp;gt;
    &amp;lt;BooksList /&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

const BooksList &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
  &amp;lt;div&amp;gt;
    &amp;lt;ShowBooks /&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

// The Provider looks something like this from the previous article&lt;span class="p"&gt;;&lt;/span&gt;

ReactDOM.render&lt;span class="o"&gt;(&lt;/span&gt;
  &amp;lt;Provider &lt;span class="nv"&gt;store&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;store&lt;span class="o"&gt;}&amp;gt;&lt;/span&gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/Provider&amp;gt;,
  document.getElementById&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;I am sure you can already figure the relationship out. Great-grandpa is the Provider which has the store, Grand-pa is the App component, Pa is the BooksList component and son(great-grandson of great-grandpa) is the ShowBooks component.&lt;br&gt;
Fortunately, our great-grandson (ShowBooks) can access every item grandpa (Provider) has got, and it can do this independently. This is how:&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps To Follow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Connect: Import 'connect' from 'react-redux'. This is what connects you to the
Store in the Provider.&lt;/li&gt;
&lt;li&gt;Create 'mapStateToProps': This is the function that maps the state of your 
Store to the props of your component, thereby making it accessible in your component when you call 'this.props'. You decide the names of the properties you want the store to be mapped to.&lt;/li&gt;
&lt;li&gt;Export your Connected Component: When exporting your component, you export your component already connected to your store, using the 'connect' you previously imported.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import React from &lt;span class="s1"&gt;'react'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
import &lt;span class="o"&gt;{&lt;/span&gt; connect &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'react-redux'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

const mapStateToProps &lt;span class="o"&gt;=&lt;/span&gt; state &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;({&lt;/span&gt; books: state.books, filter: state.filter &lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

const ShowBooks &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;({&lt;/span&gt;books, filter&lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  console.log&lt;span class="o"&gt;(&lt;/span&gt;books&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // &lt;span class="o"&gt;[&lt;/span&gt; book1,book2 &lt;span class="o"&gt;]&lt;/span&gt;
  console.log&lt;span class="o"&gt;(&lt;/span&gt;filter&lt;span class="o"&gt;)&lt;/span&gt; // &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;default connect&lt;span class="o"&gt;(&lt;/span&gt;mapStateToProps, null&lt;span class="o"&gt;)(&lt;/span&gt;ShowBooks&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And as easy as that, we skipped the 'carrier-agents'. I hope I have been able to convince you of one more reason to use redux. Do let me know if you have any questions. &lt;br&gt;
&lt;a href="https://twitter.com/AbiodunAjibade3"&gt;Twitter&lt;/a&gt;.. &lt;a href="https://www.linkedin.com/in/abiodun-ajibade"&gt;Linkedin&lt;/a&gt;.. &lt;a href="https://github.com/Tripple-A/"&gt;Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>javascript</category>
    </item>
    <item>
      <title>THE COOLEST THING ABOUT THE REDUX STORE IN REACT</title>
      <dc:creator>Abiodun Olowode</dc:creator>
      <pubDate>Thu, 05 Mar 2020 10:58:51 +0000</pubDate>
      <link>https://dev.to/tripplea/the-coolest-thing-about-redux-store-in-react-4eo3</link>
      <guid>https://dev.to/tripplea/the-coolest-thing-about-redux-store-in-react-4eo3</guid>
      <description>&lt;p&gt;Yeah, React is cool and Redux is cool too but you know what is the coolest thing about these two when they interact? &lt;code&gt;CombineReducers&lt;/code&gt;!!!.&lt;br&gt;
&lt;code&gt;CombineReducers&lt;/code&gt; makes it possible to refer to the properties of the store state by the names of the individual reducers that were combined; that way, you are never in doubt about what property you want to access, you just need to name your reducers accordingly. Pretty Cool!!, ain't it?&lt;/p&gt;

&lt;p&gt;Let us start by creating the several reducers we intend to have, this is determined by the distinct properties we expect our &lt;code&gt;state&lt;/code&gt; to possess. For instance, if you want your state which is stored in your redux store to have a &lt;code&gt;books&lt;/code&gt; property, which when called will produce a list of books, you create a reducer called &lt;strong&gt;books&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bSZv0JGt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w8ft7ctrdq51bvnk0b81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bSZv0JGt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w8ft7ctrdq51bvnk0b81.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's something fishy about the code above though; Have you figured it out? Yes, you are right!!! Random numbers!!! Id's are meant to be unique, random numbers could end up being the same and hence, generate an error. Do take note!&lt;/p&gt;

&lt;p&gt;Back to discussing our state, if we want our state to have another property called &lt;code&gt;filter&lt;/code&gt;, that returns a boolean value, we create a reducer called &lt;code&gt;filter&lt;/code&gt; which returns a boolean.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--stLDwMob--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/o1ew57yep7rowtj3dgst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--stLDwMob--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/o1ew57yep7rowtj3dgst.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is worthy of note that in this article, I have not added &lt;code&gt;action types&lt;/code&gt; to the individual reducers but I have gone ahead to return state as default; that is because you are free to add whatever action types are required by your app, it is not the focus of this article.&lt;br&gt;
All these reducers can be in their &lt;code&gt;.js&lt;/code&gt; files and stored in the reducers folder. The most important thing is creating the &lt;code&gt;rootReducer&lt;/code&gt; which will be connected to the store and combining the above-mentioned reducers in it. How do we do this? We import &lt;code&gt;combineReducers&lt;/code&gt; from &lt;code&gt;redux&lt;/code&gt; and apply its magic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WDsrfJ0w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lij2yxceg9ltesncrp06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WDsrfJ0w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lij2yxceg9ltesncrp06.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having combined the reducers in our rootReducer, we can now create our store and link it to the rootReducer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fhEvILQJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ystitjmty0g4hl9qhm19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fhEvILQJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ystitjmty0g4hl9qhm19.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the name implies, the Provider makes the store available to any children or grandchildren nested inside it. I would tell us how we can access this store in a later article, as a result, you can ignore lines 11 through 16 of the code above.&lt;/p&gt;

&lt;p&gt;Well, as simple as that, everything is set and if we run a console.log of &lt;code&gt;store.getState().books&lt;/code&gt;, we would get the &lt;strong&gt;list of books&lt;/strong&gt; and if we run  &lt;code&gt;store.getState().filter&lt;/code&gt;, we would get the boolean value of &lt;strong&gt;true&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>redux</category>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
