<?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: Isa Levine</title>
    <description>The latest articles on DEV Community by Isa Levine (@isalevine).</description>
    <link>https://dev.to/isalevine</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%2F145320%2Fe84301a0-b73d-4f6f-9619-122ab857aa83.png</url>
      <title>DEV Community: Isa Levine</title>
      <link>https://dev.to/isalevine</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/isalevine"/>
    <language>en</language>
    <item>
      <title>How To Build an Event Sourcing Pattern in Rails from Scratch</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Sun, 03 May 2020 17:11:42 +0000</pubDate>
      <link>https://dev.to/isalevine/building-an-event-sourcing-pattern-in-rails-from-scratch-355h</link>
      <guid>https://dev.to/isalevine/building-an-event-sourcing-pattern-in-rails-from-scratch-355h</guid>
      <description>&lt;p&gt;All code from this demo can be found in this GitHub repo:&lt;br&gt;
&lt;a href="https://github.com/isalevine/event-sourcing-user-app" rel="noopener noreferrer"&gt;https://github.com/isalevine/event-sourcing-user-app&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  To Recap: What is Event Sourcing?
&lt;/h1&gt;

&lt;p&gt;Event Sourcing is a system design pattern that emphasizes recording changes to data via immutable events.&lt;/p&gt;

&lt;p&gt;In other words: &lt;em&gt;every time your data changes&lt;/em&gt;, you save an event to your database with the details. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Those events never change or go away.&lt;/strong&gt; That way, you have a permanent, unchanging history of how your data reached its current state!&lt;/p&gt;

&lt;h1&gt;
  
  
  What this article covers
&lt;/h1&gt;

&lt;p&gt;We will primarily be working off of &lt;a href="https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224" rel="noopener noreferrer"&gt;Kickstarter's event sourcing example.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To create our Event pattern, we’ll take the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Get our Rails app up and running

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;User&lt;/code&gt; model and controller&lt;/li&gt;
&lt;li&gt;  PostgreSQL for our database&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  Set up our environment to test our Events

&lt;ul&gt;
&lt;li&gt;  Postico to inspect our database&lt;/li&gt;
&lt;li&gt;  Insomnia for REST client&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  Add our Events pattern

&lt;ul&gt;
&lt;li&gt;  What is an Event, and what Event data will we store in the database?&lt;/li&gt;
&lt;li&gt;  The BaseEvent that other Event classes will inherit from&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;Events::User::Created&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;Events::User::Destroyed&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Getting our Rails app up and running
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Let’s go ahead and create our new Rails app
&lt;/h2&gt;

&lt;p&gt;We’ll set the database to PostgreSQL with &lt;code&gt;--database=postgresql&lt;/code&gt; and skip tests with &lt;code&gt;--skip-test&lt;/code&gt;, as we will be adding RSpec manually later.&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;rails&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sourcing&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;postgresql&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Let’s add our &lt;code&gt;User&lt;/code&gt; model
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;User&lt;/code&gt; model will have several fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;name&lt;/code&gt; String,&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;email&lt;/code&gt; String,&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;password_digest&lt;/code&gt; String (for bcrypt)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;deleted&lt;/code&gt; Boolean (remember, part of event sourcing is that we &lt;strong&gt;never delete data&lt;/strong&gt;—instead, we will flag certain Users as &lt;em&gt;being&lt;/em&gt; deleted, and scope our queries appropriately)

&lt;ul&gt;
&lt;li&gt;  this field also needs to be &lt;code&gt;null: false&lt;/code&gt;, and be set to &lt;code&gt;default: false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll start this with Rails one-liner:&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;rails&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="n"&gt;password_digest&lt;/span&gt; &lt;span class="n"&gt;deleted&lt;/span&gt;&lt;span class="ss"&gt;:boolean&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And inside the new migration, tweak the &lt;code&gt;t.boolean :deleted&lt;/code&gt; to be &lt;code&gt;null: false&lt;/code&gt; and &lt;code&gt;default: false&lt;/code&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;# db/migrate/20200502025357_create_users.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateUsers&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;6.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;:users&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;string&lt;/span&gt; &lt;span class="ss"&gt;:name&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="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_digest&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;boolean&lt;/span&gt; &lt;span class="ss"&gt;:deleted&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="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;timestamps&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;h2&gt;
  
  
  Add a &lt;code&gt;User&lt;/code&gt; controller and routes
&lt;/h2&gt;

&lt;p&gt;Our User controller needs to have two actions, a &lt;code&gt;create&lt;/code&gt; and &lt;code&gt;destroy&lt;/code&gt; action, to handle the Events we want to make.&lt;/p&gt;

&lt;p&gt;Let’s create our controller manually, since we don’t need any views to be generated. In &lt;code&gt;app/controllers&lt;/code&gt;, create a &lt;code&gt;users_controller&lt;/code&gt; and add &lt;code&gt;def create&lt;/code&gt; and &lt;code&gt;def destroy&lt;/code&gt; actions:&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/controllers/users_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&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;Since we are not implementing auth yet, we’ll also add a &lt;code&gt;skip_before_action&lt;/code&gt; hook to make testing our code easier:&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/controllers/users_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;skip_before_action&lt;/span&gt; &lt;span class="ss"&gt;:verify_authenticity_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&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;create&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;destroy&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;Next, let’s manually add a POST and DELETE route that will go to the &lt;code&gt;create&lt;/code&gt; and &lt;code&gt;destroy&lt;/code&gt; actions in our controller:&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/routes.rb&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s1"&gt;'users/create'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'users#create'&lt;/span&gt;
  &lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="s1"&gt;'users/destroy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'users#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;Run &lt;code&gt;rails routes&lt;/code&gt; in your console to see that the routes are set up correctly:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

[13:29:44] (master) event-sourcing-user-app
// ♥ rails routes 

Prefix           Verb     URI Pattern                 Controller#Action
users_create     POST     /users/create(.:format)     users#create
users_destroy    DELETE   /users/destroy(.:format)    users#destroy


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Run database migrations
&lt;/h2&gt;

&lt;p&gt;Now, let’s create our databases and run our migrations in the usual two-step:&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;rails&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;
&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:migrate&lt;/span&gt;


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  Setting up our environment to test our Events
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Set up Postico to view our PostgreSQL database
&lt;/h2&gt;

&lt;p&gt;If you’re not familiar with &lt;a href="https://eggerapps.at/postico/" rel="noopener noreferrer"&gt;Postico&lt;/a&gt;, it’s a a database management tool and viewer for PostgreSQL with a great free trial. &lt;/p&gt;

&lt;p&gt;Download and install from their website, and open it up. Go ahead and hit &lt;code&gt;Connect&lt;/code&gt; to in the &lt;code&gt;localhost&lt;/code&gt; using its default settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3e01npyfqoi9iwb84i2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3e01npyfqoi9iwb84i2t.png" alt="Postico's main page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, click the &lt;code&gt;localhost&lt;/code&gt; button at the top to go to a list of available databases:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fckp52em08tqocdr1yno6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fckp52em08tqocdr1yno6.png" alt="Postico landing page inside localhost"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now, we should be able to select our development database:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft7cm9r4lp4el3va6mms3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft7cm9r4lp4el3va6mms3.png" alt="Postico page listing available databases"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select our &lt;code&gt;users&lt;/code&gt; table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsj9trljc4a6wubgx7fcj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsj9trljc4a6wubgx7fcj.png" alt="Postico page inside development database, showing users table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, hurray—there’s our User model, with its four fields:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fge2z2vqt53m56cgxbvi0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fge2z2vqt53m56cgxbvi0.png" alt="Postico users table"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Set up Insomnia to send HTTP requests
&lt;/h2&gt;

&lt;p&gt;Likewise, if you’re not familiar with &lt;a href="https://insomnia.rest/" rel="noopener noreferrer"&gt;Insomnia&lt;/a&gt;, it’s a tool for sending HTTP requests to test RESTful APIs. We’ll be using &lt;strong&gt;Insomnia Core&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Download, install, and open it up:&lt;/p&gt;

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

&lt;p&gt;Create a folder for our project, &lt;code&gt;event-sourcing-user-app&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F388jrxxujj7n5e52fs06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F388jrxxujj7n5e52fs06.png" alt="Insomnia showing new project folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s create our first request. We’ll make it a POST request, which we’ll user for a &lt;strong&gt;create User&lt;/strong&gt; route:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkhu76n08h5ubparkc8yk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkhu76n08h5ubparkc8yk.png" alt="Insomnia showing new request being set to POST"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And lastly, we’ll set the target URL to &lt;code&gt;localhost:3000/users/create&lt;/code&gt; for testing later:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuu41v34shfies4g4dnju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuu41v34shfies4g4dnju.png" alt="Insomnia showing target URL for Create User request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yay, now Insomnia’s ready to go! We’ll just need to fill out the body of our request with a hash once we have our Events created.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing the &lt;code&gt;create&lt;/code&gt; action with &lt;code&gt;byebug&lt;/code&gt; and Insomnia
&lt;/h2&gt;

&lt;p&gt;You can test out the routes by adding a &lt;code&gt;byebug&lt;/code&gt; to the controller action:&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/controllers/users_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="n"&gt;byebug&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Fire up &lt;code&gt;rails s&lt;/code&gt;, and send a POST request to &lt;code&gt;localhost:3000/users/create&lt;/code&gt; in Insomnia. In your console, you will see &lt;code&gt;byebug&lt;/code&gt; session:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6aywcx4o8j104jdyshq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6aywcx4o8j104jdyshq1.png" alt="screenshot showing Insomnia request, and console inside a byebug session"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, we can see our route working as expected!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now, we’re ready to build our event pattern!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What is an Event?
&lt;/h1&gt;

&lt;p&gt;In our event sourcing system, each Event will be &lt;strong&gt;a Rails model that stores information about changes to our data&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Our goal is to build two events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;Events::User::Created&lt;/code&gt; — this will record:

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;payload&lt;/code&gt;: a hash containing the &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;password&lt;/code&gt; params to create the User&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;user_id&lt;/code&gt;: the created User’s &lt;code&gt;id&lt;/code&gt;, used in its &lt;code&gt;belongs_to&lt;/code&gt; relationship&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;event_type&lt;/code&gt;: a String to show that this &lt;code&gt;user_event&lt;/code&gt; is the ”Created” type&lt;/li&gt;
&lt;li&gt;  timestamps&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;code&gt;Events::User::Destroyed&lt;/code&gt; — this will record:

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;payload&lt;/code&gt;: a hash containing the &lt;code&gt;id&lt;/code&gt; for the User to be flagged as deleted&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;user_id&lt;/code&gt;: the target User’s &lt;code&gt;id&lt;/code&gt;, used in its &lt;code&gt;belongs_to&lt;/code&gt; relationship&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;event_type&lt;/code&gt;: a String to show that this &lt;code&gt;user_event&lt;/code&gt; is the ”Destroyed” type&lt;/li&gt;
&lt;li&gt;  timestamps&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;When our Rails app creates or destroys a User, this will also trigger creating a new Event.&lt;/p&gt;

&lt;p&gt;These events will be saved to our database, and will be &lt;strong&gt;immutable&lt;/strong&gt; to serve as a permanent log of changes.&lt;/p&gt;

&lt;p&gt;Since we might end up having &lt;em&gt;a lot&lt;/em&gt; of User-related events, we’re also including the &lt;code&gt;event_type&lt;/code&gt; field on our User events so we can store them all in one &lt;code&gt;user_events&lt;/code&gt; table—and easily add more later!&lt;/p&gt;

&lt;h1&gt;
  
  
  The &lt;code&gt;Events::BaseEvent&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Our events will be built through inheritance. At the top of the chain, we will define &lt;code&gt;Events::BaseEvent&lt;/code&gt; where a lot of the event functionality will live.&lt;/p&gt;

&lt;p&gt;Since all of our events will be Rails models, go ahead and create a new &lt;code&gt;/events&lt;/code&gt; directory inside &lt;code&gt;app/models&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, we can create our BaseEvent:&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/events/base_event.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::BaseEvent&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;Base&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;abstract_class&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Since the BaseEvent only exists for inheritance, we can make it an &lt;code&gt;abstract_class&lt;/code&gt; so Rails knows not to try to load any records for it:&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/events/base_event.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::BaseEvent&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;Base&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;apply(aggregate)&lt;/code&gt; and &lt;code&gt;apply_and_persist&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Each event will have to define its own &lt;code&gt;apply&lt;/code&gt; method. This method will accept an &lt;code&gt;aggregate&lt;/code&gt;—another model, in our case a User—and update its attributes.&lt;br&gt;
&lt;em&gt;(The term &lt;code&gt;aggregate&lt;/code&gt; comes from the Kickstarter event sourcing system, and &lt;a href="https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224" rel="noopener noreferrer"&gt;you can read more about it here&lt;/a&gt;. Basically, &lt;code&gt;aggregates&lt;/code&gt; are models that receive changes via &lt;code&gt;events&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On BaseEvent, we’ll simply raise a &lt;code&gt;NotImplementedError&lt;/code&gt;. This will enforce us having to define it explicitly on each event, thus overriding the error via inheritance.&lt;/p&gt;

&lt;p&gt;The BaseEvent will also have a &lt;code&gt;before_create&lt;/code&gt; hook that calls &lt;code&gt;apply_and_persist&lt;/code&gt;. This will call &lt;code&gt;apply&lt;/code&gt;, then &lt;code&gt;save!&lt;/code&gt; the update to the database. &lt;br&gt;
&lt;em&gt;(It will also set the event’s &lt;code&gt;aggregate_id&lt;/code&gt;, specifically for Created events where the &lt;code&gt;id&lt;/code&gt; doesn’t exist until after &lt;code&gt;save!&lt;/code&gt; is called.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at the code we’ll add:&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/events/base_event.rb&lt;/span&gt;

&lt;span class="n"&gt;before_create&lt;/span&gt; &lt;span class="ss"&gt;:apply_and_persist&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply_and_persist&lt;/span&gt;
  &lt;span class="c1"&gt;# Lock the database row! (OK because we're in an ActiveRecord callback chain transaction)&lt;/span&gt;
  &lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lock!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;persisted?&lt;/span&gt;

  &lt;span class="c1"&gt;# Apply!&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;#Persist!&lt;/span&gt;
  &lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;

  &lt;span class="c1"&gt;# Update aggregate_id with id from newly created User&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;aggregate_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;after_initialize&lt;/code&gt; and &lt;code&gt;event_type&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;No matter what kind of event we instantiate, there are a couple attributes we want to set right away:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;event_type&lt;/code&gt; — every Event needs to be explicitly categorized for when it’s stored in the &lt;code&gt;user_events&lt;/code&gt; table as a &lt;code&gt;BaseEvent&lt;/code&gt; record&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;payload&lt;/code&gt; — since we always expect &lt;code&gt;payload&lt;/code&gt; to be accessible as a hash (and stored in our PostgreSQL database as JSON), we’ll add a &lt;code&gt;||=&lt;/code&gt; operator to set it to &lt;code&gt;{}&lt;/code&gt; if the event accepts no params&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, we’ll add an &lt;code&gt;after_initialize&lt;/code&gt; hook to set those attributes:&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/events/base_event.rb&lt;/span&gt;

&lt;span class="n"&gt;after_initialize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;event_type&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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="nf"&gt;last&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Above, we define &lt;code&gt;event_type&lt;/code&gt; to quickly access its own type via &lt;code&gt;attributes&lt;/code&gt; if loaded from our database—or upon first creation, deducing its type from the Event class’s name.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;self.payload_attributes(*attributes)&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In each Event class we create, we want the option to define possible &lt;code&gt;payload_attributes&lt;/code&gt; we want to record.&lt;/p&gt;

&lt;p&gt;On BaseEvent, &lt;code&gt;self.payload_attributes&lt;/code&gt; will create the getters and setters for our payload fields:&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/events/base_event.rb&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;payload_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@payload_attributes&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="n"&gt;attributes&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;&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="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;attribute&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="vi"&gt;@payload_attributes&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@payload_attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;="&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="vi"&gt;@payload_attributes&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Ultimately, this will let us define attributes like this at the top of each new Event class: &lt;code&gt;payload_attributes :name, :email, :password&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;find_or_build_aggregate&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;We want our events to be aware of their aggregates—in our case, the target User—and be able to either look it up, or create a new one.&lt;/p&gt;

&lt;p&gt;We’ll add a &lt;code&gt;before_validation&lt;/code&gt; hook (which gets called &lt;em&gt;really early&lt;/em&gt; in the &lt;code&gt;.create&lt;/code&gt; lifecycle) which will either look up or create the aggregate, based on whether a &lt;code&gt;user_id&lt;/code&gt; is supplied in the event’s initializing arguments:&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/events/base_event.rb&lt;/span&gt;

&lt;span class="n"&gt;before_validation&lt;/span&gt; &lt;span class="ss"&gt;:find_or_build_aggregate&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_or_build_aggregate&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_aggregate&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;aggregate_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_aggregate&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;find_aggregate&lt;/span&gt;
  &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aggregate_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constantize&lt;/span&gt;
  &lt;span class="n"&gt;klass&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="n"&gt;aggregate_id&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;def&lt;/span&gt; &lt;span class="nf"&gt;build_aggregate&lt;/span&gt;
  &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="s2"&gt;"build_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;aggregate_name&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;aggregate&lt;/code&gt; setters, getters, and get-its-namers
&lt;/h2&gt;

&lt;p&gt;To round out our events’ functionality, we’ll want some setters and getters—as well as methods to easily return its type or class name:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;aggregate=(model)&lt;/code&gt; and &lt;code&gt;aggregate&lt;/code&gt; will set and get the User our event targets&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;aggregate_id=(id)&lt;/code&gt; and &lt;code&gt;aggregate_id&lt;/code&gt; will map to the &lt;code&gt;user_id&lt;/code&gt; field on our &lt;code&gt;user_events&lt;/code&gt; table&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;self.aggregate_name&lt;/code&gt; gives the Event class awareness of its &lt;code&gt;belongs_to&lt;/code&gt; relationship’s target class (&lt;code&gt;#=&amp;gt; User&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;delegate :aggregate_name, to: :class&lt;/code&gt; will return a Symbol of the aggregate’s class name (&lt;code&gt;#=&amp;gt; :user&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;def event_klass&lt;/code&gt; will convert our Event class’s &lt;code&gt;::BaseEvent&lt;/code&gt; namespace into its appropriate event type (&lt;code&gt;#=&amp;gt; Events::User::Created&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;# app/models/events/base_event.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;aggregate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;aggregate_name&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;model&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;aggregate&lt;/span&gt;
  &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="n"&gt;aggregate_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;aggregate_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;aggregate_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_id="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&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;aggregate_id&lt;/span&gt;
  &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;aggregate_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_id"&lt;/span&gt;
&lt;span class="k"&gt;end&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;aggregate_name&lt;/span&gt;
  &lt;span class="n"&gt;inferred_aggregate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reflect_on_all_associations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:belongs_to&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Events must belong to an aggregate"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inferred_aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
  &lt;span class="n"&gt;inferred_aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:aggregate_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :class&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;event_klass&lt;/span&gt;
  &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;[&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;
  &lt;span class="n"&gt;klass&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="s1"&gt;'::'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;constantize&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Okay, let’s see the whole &lt;code&gt;Events::BaseEvent&lt;/code&gt;!
&lt;/h2&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/events/base_event.rb&lt;/span&gt;

&lt;span class="c1"&gt;# Kickstarter code reference:&lt;/span&gt;
&lt;span class="c1"&gt;# https://github.com/pcreux/event-sourcing-rails-todo-app-demo/blob/master/app/models/lib/base_event.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::BaseEvent&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;Base&lt;/span&gt;
  &lt;span class="n"&gt;before_validation&lt;/span&gt; &lt;span class="ss"&gt;:find_or_build_aggregate&lt;/span&gt;
  &lt;span class="n"&gt;before_create&lt;/span&gt; &lt;span class="ss"&gt;:apply_and_persist&lt;/span&gt;

  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;after_initialize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;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;payload_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@payload_attributes&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;attributes&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;&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="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;attribute&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="vi"&gt;@payload_attributes&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@payload_attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;="&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@payload_attributes&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_or_build_aggregate&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_aggregate&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;aggregate_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_aggregate&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;find_aggregate&lt;/span&gt;
    &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aggregate_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constantize&lt;/span&gt;
    &lt;span class="n"&gt;klass&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="n"&gt;aggregate_id&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;def&lt;/span&gt; &lt;span class="nf"&gt;build_aggregate&lt;/span&gt;
    &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="s2"&gt;"build_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;aggregate_name&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="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply_and_persist&lt;/span&gt;
    &lt;span class="c1"&gt;# Lock the database row! (OK because we're in an ActiveRecord callback chain transaction)&lt;/span&gt;
    &lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lock!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;persisted?&lt;/span&gt;

    &lt;span class="c1"&gt;# Apply!&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;#Persist!&lt;/span&gt;
    &lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;aggregate_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;aggregate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;aggregate_name&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;model&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;aggregate&lt;/span&gt;
    &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="n"&gt;aggregate_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;aggregate_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;aggregate_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_id="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&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;aggregate_id&lt;/span&gt;
    &lt;span class="n"&gt;public_send&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;aggregate_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_id"&lt;/span&gt;
  &lt;span class="k"&gt;end&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;aggregate_name&lt;/span&gt;
    &lt;span class="n"&gt;inferred_aggregate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reflect_on_all_associations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:belongs_to&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Events must belong to an aggregate"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inferred_aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
    &lt;span class="n"&gt;inferred_aggregate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:aggregate_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :class&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;event_type&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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="nf"&gt;last&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;event_klass&lt;/span&gt;
    &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;[&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;
    &lt;span class="n"&gt;klass&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="s1"&gt;'::'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;constantize&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;h1&gt;
  
  
  The &lt;code&gt;user_events&lt;/code&gt; table, and the &lt;code&gt;Events::User::BaseEvent&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;We previously mentioned that we will be storing multiple types of User-related events in a single &lt;code&gt;user_events&lt;/code&gt; table. &lt;/p&gt;

&lt;p&gt;To accomplish this and allow us to easily add more events later, we will create an &lt;code&gt;Events::User::BaseEvent&lt;/code&gt; which will tell all events in the &lt;code&gt;Events::User::&lt;/code&gt; namespace to save to the &lt;code&gt;user_events&lt;/code&gt; table. We will also define a &lt;code&gt;belongs_to&lt;/code&gt; relationship with a User here.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;user_events&lt;/code&gt; table
&lt;/h2&gt;

&lt;p&gt;Let’s go ahead and create our &lt;code&gt;user_events&lt;/code&gt; table in our database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224" rel="noopener noreferrer"&gt;Kickstarter’s event sourcing example&lt;/a&gt; describes that each &lt;code&gt;aggregate&lt;/code&gt; (User) has an event table (&lt;code&gt;user_events&lt;/code&gt;). These event tables will have a similar schema—we will tweak them slightly to match our verbiage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each Aggregate (ex: subscriptions) has an Event table associated to it (ex: subscription_events).&lt;br&gt;
…&lt;br&gt;
All events related to an aggregate are stored in the same table. All events tables have a similar schema:&lt;br&gt;
&lt;code&gt;id, aggregate_id, type, data (json), metadata (json), created_at&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few things we’ll tweak for our code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;aggregate_id&lt;/code&gt; will be replaced by &lt;code&gt;user_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;type&lt;/code&gt; will be replaced by &lt;code&gt;event_type&lt;/code&gt; (just to be more explicit)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;data&lt;/code&gt; will be replaced by &lt;code&gt;payload&lt;/code&gt;, and will still be type JSON&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;metadata&lt;/code&gt; will not be included at this time, since our events are relatively simple&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;created_at&lt;/code&gt; will not be included, since we will simply rely on ActiveRecord’s default timestamps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will create our &lt;code&gt;user_events&lt;/code&gt; table with a Rails migration:&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;rails&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;migration&lt;/span&gt; &lt;span class="no"&gt;CreateUserEvents&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This will create our migration with a &lt;code&gt;create_table&lt;/code&gt; block set up for us:&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;# db/migrate/20200502192018_create_user_events.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateUserEvents&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;6.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;:user_events&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="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We want to add four fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  a &lt;code&gt;belongs_to&lt;/code&gt; relationship to a &lt;code&gt;:user&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  an &lt;code&gt;event_type&lt;/code&gt; String&lt;/li&gt;
&lt;li&gt;  a &lt;code&gt;payload&lt;/code&gt; JSON&lt;/li&gt;
&lt;li&gt;  timestamps&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;# db/migrate/20200502192018_create_user_events.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateUserEvents&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;6.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;:user_events&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;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:user&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="kp"&gt;true&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;:event_type&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;json&lt;/span&gt; &lt;span class="ss"&gt;:payload&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;timestamps&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;Run the migration:&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;rails&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:migrate&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And open up Postico to check out the new &lt;code&gt;user_events&lt;/code&gt; table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq90r3lfnvvnir8dgh1sa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq90r3lfnvvnir8dgh1sa.png" alt="Postico page showing the user_events table selected"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our table and fields are ready to go!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwrmi6ouzhth65g4mte3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwrmi6ouzhth65g4mte3p.png" alt="Postico page showing user_events table fields"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Events::User::BaseEvent&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Inside our &lt;code&gt;app/models/events&lt;/code&gt; directory, create a new &lt;code&gt;user&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Inside that directory, create a new file &lt;code&gt;base_event.rb&lt;/code&gt;. This gives us the namespacing to create this class:&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/events/user/base_event.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::User::BaseEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseEvent&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"user_events"&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 &lt;code&gt;self.table_name = “user_events”&lt;/code&gt;, any new Event class we create that inherits from &lt;code&gt;Events::User::BaseEvent&lt;/code&gt; will automatically be saved and retrieved from the &lt;code&gt;user_events&lt;/code&gt; table!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;belongs_to :user&lt;/code&gt; and &lt;code&gt;has_many :events&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Since all our User-related events target a User, it makes sense to create a &lt;code&gt;has_many / belongs_to&lt;/code&gt; relationship between Users and Events in the &lt;code&gt;Events::User::&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;Since we’re deep in a namespace that uses the name &lt;code&gt;User&lt;/code&gt;, to tell Rails to look for the regular top-level &lt;code&gt;User&lt;/code&gt; model, we need to add &lt;code&gt;::&lt;/code&gt; before our classnames. This tells our &lt;code&gt;has_many&lt;/code&gt; and &lt;code&gt;belongs_to&lt;/code&gt; relationships to look outside the current namespace.&lt;/p&gt;

&lt;p&gt;Let’s update our &lt;code&gt;Events::User::BaseEvent&lt;/code&gt; and &lt;code&gt;User&lt;/code&gt; classes with the relationships:&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/events/user/base_event.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::User::BaseEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseEvent&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"user_events"&lt;/span&gt;

  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:user&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;"::User"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="c1"&gt;# app/models/user.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&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;:events&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;"Events::User::BaseEvent"&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Great! Now, when we load a User into a &lt;code&gt;user&lt;/code&gt; variable, we can call &lt;code&gt;user.events&lt;/code&gt; to load all related events from the &lt;code&gt;user_events&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We’re now ready to create some real, &lt;em&gt;usable&lt;/em&gt; Events!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating a new User with &lt;code&gt;Events::User::Created&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;With our BaseEvent pattern in place, we can now build our first event!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Events::User::Created&lt;/code&gt; will record the params used to create a User, as well as the new User’s id, and the event’s timestamp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the &lt;code&gt;Events::User::Created&lt;/code&gt; class
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;app/models/events/user&lt;/code&gt;, make a new &lt;code&gt;created.rb&lt;/code&gt; file. Our class will inherit from &lt;code&gt;Events::User::BaseEvent&lt;/code&gt; in the same directory:&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/events/user/created.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::User::Created&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseEvent&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;As we defined in the top-level &lt;code&gt;Events::BaseEvent&lt;/code&gt;, we must define an &lt;code&gt;apply&lt;/code&gt; method that will take a User instance as its &lt;code&gt;aggregate&lt;/code&gt; argument:&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/events/user/created.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::User::Created&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseEvent&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&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;Since we know creating a User requires params with a &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;password&lt;/code&gt;, we can also add them as a list of symbols to &lt;code&gt;payload_attributes&lt;/code&gt; to create our getters and setters:&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/events/user/created.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::User::Created&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseEvent&lt;/span&gt;
  &lt;span class="n"&gt;payload_attributes&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&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;h2&gt;
  
  
  Add logic to the &lt;code&gt;apply&lt;/code&gt; method
&lt;/h2&gt;

&lt;p&gt;The logic in the event’s &lt;code&gt;apply&lt;/code&gt; method is where the event’s power lies. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  takes in a User instance&lt;/li&gt;
&lt;li&gt;  applies the changes to the User instance, supplied by &lt;code&gt;payload_attributes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  returns the mutated User instance =&amp;gt; &lt;strong&gt;this is where the top-level BaseEvent receives back the User instance, and calls &lt;code&gt;save!&lt;/code&gt; to persist the changes in the database!&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to the list of attributes passed to &lt;code&gt;payload_attributes&lt;/code&gt;, we can simply call the attributes inside our &lt;code&gt;apply&lt;/code&gt; method to update the User instance:&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/events/user/created.rb&lt;/span&gt;

&lt;span class="n"&gt;payload_attributes&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;

  &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Perfect! Now, all we need to do is tell Insomnia to pass params that contain &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;password&lt;/code&gt; Strings, and our event will map them to the User model’s &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;password_digest&lt;/code&gt; fields. &lt;br&gt;
&lt;em&gt;(Remember: &lt;code&gt;password_digest&lt;/code&gt; is related to &lt;code&gt;bcrypt&lt;/code&gt; functionality, which we will explore in another article.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Update our controller to create an Event and use strong params
&lt;/h2&gt;

&lt;p&gt;Back in our &lt;code&gt;users_controller&lt;/code&gt;, we need to update two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  the &lt;code&gt;create&lt;/code&gt; action needs to call &lt;code&gt;Events::User::Created.create(payload: user_params)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  add strong params to protect the &lt;code&gt;user_params&lt;/code&gt; we will pass to &lt;code&gt;.create(payload: user_params)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the strong params, we will require the &lt;code&gt;user_params&lt;/code&gt; to have &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;password&lt;/code&gt; nested inside a &lt;code&gt;user&lt;/code&gt; key:&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/controllers/users_controller.rb&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_params&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&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;Now, we can safely pass &lt;code&gt;user_params&lt;/code&gt; to &lt;code&gt;Events::User::Created.create(payload: user_params)&lt;/code&gt; in the &lt;code&gt;create&lt;/code&gt; action:&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/controllers/users_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Created&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;payload: &lt;/span&gt;&lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_params&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&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;h2&gt;
  
  
  Let’s test our event with Insomnia and Postico!
&lt;/h2&gt;

&lt;p&gt;If we send the correct params via a POST request to &lt;code&gt;localhost:3000/users/create&lt;/code&gt;, we expect several behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  a new record in the &lt;code&gt;user_events&lt;/code&gt; table, with:

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;event_type “Created”&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;payload&lt;/code&gt; with the &lt;code&gt;user_params&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;  note that the &lt;code&gt;password&lt;/code&gt; will be stored as plaintext =&amp;gt; &lt;strong&gt;this is UNSAFE BEHAVIOR, and is because we have not implemented bcrypt encryption yet!&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;user_id&lt;/code&gt; with the newly-created User’s &lt;code&gt;id&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;  a new record in the &lt;code&gt;user&lt;/code&gt; table, with:

&lt;ul&gt;
&lt;li&gt;  correct &lt;code&gt;name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  correct &lt;code&gt;email&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;password_digest&lt;/code&gt; that is the plaintext &lt;code&gt;password&lt;/code&gt; =&amp;gt; &lt;strong&gt;this is UNSAFE BEHAVIOR, and is because we have not implemented bcrypt encryption yet!&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s test it out! &lt;/p&gt;

&lt;p&gt;Fire up &lt;code&gt;rails s&lt;/code&gt;, and open up Insomnia. &lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;Create User&lt;/code&gt; request, set the Body to JSON:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg3rgthzj73uz2dvhn37w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg3rgthzj73uz2dvhn37w.png" alt="Insomnia page showing Body type being set to JSON"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, create a JSON hash with a &lt;code&gt;”user”&lt;/code&gt; key, which points to a hash containing a &lt;code&gt;”name”&lt;/code&gt;, &lt;code&gt;”email”&lt;/code&gt;, and &lt;code&gt;”password”&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdcq61uzaow04u1m6bc7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdcq61uzaow04u1m6bc7j.png" alt="Insomnia page showing JSON body with user params"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now hit &lt;code&gt;Send&lt;/code&gt;, and let’s check out our database tables!&lt;/p&gt;

&lt;p&gt;First, let’s see if we have an event in our &lt;code&gt;user_events&lt;/code&gt; table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Facge8hrnkrq3vm4zsper.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Facge8hrnkrq3vm4zsper.png" alt="Postico table with first Created event record, overlaid on Insomnia request body"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So far, so good!&lt;br&gt;
&lt;em&gt;(Remember: &lt;strong&gt;storing passwords as plaintext is UNSAFE BEHAVIOR, and is because we have not implemented bcrypt encryption yet!&lt;/strong&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s check out the &lt;code&gt;users&lt;/code&gt; table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd82ai0lxwbhoi50sdvhh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd82ai0lxwbhoi50sdvhh.png" alt="Postico table with first User record, overlaid on Insomnia request body"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Terrific! We now have our new User, &lt;code&gt;ongo_gablogian&lt;/code&gt;, and a record of the Event and params that created him!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcl772dunyxs83v8cjb24.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcl772dunyxs83v8cjb24.gif" alt="gif of Danny DeVito as Ongo Gablogian, a parody of Andy Warhol, on Always Sunny"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There you have it! Our event sourcing system is now capturing changes to our data!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As long as we never alter the data in the &lt;code&gt;user_events&lt;/code&gt; table, we have a reliable log of how our data got to its current state!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fls69lrx60hiimpan2qpv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fls69lrx60hiimpan2qpv.jpeg" alt="screenshot of a banner stating MISSION ACCOMPLISHED on Arrested Development"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Destroying a User with &lt;code&gt;Events::User::Destroyed&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Now that we have our pattern in place, it’s very straightforward to create a new Event and record it to our &lt;code&gt;user_events&lt;/code&gt; table!&lt;/p&gt;

&lt;p&gt;Since we &lt;strong&gt;never want to destroy our data&lt;/strong&gt;, we implemented a boolean &lt;code&gt;deleted&lt;/code&gt; field on the User model. When a new User is created, it defaults to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s create a new event, &lt;code&gt;Events::User::Destroyed&lt;/code&gt;, that will set the &lt;code&gt;deleted&lt;/code&gt; field to &lt;code&gt;true&lt;/code&gt;!&lt;/p&gt;
&lt;h2&gt;
  
  
  Create an &lt;code&gt;app/models/events/user/destroyed.rb&lt;/code&gt; file
&lt;/h2&gt;

&lt;p&gt;In the same directory as our &lt;code&gt;Events::User::Created&lt;/code&gt; class, create an equivalent &lt;code&gt;Events::User::Destroyed&lt;/code&gt; class:&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/events/user/destroyed.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::User::Destroyed&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseEvent&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&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;Above, we start with an &lt;code&gt;apply&lt;/code&gt; method that simply returns the passed-in User instance.&lt;/p&gt;

&lt;p&gt;To delete a User, we’ll simply require an &lt;code&gt;id&lt;/code&gt;. Let’s add the &lt;code&gt;payload_attributes&lt;/code&gt; for it:&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/events/user/destroyed.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::User::Destroyed&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseEvent&lt;/span&gt;
  &lt;span class="n"&gt;payload_attributes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And we’ll make our &lt;code&gt;apply&lt;/code&gt; method update the passed-in User’s &lt;code&gt;deleted&lt;/code&gt; field to &lt;code&gt;true&lt;/code&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/events/user/destroyed.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events::User::Destroyed&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseEvent&lt;/span&gt;
  &lt;span class="n"&gt;payload_attributes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

    &lt;span class="n"&gt;user&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;That’s it—our new Event is done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Update the &lt;code&gt;destroy&lt;/code&gt; action in &lt;code&gt;users_controller&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In our &lt;code&gt;users_controller&lt;/code&gt;, we’ll make our &lt;code&gt;destroy&lt;/code&gt; action simply create our new &lt;code&gt;Events::User::Destroyed&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;Thanks to the &lt;code&gt;find_or_build_aggregate&lt;/code&gt; and &lt;code&gt;aggregate_id&lt;/code&gt; methods defined in our top-level BaseEvent, this &lt;code&gt;”Destroyed”&lt;/code&gt; event will look up a User automatically if a &lt;code&gt;user_id&lt;/code&gt; argument is supplied.&lt;/p&gt;

&lt;p&gt;First, let’s add &lt;code&gt;id&lt;/code&gt; to the list of strong params in &lt;code&gt;user_params&lt;/code&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/controllers/users_controller.rb&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_params&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now, our controller’s &lt;code&gt;destroy&lt;/code&gt; action can accept a &lt;code&gt;user_params&lt;/code&gt; that has the necessary &lt;code&gt;id&lt;/code&gt;. We’ll also use &lt;code&gt;user_params[:id]&lt;/code&gt; so the event can look up our target User’s record:&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/controllers/users_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
  &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Destroyed&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;user_id: &lt;/span&gt;&lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;payload: &lt;/span&gt;&lt;span class="n"&gt;user_params&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;We’re ready to go ahead and test with Insomnia!&lt;/p&gt;

&lt;h2&gt;
  
  
  Test a DELETE request in Insomnia
&lt;/h2&gt;

&lt;p&gt;Let’s fire up &lt;code&gt;rails s&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Over in Insomnia, create a new request called &lt;code&gt;Destroy User&lt;/code&gt; and make it a DELETE:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgpddvf8nf4ccuf5orhyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgpddvf8nf4ccuf5orhyh.png" alt="Insomnia page showing new Destroy User being set to type DELETE"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set its target URL to &lt;code&gt;localhost:3000/users/destroy&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fj6ffh9x0nmfjivrz87bo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fj6ffh9x0nmfjivrz87bo.png" alt="Insomnia page showing DELETE request's target URL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the Body type to JSON, and add a hash with a &lt;code&gt;”user”&lt;/code&gt; key pointing to a hash containing the &lt;code&gt;”id”&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw3kq02av0bgb0nvmt3p2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw3kq02av0bgb0nvmt3p2.png" alt="Insomnia page showing DELETE request's JSON body with a user id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hit Send, and check the database to see if the Event was created:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftqiyqf7jdsac9oaau0a7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftqiyqf7jdsac9oaau0a7.png" alt="Postico user_events table showing new Destroyed event record, overlaid on Insomnia request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, let’s check the database to see if our User has &lt;code&gt;deleted&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fciri8xikzflbhjhhvx4m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fciri8xikzflbhjhhvx4m.png" alt="Postico users table showing only User record with deleted field set to true"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! We get to keep our User record, but also have it be &lt;code&gt;deleted&lt;/code&gt;—we’re having our cake, and eating it too!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbruoty5t9v98as079fut.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbruoty5t9v98as079fut.jpg" alt="screenshot of cake from video game Portal"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;And that's no lie!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That’s all it takes to add a new event to our event sourcing system!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Wow, we covered a lot of ground! Let’s recap the steps we took to implement our event sourcing system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Create a new Rails app, with a User model and controller, and PostgreSQL for the database&lt;/li&gt;
&lt;li&gt;  Create an &lt;code&gt;Events::BaseEvent&lt;/code&gt; class in &lt;code&gt;app/models/events&lt;/code&gt; to handle Event logic:

&lt;ul&gt;
&lt;li&gt;  Looking up or creating aggregates (Users)&lt;/li&gt;
&lt;li&gt;  Creating getters and setters for &lt;code&gt;payload_attributes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Inferring its own &lt;code&gt;event_type&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Hooks for automatically applying changes and saving to the database&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  Create a &lt;code&gt;user_events&lt;/code&gt; table migration&lt;/li&gt;

&lt;li&gt;  Create an &lt;code&gt;Events::User::BaseEvent&lt;/code&gt; to save all Events in its &lt;code&gt;Events::User::&lt;/code&gt; namespace to the &lt;code&gt;user_events&lt;/code&gt; table&lt;/li&gt;

&lt;li&gt;  Create an &lt;code&gt;Events::User::Created&lt;/code&gt; event that will apply &lt;code&gt;user_params&lt;/code&gt; to a new User instance&lt;/li&gt;

&lt;li&gt;  Create an &lt;code&gt;Events::User::Destroyed&lt;/code&gt; event that will look up at User by &lt;code&gt;id&lt;/code&gt; and set its &lt;code&gt;deleted&lt;/code&gt; field to &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This minimal system allows us to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Have a record of events that create and destroy Users&lt;/li&gt;
&lt;li&gt;  Keep all User data permanently, and still have the ability to scope the &lt;code&gt;deleted&lt;/code&gt; ones as needed&lt;/li&gt;
&lt;li&gt;  A pattern that allows us to easily add new Events that will be saved to the same &lt;code&gt;user_events&lt;/code&gt; table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All code from this demo can be found in this GitHub repo:&lt;br&gt;
&lt;a href="https://github.com/isalevine/event-sourcing-user-app" rel="noopener noreferrer"&gt;https://github.com/isalevine/event-sourcing-user-app&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Next Up
&lt;/h1&gt;

&lt;p&gt;We have a lot more we can do to improve our event sourcing system, especially around security and data validations! In the next article, we will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Storing sensitive information safely in Event &lt;code&gt;payloads&lt;/code&gt;, such as passwords&lt;/li&gt;
&lt;li&gt;  Wrapping creating Events in &lt;code&gt;Commands&lt;/code&gt;, &lt;a href="[https://github.com/pcreux/event-sourcing-rails-todo-app-demo/blob/master/app/models/lib/command.rb](https://github.com/pcreux/event-sourcing-rails-todo-app-demo/blob/master/app/models/lib/command.rb)"&gt;per Kickstarter’s example&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Adding validations to &lt;code&gt;Commands&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;p&gt;Special thanks to &lt;a href="https://kickstarter.engineering/@pcreux" rel="noopener noreferrer"&gt;Philippe Creux&lt;/a&gt; and &lt;a href="https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224" rel="noopener noreferrer"&gt;Kickstarter&lt;/a&gt; for sharing &lt;a href="https://github.com/pcreux/event-sourcing-rails-todo-app-demo" rel="noopener noreferrer"&gt;their Event Sourcing example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://martinfowler.com/" rel="noopener noreferrer"&gt;Martin Fowler&lt;/a&gt; for his &lt;a href="https://martinfowler.com/articles/201701-event-driven.html" rel="noopener noreferrer"&gt;important writings&lt;/a&gt; on &lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html" rel="noopener noreferrer"&gt;Event Sourcing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://arkency.com/" rel="noopener noreferrer"&gt;Arkency&lt;/a&gt; for their great work with &lt;a href="https://github.com/RailsEventStore/rails_event_store" rel="noopener noreferrer"&gt;the RailsEventStore library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And finally, thanks to fellow Dev.to user &lt;a href="https://dev.to/mottalrd"&gt;Alfredo Motta&lt;/a&gt; for &lt;a href="https://dev.to/mottalrd/an-introduction-to-event-sourcing-for-rubyists-41e5"&gt;sharing about this years ago&lt;/a&gt; (and keeping it up for me to catch up on!).&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>tutorial</category>
      <category>eventsourcing</category>
    </item>
    <item>
      <title>Building an Event Sourcing System in Rails, Part 1: What is Event Sourcing?</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Mon, 20 Apr 2020 00:54:35 +0000</pubDate>
      <link>https://dev.to/isalevine/building-an-event-sourcing-system-in-rails-part-1-what-is-event-sourcing-46db</link>
      <guid>https://dev.to/isalevine/building-an-event-sourcing-system-in-rails-part-1-what-is-event-sourcing-46db</guid>
      <description>&lt;h1&gt;
  
  
  What is Event Sourcing?
&lt;/h1&gt;

&lt;p&gt;Event Sourcing is a system design pattern that emphasizes recording changes to data via immutable events.&lt;/p&gt;

&lt;p&gt;In other words: &lt;em&gt;every time your data changes&lt;/em&gt;, you save an event to your database with the details. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Those events never change or go away.&lt;/strong&gt; That way, you have a permanent, unchanging history of how your data reached its current state!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html"&gt;Martin Fowler’s important 2005 essay&lt;/a&gt; describes Event Sourcing this way (emphasis mine):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Event Sourcing ensures that &lt;strong&gt;all changes to application state are stored as a sequence of events.&lt;/strong&gt; Not just can we query these events, we can also use the event log to reconstruct past states, and as a foundation to automatically adjust the state to cope with retroactive changes.&lt;br&gt;
…&lt;br&gt;
The fundamental idea of Event Sourcing is that of &lt;strong&gt;ensuring every change to the state of an application is captured in an event object&lt;/strong&gt;, and that these event objects are themselves stored in the sequence they were applied for the same lifetime as the application state itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cool! From this, we get a few key points about Event Sourcing systems that we’ll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes to data are stored as Event objects&lt;/li&gt;
&lt;li&gt;The Events have a fixed chronological order that can be “replayed”&lt;/li&gt;
&lt;li&gt;The Events cannot be changed or deleted (for as long as they might be needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Examples of Event Sourcing
&lt;/h1&gt;

&lt;p&gt;The example app we will be building in these articles will focus on events related to creating and destroying a User model. Consider this situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client sends a request to create a User&lt;/li&gt;
&lt;li&gt;Server receives the request&lt;/li&gt;
&lt;li&gt;Server creates and saves a User&lt;/li&gt;
&lt;li&gt;Server creates and saves an Event, which records the details of the request and the created User&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obviously, the implementation (and order of operations) will be more complex--but at its core, that's the basic Event Sourcing system we'll be creating!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinfowler.com/articles/201701-event-driven.html"&gt;In a 2017 essay, Martin Fowler points out&lt;/a&gt; that version-control systems (like Git) are probably the most common example of Event Sourcing that programmers encounter (emphasis mine):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The core idea of &lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html"&gt;event sourcing&lt;/a&gt; is that whenever we make a change to the state of a system, we record that state change as an event, and we can confidently rebuild the system state by reprocessing the events at any time in the future. The event store becomes the principal source of truth, and the system state is purely derived from it. For programmers, &lt;strong&gt;the best example of this is a version-control system. The log of all the commits is the event store and the working copy of the source tree is the system state.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Rails-specific Event Sourcing, two resources/examples are often cited:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224"&gt;Kickstarter’s minimal Event Sourcing system, described in this blog post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/RailsEventStore/rails_event_store"&gt;Arkency’s RailsEventStore library, which has a number of versatile uses detailed in the GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The focus of these articles will be on following Kickstarter’s example as I create a personal project with event sourcing architecture. &lt;strong&gt;Thanks Kickstarter, you folks are amazing!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Some implications of using an Event Sourcing system
&lt;/h1&gt;

&lt;p&gt;Since working on an event sourcing system at my previous job, I’ve been building a personal project using one too. The key difference is that, at work, I was expanding on an already-existing system (and one that &lt;em&gt;wasn’t&lt;/em&gt; initially designed for event sourcing). &lt;/p&gt;

&lt;p&gt;Now that I’m building one from scratch, I’ve encountered some important considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data is &lt;strong&gt;truly immutable&lt;/strong&gt;—forget about using &lt;code&gt;ActiveRecord.destroy&lt;/code&gt;! Instead, it’s important to design data for permanence, such as including a &lt;code&gt;deleted&lt;/code&gt; field on User models.&lt;/li&gt;
&lt;li&gt;Recording payload dumps of event data can include &lt;strong&gt;potentially storing sensitive data in an unsafe format&lt;/strong&gt; if the event is saved outside of auth features like &lt;code&gt;has_secure_password&lt;/code&gt;—you must be extra cautious about when sensitive data might be exposed, and be ready to manually implement encryption where needed!&lt;/li&gt;
&lt;li&gt;Since events become “the principal source of truth” (&lt;a href="https://martinfowler.com/articles/201701-event-driven.html"&gt;in Martin Fowler’s words&lt;/a&gt;), tons of logic ends up being wrapped in Event objects—naming and organizing code (and making sure it’s &lt;strong&gt;future-proof&lt;/strong&gt;) is more essential than ever! &lt;a href="https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224"&gt;As Philippe Creux at Kickstarter described&lt;/a&gt; (emphasis &lt;em&gt;not&lt;/em&gt; mine):&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Naming is hard.&lt;/strong&gt; And there are so many &lt;strong&gt;immutable&lt;/strong&gt; events and attributes to name. The names you choose now will be the ones stored forever. So take a good dictionary and make sure that you nail down names that are explicit and future-proof.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I hope to explore and share these implications (and future ones that will surely arise!) in these articles, as we walk through how to implement an Event Sourcing system from scratch.&lt;/p&gt;

&lt;h1&gt;
  
  
  Next Up
&lt;/h1&gt;

&lt;p&gt;The next article will go through the basics of creating a Rails app, centered around creating and destroying a User model, that uses basic Event Sourcing architecture. We will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up the Rails app with a &lt;code&gt;User&lt;/code&gt; model and controller&lt;/li&gt;
&lt;li&gt;Building the BaseEvent class architecture (&lt;a href="https://github.com/pcreux/event-sourcing-rails-todo-app-demo"&gt;per Kickstarter’s example&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Implementing &lt;code&gt;Events::User::Created&lt;/code&gt; and &lt;code&gt;Events::User::Destroyed&lt;/code&gt; event classes&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;p&gt;Special thanks to &lt;a href="https://kickstarter.engineering/@pcreux"&gt;Philippe Creux&lt;/a&gt; and &lt;a href="https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224"&gt;Kickstarter&lt;/a&gt; for sharing &lt;a href="https://github.com/pcreux/event-sourcing-rails-todo-app-demo"&gt;their Event Sourcing example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://martinfowler.com/"&gt;Martin Fowler&lt;/a&gt; for his &lt;a href="https://martinfowler.com/articles/201701-event-driven.html"&gt;important writings&lt;/a&gt; on &lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html"&gt;Event Sourcing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://arkency.com/"&gt;Arkency&lt;/a&gt; for their great work with &lt;a href="https://github.com/RailsEventStore/rails_event_store"&gt;the RailsEventStore library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And finally, thanks to fellow Dev.to user &lt;a href="https://dev.to/mottalrd"&gt;Alfredo Motta&lt;/a&gt; for &lt;a href="https://dev.to/mottalrd/an-introduction-to-event-sourcing-for-rubyists-41e5"&gt;sharing about this years ago&lt;/a&gt; (and keeping it up for me to catch up on!).&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>eventsourcing</category>
      <category>designsystem</category>
    </item>
    <item>
      <title>Hi Folks, Good to be Back!</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Sun, 12 Apr 2020 21:26:06 +0000</pubDate>
      <link>https://dev.to/isalevine/hi-folks-good-to-be-back-3o3o</link>
      <guid>https://dev.to/isalevine/hi-folks-good-to-be-back-3o3o</guid>
      <description>&lt;h3&gt;
  
  
  Hi, Dev.to folks! Hope you’ve all been well and are staying safe!
&lt;/h3&gt;

&lt;p&gt;I wanted to do an update/retrospective after my first few months of working at my first software engineering job…but a little while ago, it was part of the big wave of COVID-related layoffs. &lt;/p&gt;

&lt;p&gt;Luckily, I learned a HUGE amount during my 2 1/2 months there, so I’m here to share and explore those lessons!&lt;/p&gt;

&lt;h3&gt;
  
  
  There are three things I plan to write about:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I learned a lot of patterns and techniques for working with Rails and Vue—and I’m currently starting a project, QueerHub, to re-implement them on my own, entirely from scratch!&lt;/li&gt;
&lt;li&gt;Both last year and this year, I’ve accumulated some jobhunting advice, much of which comes from answering questions from other coding bootcamp graduates—I want to share my strategies, especially with respect to navigating mental health issues, queerness, and the current US quarantine&lt;/li&gt;
&lt;li&gt;One of the reasons I love Dev.to is that it’s a place to be my whole-ass queer self—I’m here to share my perspective as a queer trans woman, and ways to be a better ally to people like me while at work, while networking, and in general!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good to be back, everyone—glad you’re all here, and looking forward to catching up! (And picking up some new video game and/or trashy TV recommendations to keep me sane!)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.pinimg.com%2Foriginals%2F7f%2F76%2F10%2F7f76102bedf6de4e34065709d16a9ef8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.pinimg.com%2Foriginals%2F7f%2F76%2F10%2F7f76102bedf6de4e34065709d16a9ef8.gif" alt="anime gif of two characters hugging and crying"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Accurate representation of how much I want to hug right now. &amp;lt;3&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>watercooler</category>
      <category>covid</category>
      <category>jobhunting</category>
    </item>
    <item>
      <title>What Happened to Video Games Having In-Game CLI Consoles?</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Sun, 15 Dec 2019 23:34:02 +0000</pubDate>
      <link>https://dev.to/isalevine/what-happened-to-video-games-having-in-game-cli-consoles-8c4</link>
      <guid>https://dev.to/isalevine/what-happened-to-video-games-having-in-game-cli-consoles-8c4</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CUqeicPu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://i.ytimg.com/vi/qCnVO_1ovcQ/maxresdefault.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CUqeicPu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://i.ytimg.com/vi/qCnVO_1ovcQ/maxresdefault.jpg" alt="screenshot of Skyrim fight with console cheats enabled"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Console command cheats being used in Skyrim&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Okay, sorry for the clickbait-y title...I know in-game consoles were never really &lt;em&gt;common&lt;/em&gt; per se, and I'm writing this because of I've been relying on the console to fix bugs in Skyrim...
&lt;/h3&gt;

&lt;h3&gt;
  
  
  But still, the question stands: why don't we see in-game consoles more frequently in video games today?
&lt;/h3&gt;

&lt;p&gt;My PC version of Skyrim has always been broken and buggy. Having played the same installed version on the same machine since college, it's become more and more unstable as time has gone on, hardware's been upgraded or replaced, and the computer itself has been (less than carefully) moved across the country.&lt;/p&gt;

&lt;p&gt;Recently, I booted it up again to (finally) do a playthrough as a mage. Upon killing the first dragon, I uncovered a new and amazing bug: &lt;em&gt;I can no longer absorb dragon souls&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Luckily, &lt;a href="https://www.google.com/search?sxsrf=ACYBGNSXrKLiKT5pYerz2o2Ou8k9dUFbZw%3A1576448430645&amp;amp;ei=rrH2Xd39JtzE0PEP3K-jsAE&amp;amp;q=skyrim+can%27t+absorb+first+dragon+soul&amp;amp;oq=skyrim+can%27t+absorb+first+dragon+soul&amp;amp;gs_l=psy-ab.3..0i7i30j0j0i333l2.16483.17180..17298...0.2..0.83.528.7......0....1..gws-wiz.......0i71j35i304i39j0i13i30.ng19vDsq-yY&amp;amp;ved=0ahUKEwjdjNyF2LjmAhVcIjQIHdzXCBYQ4dUDCAs&amp;amp;uact=5"&gt;this is a rather common bug&lt;/a&gt;, and the easiest solution is to simply enter the game's console and use the command &lt;code&gt;setstage MQ104 90&lt;/code&gt; to mark the quest as complete. As I defeat other dragons, I have to artificially increase my dragon soul count by 1 using &lt;code&gt;player.modav dragonsouls 1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's learn a bit about what these consoles are, and where they originate! &lt;/p&gt;

&lt;p&gt;To save you some time with trying to google "video game console" without the interference of console machines (PS4, Xbox One, Switch, etc.), here's the &lt;a href="https://en.wikipedia.org/wiki/Console_(video_game_CLI)"&gt;Wikipedia article on console (video game CLI)&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Popular drop-down console box started with Quake (1996), for logs and cheats
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Console_(video_game_CLI)#History"&gt;The most recognizable in-game console is likely traceable to&lt;/a&gt; &lt;a href="https://en.wikipedia.org/wiki/Quake_(video_game)"&gt;Quake (1996)&lt;/a&gt;, the popular first-person shooter and successor to Doom. &lt;/p&gt;

&lt;p&gt;Activated by either ~ (tilde) or ` (grave), the console was a box that dropped down from the top of the screen. &lt;/p&gt;

&lt;p&gt;It displayed both logs for engine events (sound, joysticks, etc.), as well as in-game events (picking up items):&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gIx7NN0V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://steamuserimages-a.akamaihd.net/ugc/596992393158083827/1D1484D35B55F322DA91E4DC02EAC3C39759F884/" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gIx7NN0V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://steamuserimages-a.akamaihd.net/ugc/596992393158083827/1D1484D35B55F322DA91E4DC02EAC3C39759F884/" alt="screenshot of Quake console showing event logs"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Console event log in Quake&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In addition, the Quake console also included codes you could enter for both regular in-game commands, and cheats--here's a &lt;a href="https://quake.fandom.com/wiki/Console_Commands_(Q1)"&gt;list of Quake console commands&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;I personally first encountered Quake's console with &lt;a href="https://www.eecis.udel.edu/~portnoi/quake/quakecom.html#noclip"&gt;&lt;code&gt;noclip&lt;/code&gt;&lt;/a&gt;, a command to allow walking through walls and flying, and &lt;a href="https://www.eecis.udel.edu/~portnoi/quake/quakecom.html#god"&gt;&lt;code&gt;god&lt;/code&gt;&lt;/a&gt;, the god-mode (invincibility) cheat. These still commonly appear in games that have their own in-game consoles!&lt;/p&gt;

&lt;h1&gt;
  
  
  Why aren't consoles like Quake's more common in video games today?
&lt;/h1&gt;

&lt;p&gt;One important thing to know about in-game consoles (like Quake's above) is that &lt;strong&gt;they are NOT windows into the engine's processes, nor the game's source code&lt;/strong&gt;. It's easy to look at a console like this and assume that you're pausing the game's engine and directly accessing/modifying the code that's currently running. &lt;strong&gt;Nope.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;You are not hacking the game's source code by using the in-game console--&lt;em&gt;and this is why &lt;strong&gt;not every game&lt;/strong&gt; has its own console&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Instead, an in-game console &lt;strong&gt;is an additional CLI built into the game that offers a specific set of commands for &lt;em&gt;that&lt;/em&gt; game's console &lt;em&gt;alone&lt;/em&gt;&lt;/strong&gt;. While some game engines may have a set of common commands, &lt;em&gt;their effects are game-specific,&lt;/em&gt; and &lt;em&gt;they must be created by developers&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So, long-story short: most video games &lt;strong&gt;don't&lt;/strong&gt; have an in-game console because &lt;strong&gt;they're a lot of extra effort for developers to make!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Modern example
&lt;/h1&gt;

&lt;p&gt;Luckily, the in-game console lives on in some modern (and very popular) game engines.&lt;/p&gt;

&lt;p&gt;Each of the games in these series/with these engines will usually have an in-game console, with varying numbers of commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bethesda's Creation engine &lt;strong&gt;(Skyrim, Fallout)&lt;/strong&gt; - &lt;a href="https://elderscrolls.fandom.com/wiki/Console_Commands_(Skyrim)"&gt;list of Skyrim console commands&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Valve's Source/GoldSrc engine &lt;strong&gt;(Counter-Strike, Half-Life)&lt;/strong&gt; - &lt;a href="https://developer.valvesoftware.com/wiki/Console_Command_List"&gt;list of console commands common to all Source engine games&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Epic's Unreal engine &lt;strong&gt;(BioShock, Borderlands)&lt;/strong&gt; - &lt;a href="https://docs.unrealengine.com/udk/Three/ConsoleCommands.html"&gt;list of console commands for Unreal Engine 3 games and editor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lots of articles are popping up lately &lt;a href="https://www.pcgamer.com/skyrim-console-commands-let-you-cheat-and-do-other-stuff/"&gt;about replaying some of these games using console commands/cheats&lt;/a&gt;, so now is a great time to dive in and explore the possibilities!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Depending on the game, you may have to enable use of the in-game console (for example, &lt;a href="https://support.steampowered.com/kb_article.php?ref=1040-JWMT-2947"&gt;with Steam games, running them with the &lt;code&gt;-console&lt;/code&gt; flag&lt;/a&gt;), or by otherwise making it available. Just be prepared to do some Googling on the specific game!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note (also):&lt;/strong&gt; Some console (non-PC/Mac) versions of video games do NOT have an in-game console, usually due to complications of accessing it without a keyboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3mGQxlZb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ytimg.com/vi/b4MPEpLTSMc/hqdefault.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3mGQxlZb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ytimg.com/vi/b4MPEpLTSMc/hqdefault.jpg" alt="screenshot of cheats being activated in console in Half-Life"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Cheats being activated in Half-Life via the Steam in-game console&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Links and sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Console_(video_game_CLI)"&gt;Console_(video_game_CLI) Wikipedia article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gamefaqs.gamespot.com/boards/916373-pc/67616731"&gt;GameFaqs thread on in-game consoles, with anecdotal history and examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.eecis.udel.edu/~portnoi/quake/quakecom.html"&gt;Quake console commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://elderscrolls.fandom.com/wiki/Console_Commands_(Skyrim)"&gt;Skyrim console commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.steampowered.com/kb_article.php?ref=1040-JWMT-2947"&gt;Activating console in Steam games&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.valvesoftware.com/wiki/Console_Command_List"&gt;Source engine console commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.unrealengine.com/udk/Three/ConsoleCommands.html"&gt;Unreal engine console commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pcgamer.com/skyrim-console-commands-let-you-cheat-and-do-other-stuff/"&gt;Article on replaying Skyrim with console commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.packtpub.com/making-game-console-unity-part-1/"&gt;Article on adding consoles to Unity games&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Image sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="%5Bhttps://nixyn.creativespeed.ga/skyrim-console-command-show-item-id-minecraft%5D(https://nixyn.creativespeed.ga/skyrim-console-command-show-item-id-minecraft)"&gt;Skyrim&lt;/a&gt; - found via Google images, direct link currently not working&lt;/li&gt;
&lt;li&gt;&lt;a href="https://steamcommunity.com/sharedfiles/filedetails/?id=120426294"&gt;Quake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=b4MPEpLTSMc"&gt;Half-Life&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Got any pieces of in-game console history (or favorite cheats) to share? Please comment below! &amp;lt;3
&lt;/h3&gt;

</description>
      <category>watercooler</category>
      <category>discuss</category>
      <category>devjournal</category>
      <category>videogames</category>
    </item>
    <item>
      <title>Intro to RSpec in Rails (part 2): Improving Tests with `let` and `context` </title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Sun, 08 Dec 2019 19:40:22 +0000</pubDate>
      <link>https://dev.to/isalevine/intro-to-rspec-in-rails-part-2-improving-tests-with-let-and-context-241n</link>
      <guid>https://dev.to/isalevine/intro-to-rspec-in-rails-part-2-improving-tests-with-let-and-context-241n</guid>
      <description>&lt;p&gt;In our last post, we started to write some RSpec tests for our character-generator API, and its &lt;code&gt;RandomCharacterGenerator&lt;/code&gt; service class.&lt;/p&gt;

&lt;p&gt;&lt;a href="%5Bhttps://dev.to/andrewbrown/comment/id4h%5D(https://dev.to/andrewbrown/comment/id4h)"&gt;Dev.to community member Andrew Brown pointed out that several aspects of our code were not written optimally&lt;/a&gt;, so let's take a look at a few of his recommendations!&lt;/p&gt;

&lt;h1&gt;
  
  
  Use &lt;code&gt;let&lt;/code&gt; to wrap your testing variables
&lt;/h1&gt;

&lt;p&gt;In our original test code, we simply created our testing variables inside our &lt;code&gt;describe&lt;/code&gt; block. This is &lt;strong&gt;not correct&lt;/strong&gt;--instead, we are expected to wrap this code in either a &lt;code&gt;before&lt;/code&gt; or &lt;code&gt;let&lt;/code&gt; block. Both of these will help RSpec understand &lt;em&gt;when&lt;/em&gt; to create the variables, and help our tests run correctly.&lt;/p&gt;

&lt;p&gt;Here's the original test code we wrote (with a few omissions for readability):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rails_helper'&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="c1"&gt;# NOTE: Do NOT create your test variables this way!!&lt;/span&gt;
        &lt;span class="n"&gt;rcg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a new Character instance"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="c1"&gt;# For now, we'll ignore the other tests we wrote...&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;So, we need to do something about where we create those &lt;code&gt;starting_database_count&lt;/code&gt;, &lt;code&gt;rcg&lt;/code&gt;, &lt;code&gt;player&lt;/code&gt;, and &lt;code&gt;character&lt;/code&gt; variables!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;before&lt;/code&gt; or &lt;code&gt;let&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;We have two options for wrapping our variable-creation: &lt;code&gt;before&lt;/code&gt; or &lt;code&gt;let&lt;/code&gt; blocks. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;before&lt;/code&gt; is a hook that will run before each test (by default), and thus may be run multiple times when we don't need it to. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;let&lt;/code&gt; is only called when a test needs the variable it creates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, we could rewrite our code two different ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

        &lt;span class="c1"&gt;# OPTION 1 (run before each test):&lt;/span&gt;
        &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;rcg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&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;# OPTION 2 (run only when variables are called in a test):&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;rcg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&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;One resource Andrew pointed me to is &lt;a href="http://www.betterspecs.org/"&gt;BetterSpecs&lt;/a&gt;, an excellent list of guidelines for writing more standardized/readable/maintainable/side-effect-less RSpec tests. Here's what BetterSpecs says about the &lt;code&gt;let&lt;/code&gt; block:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you have to assign a variable instead of using a  &lt;code&gt;before&lt;/code&gt;  block to create an instance variable, use  &lt;code&gt;let&lt;/code&gt;. Using  &lt;code&gt;let&lt;/code&gt;  the variable lazy loads only when it is used the first time in the test and get cached until that specific test is finished. A really good and deep description of what  &lt;code&gt;let&lt;/code&gt;  does can be found in this  &lt;a href="http://stackoverflow.com/questions/5359558/when-to-use-rspec-let/5359979#5359979"&gt;stackoverflow answer&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, &lt;code&gt;let&lt;/code&gt; is the preferred alternative to a &lt;code&gt;before&lt;/code&gt; block. Both of these are methods we can wrap around creating our testing variables. But &lt;code&gt;let&lt;/code&gt; is preferred because it runs more efficiently (and, &lt;a href="https://dev.to/andrewbrown/comment/id4h"&gt;as Andrew pointed out&lt;/a&gt;, has fewer side effects).&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="http://www.betterspecs.org/#let"&gt;Sidenote from BetterSpecs&lt;/a&gt; -- this is how they describe &lt;code&gt;let&lt;/code&gt; working under-the-hood:
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# this:&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Foo&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="c1"&gt;# is very nearly equivalent to this:&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
  &lt;span class="vi"&gt;@foo&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Foo&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;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, &lt;code&gt;let&lt;/code&gt; prevents us from re-instantiating classes over and over! Definitely more efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's go ahead and update our code with &lt;code&gt;let&lt;/code&gt;, and run the test:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rails_helper'&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;rcg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a new Character instance"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&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;And run &lt;code&gt;bundle exec rspec&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle exec rspec
.

Finished in 0.04259 seconds (files took 0.78975 seconds to load)
1 example, 0 failures
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Awesome! Our test is still working (and passing) as expected.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using &lt;code&gt;context&lt;/code&gt; for different cases
&lt;/h1&gt;

&lt;p&gt;Both &lt;a href="https://dev.to/andrewbrown/comment/id4h"&gt;Andrew&lt;/a&gt; and &lt;a href="http://www.betterspecs.org/#contexts"&gt;BetterSpecs recommend using &lt;code&gt;context&lt;/code&gt;&lt;/a&gt; to organize tests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;context&lt;/code&gt; is an alias for &lt;code&gt;describe&lt;/code&gt;, so there is no under-the-hood different. &lt;strong&gt;&lt;code&gt;context&lt;/code&gt; exists solely to make tests more understandable to developers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One common way to implement &lt;code&gt;context&lt;/code&gt; is to use them for different cases, such as "success" and "failure". &lt;/p&gt;

&lt;h2&gt;
  
  
  Use case: &lt;code&gt;context "success"&lt;/code&gt; and &lt;code&gt;context "failure"&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let's add some contexts to our tests to reflect when the &lt;code&gt;new_character&lt;/code&gt; method succeeds or fails, based on the uniqueness of our character's name &lt;em&gt;(a validation which is NOT implemented on the Character model yet)&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rcg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;    
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a new Character instance"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&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;context&lt;/span&gt; &lt;span class="s2"&gt;"failure (non-unique name)"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="c1"&gt;# test code here&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;Now, let's make a &lt;code&gt;duplicate&lt;/code&gt; variable by trying to create a new Character with the same name as our first &lt;code&gt;character&lt;/code&gt;. In our test, we'll also expect that &lt;code&gt;duplicate&lt;/code&gt; is equal to an error message string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rcg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:duplicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;    
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a new Character instance"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&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;context&lt;/span&gt; &lt;span class="s2"&gt;"failure (non-unique name)"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"returns a message that Character is not created"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duplicate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="s2"&gt;"Character not created -- name already exists!"&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;Now, our &lt;code&gt;context "failure (non-unique name)"&lt;/code&gt; has an &lt;code&gt;it&lt;/code&gt; block that instantiates &lt;code&gt;character&lt;/code&gt; again &lt;strong&gt;(remember, the &lt;code&gt;let&lt;/code&gt; variables don't exist until we call them in a test!)&lt;/strong&gt;, and tries to create &lt;code&gt;duplicate&lt;/code&gt; with the same name. Instead of being a Character, &lt;code&gt;duplicate&lt;/code&gt; should equal a string containing our error message.&lt;/p&gt;

&lt;p&gt;Let's run our tests to make sure they work, and are not passing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle exec rspec
.F

Failures:

  1) RandomCharacterGenerator#new_character failure does not create a new Character instance
     Failure/Error: expect(duplicate).to eq "Character not created -- name already exists!"

       expected: "Character not created -- name already exists!"
            got: #&amp;lt;Character id: 2, name: "Ronnie the Rat", strength: 2, dexterity: 3, intelligence: 3, charisma: 1, created_at: "2019-12-08 19:05:55", updated_at: "2019-12-08 19:05:55", player_id: 1&amp;gt;

       (compared using ==)

       Diff:
       @@ -1,2 +1,11 @@
       -"Character not created -- name already exists!"
       +#&amp;lt;Character:0x00007f9e56d37880
       + id: 2,
       + name: "Ronnie the Rat",
       + strength: 2,
       + dexterity: 3,
       + intelligence: 3,
       + charisma: 1,
       + created_at: Sun, 08 Dec 2019 19:05:55 UTC +00:00,
       + updated_at: Sun, 08 Dec 2019 19:05:55 UTC +00:00,
       + player_id: 1&amp;gt;

     # ./spec/services/random_character_generator_spec.rb:63:in `block (4 levels) in &amp;lt;top (required)&amp;gt;'

Finished in 0.1283 seconds (files took 1.76 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/services/random_character_generator_spec.rb:61 # RandomCharacterGenerator#new_character failure does not create a new Character instance
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Perfect--our test works, and is failing because &lt;code&gt;duplicate&lt;/code&gt; is being created as a Character instance, instead of the error-message string we were expecting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's add a uniqueness validation for &lt;code&gt;:name&lt;/code&gt; to our Character model:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /app/models/character.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Character&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;:player&lt;/span&gt;

    &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  And in &lt;code&gt;RandomCharacterGenerator.new_character()&lt;/code&gt;, let's add a &lt;code&gt;rescue&lt;/code&gt; that returns our error-message string:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RandomCharacterGenerator&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_character&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="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="no"&gt;Character&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="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;player: &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tap&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;character&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;rescue&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;RecordInvalid&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Character not created -- name already exists!"&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;h3&gt;
  
  
  Running our tests now:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle exec rspec
..

Finished in 0.07993 seconds (files took 1.86 seconds to load)
2 examples, 0 failures
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Awesome, now our &lt;code&gt;new_character&lt;/code&gt; method has some better error-handling built in, and it's backed up by test coverage!&lt;/p&gt;

&lt;h1&gt;
  
  
  Using &lt;code&gt;it { expect }&lt;/code&gt; syntax with &lt;code&gt;context&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;One final suggestion &lt;a href="https://dev.to/andrewbrown/comment/id4h"&gt;from both Andrew&lt;/a&gt; and &lt;a href="http://www.betterspecs.org/#short"&gt;the BetterSpecs section on keeping descrptions short&lt;/a&gt; is to simplify the &lt;code&gt;it&lt;/code&gt; block's syntax with curly brackets &lt;code&gt;{}&lt;/code&gt; around the &lt;code&gt;expect&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;When used inside a &lt;code&gt;context&lt;/code&gt; block, it can make the test much more expressive and readable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rcg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:duplicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# BEFORE:&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;    
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a new Character instance"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="c1"&gt;# AFTER:&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;    
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&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;The second version reduces our &lt;code&gt;it&lt;/code&gt; block from three lines to one, and completely eliminates the descriptive (and possibly redundant) text &lt;code&gt;"creates a new Character instance"&lt;/code&gt;. Instead, we can read the test as &lt;strong&gt;"the 'success' context expects variable &lt;code&gt;character&lt;/code&gt; to be an instance of Character"&lt;/strong&gt;. Pretty self-descriptive code!&lt;/p&gt;

&lt;h3&gt;
  
  
  However, this strategy is best used for one-expectation tests. Our "failure (non-unique name)" test currently relies on two &lt;code&gt;expect&lt;/code&gt; lines inside the same &lt;code&gt;it&lt;/code&gt; block, so this would NOT work:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rcg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:duplicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# this works:&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"failure (non-unique name)"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"returns a message that Character is not created"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duplicate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="s2"&gt;"Character not created -- name already exists!"&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="c1"&gt;# this does NOT work:&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"failure (non-unique name)"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duplicate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="s2"&gt;"Character not created -- name already exists!"&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;The second option does not work because each &lt;code&gt;it&lt;/code&gt; line creates its own scope, so &lt;code&gt;duplicate&lt;/code&gt; is created as a Character successfully because the previous &lt;code&gt;character&lt;/code&gt; is not instantiated in &lt;code&gt;duplicate's&lt;/code&gt; scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  So, our final successful test code looks like this:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rails_helper'&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rcg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:duplicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;    
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"failure (non-unique name)"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"returns a message that Character is not created"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt;
                &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duplicate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="s2"&gt;"Character not created -- name already exists!"&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt; 
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;As we've covered, using &lt;code&gt;let&lt;/code&gt; to create your testing variables and &lt;code&gt;context&lt;/code&gt; to wrap your test cases can improve both the readability and the efficiency of your RSpec tests. We've also seen one way to simplify &lt;code&gt;it&lt;/code&gt; blocks down to a single line if there's a single expectation!&lt;/p&gt;

&lt;h3&gt;
  
  
  Thanks to Andrew Brown &lt;a href="https://dev.to/andrewbrown/comment/id4h"&gt;for his incredibly helpful comment on my previous post!&lt;/a&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Additional thanks to Masaki Matsuo and Amy Pivo for helping me practice writing better RSpec tests this week!
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Further reading/links/resources about RSpec testing:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.betterspecs.org/"&gt;betterspecs.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/5359979"&gt;https://stackoverflow.com/a/5359979&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ombulabs.com/blog/rails/rspec/ruby/let-vs-instance.html"&gt;https://www.ombulabs.com/blog/rails/rspec/ruby/let-vs-instance.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lmws.net/describe-vs-context-in-rspec"&gt;https://lmws.net/describe-vs-context-in-rspec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/my-issues-with-let"&gt;https://thoughtbot.com/blog/my-issues-with-let&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Got any tips, tricks, or instances for testing with RSpec? Please feel free to comment and share below! &amp;lt;3
&lt;/h3&gt;

</description>
      <category>rails</category>
      <category>rspec</category>
      <category>ruby</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Intro to RSpec in Rails (part 1): Basic Syntax and Strategy for Testing</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Sun, 01 Dec 2019 17:00:46 +0000</pubDate>
      <link>https://dev.to/isalevine/intro-to-rspec-in-rails-basic-syntax-and-strategy-for-testing-3hh6</link>
      <guid>https://dev.to/isalevine/intro-to-rspec-in-rails-basic-syntax-and-strategy-for-testing-3hh6</guid>
      <description>&lt;p&gt;While working on &lt;a href="https://dev.to/isalevine/pros-and-cons-of-ruby-s-tap-method-2m3j"&gt;the character-generator API from my last post&lt;/a&gt;, I found myself writing most of my code in the &lt;code&gt;RandomCharacterGenerator&lt;/code&gt; service object. As the class got more complicated, I realized that I had a very clear idea of the expected behavior--&lt;strong&gt;so this was a perfect opportunity to practice writing RSpec tests for those behaviors!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RSpec is a behavior-driven development (BDD) testing tool for Ruby, and is widely used for testing both plain ol' Ruby and full-on Rails applications. I'm writing this as an intro to some basic patterns and syntax used in RSpec Rails testing, as well as some basic BDD strategy for breaking down code into testable units.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behavior-Driven Development (BDD)
&lt;/h2&gt;

&lt;p&gt;It's hard to understand RSpec without having some idea about behavior-driven development (BDD), so let's start there!&lt;/p&gt;

&lt;p&gt;BDD is a testing philosophy that grew out of test-driven development (TDD). Essentially, the goal of both is: &lt;strong&gt;write tests first, &lt;em&gt;then&lt;/em&gt; write code to make the tests pass!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The idea behind this is that being able to identify and articulate the behaviors you want to test &lt;em&gt;should help guide you when writing the code itself&lt;/em&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you know what behavior you need to see, you can write tests for it&lt;/li&gt;
&lt;li&gt;If you write tests for it, you will know when your code is doing that behavior correctly&lt;/li&gt;
&lt;li&gt;If you know when your code is doing that behavior correctly, you can:

&lt;ul&gt;
&lt;li&gt;write as little code as possible (to get the test to pass)&lt;/li&gt;
&lt;li&gt;refactor with the confidence that the tests will tell you if the behavior is not working anymore&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Applying this to Rails (or Ruby more broadly, or object-oriented programming languages in general), these are the main steps we will use:&lt;br&gt;
&lt;strong&gt;1. Decide on the class/method/behavior to test&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2. Write tests for that class/method/behavior&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;3. Write code to make the tests pass&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;4. When refactoring your code, make sure the tests still pass&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  RSpec
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Adding RSpec to a new or existing Rails app
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/a/6729210"&gt;This is an excellent StackOverflow discussion for both starting a new Rails app with RSpec, and migrating an existing Rails app to RSpec (from &lt;code&gt;test-unit&lt;/code&gt;, the default Rails test tool)&lt;/a&gt;. The following instructions are pulled from the thread's answers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Starting a new Rails app with no test tool, and adding RSpec
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/a/6728831"&gt;Per user Zabba&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;at command line:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails new MYAPP -T # The -T option tells rails not to include Test::Unit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;in Gemfile:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem 'rspec-rails'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;at command line:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle install
rails g rspec:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Migrate an existing Rails app with default &lt;code&gt;test-unit&lt;/code&gt; to RSpec
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/a/6729210"&gt;Per user Sayuj&lt;/a&gt;: &lt;br&gt;
&lt;strong&gt;NOTE: This does include deleting all existing files in &lt;code&gt;test-unit&lt;/code&gt;'s directory! Make sure to back up or migrate any existing tests before following these steps!!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create your new rails application as:&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails new &amp;lt;app_name&amp;gt; -T
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Or remove your  &lt;code&gt;test&lt;/code&gt;  directory from your existing application:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rm -rf test/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Make an entry in your Gemfile:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem 'rspec-rails'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;From the command line install the gem&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;From the command line install rspec into your application:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails g rspec:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Now your rails application uses RSpec instead of test-unit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Running RSpec tests
&lt;/h2&gt;

&lt;p&gt;To run all RSpec tests in your Rails project, use this console command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle exec rspec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackoverflow.com/a/6116744"&gt;Per StackOverflow user apneadiving&lt;/a&gt;, you can also run individual tests by specifying the filepath and line number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle exec rspec ./spec/controllers/groups_controller_spec.rb:42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  RSpec building blocks: &lt;code&gt;describe&lt;/code&gt; and &lt;code&gt;it&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In RSpec, the main building blocks for test suites are &lt;code&gt;describe&lt;/code&gt; (for grouping) and &lt;code&gt;it&lt;/code&gt; (for tests). We write both of them as &lt;code&gt;do...end&lt;/code&gt; blocks, where we do things like instantiate classes, set variables, and write individual tests.&lt;/p&gt;

&lt;p&gt;Here's the first test from &lt;code&gt;random_character_generator_spec.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="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="c1"&gt;# NOTE: Do NOT create your test variables this way!! (See comments for why.) This is just an example for readability...&lt;/span&gt;
        &lt;span class="n"&gt;rcg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a new Character instance"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&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;Let's walk through this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At the top, we have &lt;code&gt;RSpec.describe &amp;lt;ClassName&amp;gt; do&lt;/code&gt;. This encapsulates our tests for the &lt;code&gt;RandomCharacterGenerator&lt;/code&gt; service object class.&lt;/li&gt;
&lt;li&gt;Underneath, we have &lt;code&gt;describe "#method_name" do&lt;/code&gt;. This encapsulates our tests for the &lt;code&gt;new_character&lt;/code&gt; method. It is a Rails convention to add &lt;code&gt;#&lt;/code&gt; before this--that way, the test output will read &lt;code&gt;RandomCharacterGenerator#new_character&lt;/code&gt;, which helps us know what we're testing.&lt;/li&gt;
&lt;li&gt;Inside that &lt;code&gt;describe&lt;/code&gt; block for our method, we instantiate a couple objects to test, and run the &lt;code&gt;new_character&lt;/code&gt; method so we can assign its output to the variable &lt;code&gt;character&lt;/code&gt;. This will give us all the objects we need to test the method's behavior. 
(&lt;strong&gt;NOTE: This is NOT the right way to instantiate variables in an RSpec test! It is here only for an introductory example. See &lt;a href="https://dev.to/andrewbrown/comment/id4h"&gt;this excellent comment from Andrew Brown&lt;/a&gt; [and &lt;a href="https://dev.to/isalevine/intro-to-rspec-in-rails-part-2-improving-tests-with-let-and-context-241n"&gt;the next post in this series&lt;/a&gt;] to see why and learn the proper alternatives!&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Finally, we have an &lt;code&gt;it "description of expected behavior" do&lt;/code&gt; block. This is where we write the test code logic! In a very expressive style, we see that the test itself &lt;strong&gt;expects the variable &lt;code&gt;character&lt;/code&gt; to be an instance of the class &lt;code&gt;Character&lt;/code&gt;&lt;/strong&gt;. If this line evaluates to True, our test will pass--otherwise, it will fail!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting clear &lt;code&gt;expect&lt;/code&gt;ations
&lt;/h2&gt;

&lt;p&gt;As we saw above, RSpec provides an expressive domain-specific language (DSL) to write our tests with. The idea here is that methods can be chained together in a way that sounds very close to plain English. The code &lt;code&gt;expect(character).to be_an_instance_of Character&lt;/code&gt; almost reads like a real sentence!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The most important syntax is &lt;code&gt;expect(foo).to&lt;/code&gt;, because this creates the test itself!&lt;/strong&gt; It's also important because &lt;em&gt;we must know what &lt;strong&gt;one specific thing&lt;/strong&gt; we're testing&lt;/em&gt; (foo) in order to write the test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Here are some common syntax options, &lt;a href="https://devhints.io/rspec"&gt;all sourced from the excellent resource RSpec Cheatsheet by @rstacruz&lt;/a&gt;:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Equal value (or not)&lt;/strong&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;not_to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Math comparisons&lt;/strong&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;expect&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="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="n"&gt;expect&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="nf"&gt;to&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;expect&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="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="n"&gt;expect&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="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_between&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="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_within&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Type-specific&lt;/strong&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;expect&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="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_zero&lt;/span&gt;    &lt;span class="c1"&gt;# FixNum#zero?&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_empty&lt;/span&gt;   &lt;span class="c1"&gt;# Array#empty?&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_key&lt;/span&gt;   &lt;span class="c1"&gt;# Hash#has_key?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Objects&lt;/strong&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;MyClass&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_a_kind_of&lt;/span&gt; &lt;span class="no"&gt;MyClass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Errors&lt;/strong&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;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;raise_error&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;raise_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ExceptionName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/msg/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Enumerables&lt;/strong&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="kp"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;object&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have&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="nf"&gt;things&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_at_least&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;things&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_at_most&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;things&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common RSpec strategy: (1) pick a class, (2) pick a method, (3) outline expected behaviors, (4) write one &lt;code&gt;it&lt;/code&gt; block per behavior
&lt;/h2&gt;

&lt;p&gt;In the example test above, our three layers of &lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;it&lt;/code&gt; blocks correspond to increasingly specific things we're testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At the highest level, we have the class &lt;code&gt;RandomCharacterGenerator&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Within that class, we have the method &lt;code&gt;new_character&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Within that method, we have the expected behavior "creates a new Character instance"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This maps well to the overall BDD strategy we outlined earlier. With RSpec specifically, we can translate this into four steps:&lt;br&gt;
&lt;strong&gt;1. Pick a class to test&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2. Pick a method on the class to test&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;3. Outline expected behaviors from that method &lt;em&gt;(input/output, object/state mutations, errors, etc.)&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;4. Write one &lt;code&gt;it&lt;/code&gt; block per behavior. This block can contain ONE or MANY &lt;code&gt;expect(foo).to&lt;/code&gt; tests &lt;em&gt;(to cover specific values, test/edge cases, etc.)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rails makes picking classes straightforward--choose a model, a controller, a service object, anything defined as a class!&lt;/p&gt;

&lt;p&gt;Ideally, you'll want to test the functionality of individual methods, as well as an overall input/output from a chain of methods. Doing both will help give you confidence that you know what each step of your code is doing (and provide a safety net for catching errors when refactoring).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In BDD, it is very important to think of expected behaviors AHEAD OF TIME!&lt;/strong&gt; This is good practice to reinforce approaching your code with a deliberate plan. Be able to break down your methods (and overall functionality) into individual behaviors, as well as test cases (both expected and edge cases).&lt;/p&gt;
&lt;h1&gt;
  
  
  Use case: character-generator API
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Here's a review of &lt;a href="https://dev.to/isalevine/pros-and-cons-of-ruby-s-tap-method-2m3j"&gt;the character-generator API from my last post&lt;/a&gt; (with some recent refactors):
&lt;/h2&gt;

&lt;p&gt;Our schema includes a &lt;code&gt;Character&lt;/code&gt; model, which belongs to a &lt;code&gt;Player&lt;/code&gt; model. Characters have a name string and four stats as integers (strength, dexterity, intelligence, charisma), and a foreign key for their Player. Players have a user name and a display name, both strings.&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;# /db/schema.rb&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;Schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;version: &lt;/span&gt;&lt;span class="mi"&gt;2019_11_24_071655&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="s2"&gt;"characters"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: :cascade&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;string&lt;/span&gt; &lt;span class="s2"&gt;"name"&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="s2"&gt;"strength"&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="s2"&gt;"dexterity"&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="s2"&gt;"intelligence"&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="s2"&gt;"charisma"&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;datetime&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&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;datetime&lt;/span&gt; &lt;span class="s2"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&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;integer&lt;/span&gt; &lt;span class="s2"&gt;"player_id"&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="s2"&gt;"player_id"&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;"index_characters_on_player_id"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="s2"&gt;"players"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: :cascade&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;string&lt;/span&gt; &lt;span class="s2"&gt;"user_name"&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="s2"&gt;"display_name"&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;datetime&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&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;datetime&lt;/span&gt; &lt;span class="s2"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&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="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;add_foreign_key&lt;/span&gt; &lt;span class="s2"&gt;"characters"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"players"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;RandomCharacterGenerator&lt;/code&gt; service object is a class that handles all of the logic to generate a new Character, and assign their stats points randomly based on the available &lt;code&gt;@points_pool&lt;/code&gt; and &lt;code&gt;@max_roll&lt;/code&gt; attributes. There's also an attribute for &lt;code&gt;@stats_array&lt;/code&gt; that contains strings for the four Character attributes. (Yes, I know that was lazy of me...)&lt;/p&gt;

&lt;p&gt;The public method &lt;code&gt;new_character&lt;/code&gt; returns a new Character instance with its stats filled in. There is also a private method, &lt;code&gt;roll_stats&lt;/code&gt;, that handles the stat-randomizing:&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/services/random_character_generator.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RandomCharacterGenerator&lt;/span&gt;
    &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:max_roll&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;@stats_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"strength"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"dexterity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"intelligence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"charisma"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="vi"&gt;@points_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;
        &lt;span class="vi"&gt;@max_roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&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;new_character&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="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# more code needed here to make tests pass&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# more code needed here to make tests pass&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;For this article, we'll group all our tests together by the method &lt;code&gt;new_character&lt;/code&gt;, as we're concerned with the output Character it returns. Because this method relies on the private method &lt;code&gt;roll_stats&lt;/code&gt;, we &lt;em&gt;are&lt;/em&gt; indirectly testing that too. However, some of these tests &lt;em&gt;could&lt;/em&gt; be rewritten to be directly attached to &lt;code&gt;roll_stats&lt;/code&gt; as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's go through the RSpec strategy steps above:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Pick a class:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RandomCharacterGenerator&lt;/code&gt; service object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Pick a method on the class:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;new_character&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Outline expected behaviors:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It creates a new Character instance&lt;/li&gt;
&lt;li&gt;It randomly allocates all stat points (9) between four Character stats (strength, dexterity, intelligence, charisma)&lt;/li&gt;
&lt;li&gt; It allocates stat points so stat values are between 1 and max roll (6)&lt;/li&gt;
&lt;li&gt;It saves the Character to the database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Write one &lt;code&gt;it&lt;/code&gt; block per behavior:&lt;/strong&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="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="c1"&gt;# NOTE: Do NOT create your test variables this way!! (See comments for why.) This is just an example for readability...&lt;/span&gt;
        &lt;span class="n"&gt;rcg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a new Character instance"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="c1"&gt;# expect(x).to&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"randomly allocates all &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;points_pool&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; stat points between &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="c1"&gt;# expect(x).to&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"allocates stat points so stat values are between 1 and max roll (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_roll&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="c1"&gt;# expect(x).to&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"saves the Character to the database"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="c1"&gt;# expect(x).to&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;h2&gt;
  
  
  Creating our tests
&lt;/h2&gt;

&lt;p&gt;Let's see our tests by running &lt;code&gt;bundle exec rspec&lt;/code&gt;. This will execute all the tests available, and return the results to our console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle exec rspec
....

Finished in 0.01019 seconds (files took 0.8859 seconds to load)
4 examples, 0 failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our tests are technically passing, because we haven't filled them in yet! Let's build them one-by-one.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;it "creates a new Character instance"&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We can test whether a variable is an instance of a particular class with &lt;code&gt;be_an_instance_of&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="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a new Character instance"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;bundle exec rspec&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failures:

  1) RandomCharacterGenerator#new_character creates a new Character instance
     Failure/Error: expect(character).to be_an_instance_of Character
       expected nil to be an instance of Character
     # ./spec/services/random_character_generator_spec.rb:70:in `block (3 levels) in &amp;lt;top (required)&amp;gt;'

Finished in 0.03433 seconds (files took 0.91909 seconds to load)
4 examples, 1 failure

Failed examples:

rspec ./spec/services/random_character_generator_spec.rb:69 # RandomCharacterGenerator#new_character creates a new Character instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now we're ready to fill in the code in &lt;code&gt;new_character&lt;/code&gt; to make this test pass. &lt;strong&gt;This is the heart of BDD (and TDD): write the tests first, then write the code!&lt;/strong&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="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_character&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="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="no"&gt;Character&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="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;player: &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tap&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;character&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="c1"&gt;# roll_stats() will be called here&lt;/span&gt;
            &lt;span class="c1"&gt;# we will save! the character here&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 will make our tests pass (even though &lt;code&gt;roll_stats&lt;/code&gt; doesn't do anything yet). 1 of 4 complete!&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;it "randomly allocates all 9 stat points between (strength, dexterity, intelligence, charisma)"&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Since we have the stats we want to check stored in &lt;code&gt;@stats_array&lt;/code&gt;, we can use &lt;code&gt;reduce&lt;/code&gt; to iterate through it, access &lt;code&gt;character[stat]&lt;/code&gt; to check its value, and add up the integers for each stat. We can then use that with &lt;code&gt;expect(x).to eq value&lt;/code&gt; to check for equality:&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;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"randomly allocates all &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;points_pool&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; stat points between &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;points_pool&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 run &lt;code&gt;bundle exec rspec&lt;/code&gt; to see what the failing test outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failures:

  1) RandomCharacterGenerator#new_character randomly allocates all 9 stat points between ["strength", "dexterity", "intelligence", "charisma"]
     Failure/Error: expect(rcg.stats_array.reduce(0) { |points, stat| points += character[stat] }).to eq rcg.points_pool

     TypeError:
       nil can't be coerced into Integer
     # ./spec/services/random_character_generator_spec.rb:74:in `+'
     # ./spec/services/random_character_generator_spec.rb:74:in `block (4 levels) in &amp;lt;top (required)&amp;gt;'
     # ./spec/services/random_character_generator_spec.rb:74:in `each'
     # ./spec/services/random_character_generator_spec.rb:74:in `reduce'
     # ./spec/services/random_character_generator_spec.rb:74:in `block (3 levels) in &amp;lt;top (required)&amp;gt;'

Finished in 0.01359 seconds (files took 0.86408 seconds to load)
4 examples, 1 failure

Failed examples:

rspec ./spec/services/random_character_generator_spec.rb:73 # RandomCharacterGenerator#new_character randomly allocates all 9 stat points between ["strength", "dexterity", "intelligence", "charisma"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since nothing is assigned to each &lt;code&gt;character[stat]&lt;/code&gt; yet, we're getting an error saying nil cannot be coerced into an integer for addition. Let's add some code to the &lt;code&gt;roll_stats&lt;/code&gt; method, and call it inside &lt;code&gt;new_character&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="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_character&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="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="no"&gt;Character&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="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;player: &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tap&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;character&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# we will save! the character here&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;stats_array&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;stat&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;
            &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;roll&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;Run &lt;code&gt;bundle exec rspec&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failures:

  1) RandomCharacterGenerator#new_character randomly allocates all 9 stat points between ["strength", "dexterity", "intelligence", "charisma"]
     Failure/Error: expect(rcg.stats_array.reduce(0) { |points, stat| points += character[stat] }).to eq rcg.points_pool

       expected: 9
            got: 20

       (compared using ==)
     # ./spec/services/random_character_generator_spec.rb:74:in `block (3 levels) in &amp;lt;top (required)&amp;gt;'

Finished in 0.02692 seconds (files took 0.84623 seconds to load)
4 examples, 1 failure

Failed examples:

rspec ./spec/services/random_character_generator_spec.rb:73 # RandomCharacterGenerator#new_character randomly allocates all 9 stat points between ["strength", "dexterity", "intelligence", "charisma"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, we've got a new error! The value we're adding up with &lt;code&gt;reduce&lt;/code&gt; isn't matching the expected value (9). Let's add a bit more code to ensure that &lt;code&gt;@points_pool&lt;/code&gt; isn't exceeded by each &lt;code&gt;rand(1..max_roll)&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="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;stats_array&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;stat&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;
                &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;

            &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;
            &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;roll&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;Awesome, our test will now pass!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;it "allocates stat points so they do not exceed max roll 6"&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;For this, let's write four separate &lt;code&gt;expect&lt;/code&gt; tests in the same &lt;code&gt;it&lt;/code&gt; block. Each one will test an individual &lt;code&gt;character[stat]&lt;/code&gt;, and the test will only pass if &lt;strong&gt;all four &lt;code&gt;expects&lt;/code&gt; are true&lt;/strong&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="c1"&gt;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"allocates stat points so stat values are between 1 and max roll (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_roll&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strength&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_between&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;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dexterity&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_between&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;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;intelligence&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_between&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;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charisma&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_between&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;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check out our result with &lt;code&gt;bundle exec rspec&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failures:

  1) RandomCharacterGenerator#new_character allocates stat points so stat values are between 1 and max roll (6)
     Failure/Error: expect(character.strength).to be_between(1, rcg.max_roll)
       expected 8 to be between 1 and 6 (inclusive)
     # ./spec/services/random_character_generator_spec.rb:78:in `block (3 levels) in &amp;lt;top (required)&amp;gt;'

Finished in 0.04061 seconds (files took 0.91006 seconds to load)
4 examples, 1 failure

Failed examples:

rspec ./spec/services/random_character_generator_spec.rb:77 # RandomCharacterGenerator#new_character allocates stat points so stat values are between 1 and max roll (6)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, so our rolls are going too high! (However, it IS possible for the stats to randomly all be in the correct range--&lt;strong&gt;beware of false positives!&lt;/strong&gt;) &lt;/p&gt;

&lt;p&gt;Change &lt;code&gt;roll_stats&lt;/code&gt; so that our &lt;code&gt;rand()&lt;/code&gt; call cannot exceed &lt;code&gt;@max_roll&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="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;stats_array&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;stat&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;
                &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;

            &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;
            &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;roll&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;Perfect! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sidenote: the final code to allocate points across an &lt;em&gt;arbitrary&lt;/em&gt; number of possible stats has &lt;code&gt;roll_stats&lt;/code&gt; written to be this (and debugged using &lt;code&gt;.tap&lt;/code&gt;!), which is recognizable from the previous article:&lt;/strong&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="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_index&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;stat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"roll: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;r&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;remaining_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;stats_array&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;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"remaining_stats: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;r&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"points_pool (before): &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;points_pool&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt;
                &lt;span class="n"&gt;max_points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_points&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;max_points&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;roll&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;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"character[&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"points_pool (after): &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&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;Run the tests again, and they will still pass!&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;it "saves the Character to the database"&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We'll check if our Character is successfully saved to the database by checking if the number of Character records stored in the database has increased by 1.&lt;/p&gt;

&lt;p&gt;To start, let's get a &lt;code&gt;starting_database_count&lt;/code&gt; variable set up in our &lt;code&gt;describe "#new_character&lt;/code&gt; block. We'll use the ActiveRecord &lt;code&gt;.count&lt;/code&gt; method on the &lt;code&gt;Character&lt;/code&gt; model to get this number:&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;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#new_character"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="c1"&gt;# NOTE: Do NOT create your test variables this way!! (See comments for why.) This is just an example for readability...&lt;/span&gt;
        &lt;span class="n"&gt;starting_database_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;

        &lt;span class="n"&gt;rcg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RandomCharacterGenerator&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;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Player&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;user_name: &lt;/span&gt;&lt;span class="s2"&gt;"Ronald McDonald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;display_name: &lt;/span&gt;&lt;span class="s2"&gt;"Mac"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ronnie the Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="o"&gt;...&lt;/span&gt;

        &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"saves the Character to the database"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="c1"&gt;# expect(x).to&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;Now, we can use &lt;code&gt;expect(x).to eq value&lt;/code&gt; to check if the &lt;code&gt;Character.count&lt;/code&gt; value &lt;em&gt;after&lt;/em&gt; calling &lt;code&gt;new_character&lt;/code&gt; has increased by 1:&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;# /spec/services/random_character_generator_spec.rb&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"saves the Character to the database"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;starting_database_count&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see what the failing test outputs with &lt;code&gt;bundle exec rspec&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failures:

  1) RandomCharacterGenerator#new_character saves the Character to the database
     Failure/Error: expect(Character.count).to eq (starting_database_count + 1)

       expected: 1736
            got: 1735

       (compared using ==)
     # ./spec/services/random_character_generator_spec.rb:87:in `block (3 levels) in &amp;lt;top (required)&amp;gt;'

Finished in 0.02612 seconds (files took 0.88349 seconds to load)
4 examples, 1 failure

Failed examples:

rspec ./spec/services/random_character_generator_spec.rb:86 # RandomCharacterGenerator#new_character saves the Character to the database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, so our Character isn't being saved. Let's toss a &lt;code&gt;save!&lt;/code&gt; call in the &lt;code&gt;.tap&lt;/code&gt; block called on &lt;code&gt;Character.new&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="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_character&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="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="no"&gt;Character&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="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;player: &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tap&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;character&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&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;And our final test output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle exec rspec
....

Finished in 0.01343 seconds (files took 0.86781 seconds to load)
4 examples, 0 failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooray! We now have test coverage for our &lt;code&gt;RandomCharacterGenerator#new_character&lt;/code&gt; method!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;RSpec is a great tool to help you familiarize yourself with behavior-driven development and testing in Rails. &lt;/p&gt;

&lt;p&gt;I highly recommend reading through the resources below, especially the top few (including the RubyGuides tutorial, an excellent intro tutorial by fellow Flatiron alum Aliya Lewis, and the Devhints RSpec cheatsheet!).&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading/links/resources about RSpec testing:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rubyguides.com/2018/07/rspec-tutorial/"&gt;https://www.rubyguides.com/2018/07/rspec-tutorial/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aliyalewis/testing-the-waters-with-rspec-23ga"&gt;https://dev.to/aliyalewis/testing-the-waters-with-rspec-23ga&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aliyalewis/testing-the-waters-with-rspec-part-2-30po"&gt;https://dev.to/aliyalewis/testing-the-waters-with-rspec-part-2-30po&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devhints.io/rspec"&gt;https://devhints.io/rspec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/6728618/how-can-i-tell-rails-to-use-rspec-instead-of-test-unit-when-creating-a-new-rails/6729210#6729210"&gt;https://stackoverflow.com/questions/6728618/how-can-i-tell-rails-to-use-rspec-instead-of-test-unit-when-creating-a-new-rails/6729210#6729210&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/6116668/how-to-run-a-single-rspec-test"&gt;https://stackoverflow.com/questions/6116668/how-to-run-a-single-rspec-test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Behavior-driven_development"&gt;https://en.wikipedia.org/wiki/Behavior-driven_development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Got any tips, tricks, or instances for testing with RSpec? Please feel free to comment and share below! &amp;lt;3
&lt;/h3&gt;

</description>
      <category>rails</category>
      <category>rspec</category>
      <category>ruby</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Pros and Cons of Ruby's .tap Method</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Mon, 25 Nov 2019 02:26:29 +0000</pubDate>
      <link>https://dev.to/isalevine/pros-and-cons-of-ruby-s-tap-method-2m3j</link>
      <guid>https://dev.to/isalevine/pros-and-cons-of-ruby-s-tap-method-2m3j</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdata.whicdn.com%2Fimages%2F105099972%2Foriginal.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdata.whicdn.com%2Fimages%2F105099972%2Foriginal.gif" alt="anime gif of water running from a faucet" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During a recent technical challenge, I got the chance to learn about and use Ruby's &lt;code&gt;.tap&lt;/code&gt; method. This method allows you to "tap" into the result of a method call, usually to &lt;strong&gt;perform additional operations on that result&lt;/strong&gt;, or to &lt;strong&gt;debug that result (i.e. by printing to the console)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A quick Google search for &lt;code&gt;ruby tap method&lt;/code&gt; will return a lot of articles and StackOverflow discussions about when to use &lt;code&gt;.tap&lt;/code&gt;. There's a pretty clear &lt;a href="https://stackoverflow.com/a/17493351" rel="noopener noreferrer"&gt;divide between folks&lt;/a&gt; &lt;a href="https://stackoverflow.com/a/30221768" rel="noopener noreferrer"&gt;who think it's useful for communicating intent&lt;/a&gt; &lt;a href="https://medium.com/aviabird/ruby-tap-that-method-90c8a801fd6a" rel="noopener noreferrer"&gt;and thus improving readability&lt;/a&gt;, and others &lt;a href="https://redningja.com/dev/rubys-object-tap-a-better-use" rel="noopener noreferrer"&gt;who think it is overly-used&lt;/a&gt; / &lt;a href="https://www.ruby-forum.com/t/tap-method-good-or-bad-practice/228410" rel="noopener noreferrer"&gt;rarely-useful syntactic sugar&lt;/a&gt; &lt;a href="https://stackoverflow.com/a/17494191" rel="noopener noreferrer"&gt;that harms readability&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I'll explore some of the pros and cons of the &lt;code&gt;.tap&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F66.media.tumblr.com%2F4969e0b880e1d90247561146c6e548e8%2Ftumblr_n8dt1aWi0C1sg6db3o1_500.gifv" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F66.media.tumblr.com%2F4969e0b880e1d90247561146c6e548e8%2Ftumblr_n8dt1aWi0C1sg6db3o1_500.gifv" alt="anime gif of water pouring from faucet into two cupped hands" width="500" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What is the .tap method?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://docs.ruby-lang.org/en/2.4.0/Object.html#method-i-tap" rel="noopener noreferrer"&gt;From the Ruby documentation for Object&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;tap{|x|...} → obj&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yields self to the block, and then returns self. The primary purpose of this method is to “tap into” a method chain, in order to perform operations on intermediate results within the chain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, when we call &lt;code&gt;.tap&lt;/code&gt; on an Object, we can insert extra operations before any other code executes. This is especially useful with things like ActiveRecord creation/lookups, where we may immediately want to do something to a new/found record.&lt;/p&gt;

&lt;p&gt;For example, if we have a library app with Book and Checkout models, we could check for an existing Checkout record with &lt;code&gt;Checkout.find_by(book: book)&lt;/code&gt;, then use &lt;code&gt;.tap&lt;/code&gt; to create a block to manipulate the found Checkout (and the Book we used to find it!):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;book: &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;tap&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;checkout&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;checkout&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;check_in_date: &lt;/span&gt;&lt;span class="n"&gt;date&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="nf"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;checked_out: &lt;/span&gt;&lt;span class="kp"&gt;false&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;h2&gt;
  
  
  Pros
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjyg6attdzl9fh51bejx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjyg6attdzl9fh51bejx.gif" alt="gif of cat drinking water from a faucet" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;wrap ActiveRecord creation/lookup patterns in a block that communicates intent&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/a/17493351" rel="noopener noreferrer"&gt;per this StackOverflow example&lt;/a&gt;, this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&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;tap&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;u&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_profile&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_credit_card&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ship_out_item&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_email_confirmation&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blahblahyougetmypoint&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;could be easier to understand at a glance than seeing this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_profile&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_credit_card&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ship_out_item&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_email_confirmation&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blahblahyougetmypoint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;provide a way to debug code step-by-step (as an alternative to &lt;code&gt;pry&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=""&gt;Again from the Ruby documentation for &lt;code&gt;.tap&lt;/code&gt;&lt;/a&gt;, this example is designed to write step-by-step outputs to the console to help us debug:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&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;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"original: &lt;/span&gt;&lt;span class="si"&gt;#{&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;inspect&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&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;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"array: &lt;/span&gt;&lt;span class="si"&gt;#{&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;inspect&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&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;|&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;2&lt;/span&gt;&lt;span class="o"&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="nf"&gt;tap&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;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"evens: &lt;/span&gt;&lt;span class="si"&gt;#{&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;inspect&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="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;x&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="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&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;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"squares: &lt;/span&gt;&lt;span class="si"&gt;#{&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;inspect&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;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cons
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facgkrzvhpejzlri1qj79.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facgkrzvhpejzlri1qj79.gif" alt="gif of polygon-rendered skeleton in a bathtub turning a faucet handle" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;limited use because sometimes ActiveRecord patterns are more recognizable/readable than the .tap syntax&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://redningja.com/dev/rubys-object-tap-a-better-use" rel="noopener noreferrer"&gt;per this soapbox rant, the "traditional" pattern may be more recognizable&lt;/a&gt; to people familiar with Rails, despite (or perhaps because of) the redundant return of &lt;code&gt;self&lt;/code&gt; at the end:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SomeClass&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;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blah&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;obj&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;when debugging, must carefully format to be readable without disrupting the rest of the code (as intended)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://redningja.com/dev/rubys-object-tap-a-better-use" rel="noopener noreferrer"&gt;per the same article as the previous con, using &lt;code&gt;.tap&lt;/code&gt; to debug chains of ActiveRecord methods&lt;/a&gt; is very useful, and doesn't get in the way of reading the code it's testing--but writing this code, and &lt;em&gt;keeping it tidy and readable&lt;/em&gt; while maintaining/changing it, is extra effort:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;                      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Users so far: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;non_admin&lt;/span&gt;                   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Users so far: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at_least_years_old&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Users so far: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;residing_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'USA'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Use case: character-generator API
&lt;/h1&gt;

&lt;p&gt;In this use case, I build a character-generator API where Players can create randomly-generated Characters. Here, we will see two places to (potentially) use &lt;code&gt;.tap&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When creating a new Character instance, to more clearly communicate our intent behind several variables, and to reduce some redundant code&lt;/li&gt;
&lt;li&gt;When using a set number of points to assign to a Character's stats, while keeping track of (a) the random "6-sided die roll", (b) the remaining number of points, and (c) the Character's current and remaining stats.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  API overview: models and relationships
&lt;/h2&gt;

&lt;p&gt;Our two main models will be Players and Characters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Players&lt;/strong&gt; simply have a &lt;code&gt;user_name&lt;/code&gt; and a &lt;code&gt;display_name&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /db/schema.rb&lt;/span&gt;

  &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="s2"&gt;"players"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: :cascade&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;string&lt;/span&gt; &lt;span class="s2"&gt;"user_name"&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="s2"&gt;"display_name"&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;datetime&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&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;datetime&lt;/span&gt; &lt;span class="s2"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Characters&lt;/strong&gt; have a name, 4 stats, and a Player they belong to:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /db/schema.rb&lt;/span&gt;

  &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="s2"&gt;"characters"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: :cascade&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;string&lt;/span&gt; &lt;span class="s2"&gt;"name"&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="s2"&gt;"strength"&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="s2"&gt;"dexterity"&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="s2"&gt;"intelligence"&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="s2"&gt;"charisma"&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;datetime&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&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;datetime&lt;/span&gt; &lt;span class="s2"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&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;integer&lt;/span&gt; &lt;span class="s2"&gt;"player_id"&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="s2"&gt;"player_id"&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;"index_characters_on_player_id"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  RandomCharacterGenerator service object
&lt;/h2&gt;

&lt;p&gt;We will be using &lt;code&gt;.tap&lt;/code&gt; inside a service object, &lt;code&gt;RandomCharacterGenerator&lt;/code&gt;, which will provide a &lt;code&gt;.new_character()&lt;/code&gt; method that takes in a name and player, and automatically creates and saves a Character in the database:&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/services/random_character_generator.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RandomCharacterGenerator&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_character&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="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Character&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="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;player: &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;stats_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"strength"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"dexterity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"intelligence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"charisma"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;
        &lt;span class="n"&gt;max_roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
        &lt;span class="n"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
        &lt;span class="n"&gt;character&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_index&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;stat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;remaining_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stats_array&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;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt;
                &lt;span class="n"&gt;max_points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_points&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;max_points&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's look at some places where we can use &lt;code&gt;.tap&lt;/code&gt; to &lt;strong&gt;(potentially)&lt;/strong&gt; tie our code together more clearly!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F021e5o0gi7dmicpikza4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F021e5o0gi7dmicpikza4.gif" alt="gif of popeye the sailor tying two streams of water together in a knot" width="252" height="168"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Case 1: communicating intent and context of variables during &lt;code&gt;Character.new()&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;In &lt;code&gt;.new_character()&lt;/code&gt;, we're performing several operations as soon as we create a new Character instance. Right after &lt;code&gt;Character.new()&lt;/code&gt;, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;define 3 variables (stats_array, points_pool, max_roll)&lt;/li&gt;
&lt;li&gt;call the private method &lt;code&gt;roll_stats()&lt;/code&gt; with those 3 variables&lt;/li&gt;
&lt;li&gt;call &lt;code&gt;save!&lt;/code&gt; on our new Character&lt;/li&gt;
&lt;li&gt;return the Character&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, it's a little unclear whether those 3 new variables need to be used anywhere else. Plus, you may be the kind of person who hates that redundant hanging line that just returns &lt;code&gt;character&lt;/code&gt;! &lt;/p&gt;

&lt;p&gt;So, let's use &lt;code&gt;.tap&lt;/code&gt; to wrap those variables, the &lt;code&gt;roll_stats()&lt;/code&gt; call that uses them, and the &lt;code&gt;save!&lt;/code&gt; call in a block--and eliminate that final &lt;code&gt;character&lt;/code&gt; line while we're at it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_character&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="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="no"&gt;Character&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;tap&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;character&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;stats_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"strength"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"dexterity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"intelligence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"charisma"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;
            &lt;span class="n"&gt;max_roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
            &lt;span class="n"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;save!&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;Now, we have several potential advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our intent with &lt;code&gt;stats_array&lt;/code&gt;, &lt;code&gt;points_pool&lt;/code&gt;, and &lt;code&gt;max_roll&lt;/code&gt; is clearer, because the variables only exist in the &lt;code&gt;.tap&lt;/code&gt; block's scope, and are immediately passed into &lt;code&gt;roll_stats()&lt;/code&gt;. &lt;strong&gt;We can be more confident that those variables won't be needed anywhere else in our code.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;We know that &lt;code&gt;.tap&lt;/code&gt; always returns the object it's called on, so we don't have to worry about forgetting to explicitly return &lt;code&gt;character&lt;/code&gt; at the end of &lt;code&gt;new_character()&lt;/code&gt;. &lt;strong&gt;We can be more confident that our new Character will be returned as expected.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We saved...&lt;strong&gt;0 lines of code.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pro or con: do you think the &lt;em&gt;ORIGINAL&lt;/em&gt; or &lt;em&gt;NEW&lt;/em&gt; version is more readable? (Feel free to comment below!)
&lt;/h3&gt;

&lt;h1&gt;
  
  
  Case 2: debugging values step-by-step during iteration in &lt;code&gt;roll_stats()&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;In the private method &lt;code&gt;roll_stats()&lt;/code&gt;, we have several values that need to be tracked as each Character stat is randomly assigned a number 1 through 6:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how many points are left to allocate&lt;/li&gt;
&lt;li&gt;how many stats are left to assign points to (so no stat ends up with 0)&lt;/li&gt;
&lt;li&gt;what number 1 through 6 is randomly "rolled"&lt;/li&gt;
&lt;li&gt;is the stat being correctly assigned the "rolled" value&lt;/li&gt;
&lt;li&gt;is the number of points left to spend decreasing by the "rolled" value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Faced with the option to insert a &lt;code&gt;puts "value: #{value}"&lt;/code&gt; or &lt;code&gt;pry&lt;/code&gt; line in-between each step, I realized that using &lt;code&gt;.tap&lt;/code&gt; instead would let me add debugging messages to the console &lt;strong&gt;without breaking up the code I'm testing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, after each step (and in a few extra places), I tabbed way over to the side and added a &lt;code&gt;.tap&lt;/code&gt; call with a &lt;code&gt;puts "value: #{value}"&lt;/code&gt; message to check the values step-by-step: &lt;em&gt;&lt;strong&gt;(scroll to the right)&lt;/strong&gt;&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="c1"&gt;# /app/services/random_character_generator.rb&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;stats_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_index&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;stat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;max_roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"roll: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;r&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;remaining_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;stats_array&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;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"remaining_stats: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;r&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"points_pool (before): &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;points_pool&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt;
                &lt;span class="n"&gt;max_points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;remaining_stats&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_points&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;max_points&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;
                &lt;span class="n"&gt;points_pool&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;roll&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;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"character[&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;]&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"points_pool (after): &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;points_pool&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&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;Now, when I'm running tests with RSpec, I can get this console output to help me debug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[18:36:52] (master) tap-example-character-creator-api
// ♥ bundle exec rspec
roll: 3
remaining_stats: 3
points_pool (before): 9
character[strength]: 3
points_pool (after): 6

roll: 2
remaining_stats: 2
points_pool (before): 6
character[dexterity]: 2
points_pool (after): 4

roll: 1
remaining_stats: 1
points_pool (before): 4
character[intelligence]: 1
points_pool (after): 3

roll: 2
remaining_stats: 0
points_pool (before): 3
character[charisma]: 3
points_pool (after): 0

....

Finished in 0.02678 seconds (files took 3.61 seconds to load)
4 examples, 0 failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main advantage here is that &lt;strong&gt;the readability of the original code is not changed&lt;/strong&gt;--in fact, it's easy to ignore that those &lt;code&gt;.tap&lt;/code&gt; calls are even there!&lt;/p&gt;

&lt;p&gt;However, it took time to format those &lt;code&gt;.tap&lt;/code&gt; calls to be out of the way but still formatted in a readable way--&lt;strong&gt;and any changes to those lines of code will inevitably change the indentation that keeps them lined up.&lt;/strong&gt; Maintaining that readability, or wanting to comment out/delete those &lt;code&gt;.tap&lt;/code&gt; calls, costs extra time and effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pro or con: do you think it takes &lt;em&gt;MORE&lt;/em&gt; or &lt;em&gt;LESS&lt;/em&gt; time to write and maintain this syntax for debugging? (Feel free to comment below!)
&lt;/h3&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Honestly, I can't sum up the &lt;code&gt;.tap&lt;/code&gt; method any better than &lt;a href="https://medium.com/aviabird/ruby-tap-that-method-90c8a801fd6a" rel="noopener noreferrer"&gt;Kartik Jagdale does in this article&lt;/a&gt;, so I'll simply quote him:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Just like any other ruby syntactic sugar , I would say &lt;code&gt;tap&lt;/code&gt; is a pretty cool ruby method which can not just be used for readability but also to debug chained methods. Give it a shot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdata.whicdn.com%2Fimages%2F302979371%2Foriginal.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdata.whicdn.com%2Fimages%2F302979371%2Foriginal.gif" alt="anime gif of two golden faucets powerfully streaming water" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Special thanks to &lt;a href="https://www.antfeedr.com/" rel="noopener noreferrer"&gt;Anthony Hernandez&lt;/a&gt; for introducing me to the .tap method! :)
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Further reading/links/discussions/debates about Ruby's .tap method:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.ruby-lang.org/en/2.4.0/Object.html#method-i-tap" rel="noopener noreferrer"&gt;https://docs.ruby-lang.org/en/2.4.0/Object.html#method-i-tap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/17493080/advantage-of-tap-method-in-ruby" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/17493080/advantage-of-tap-method-in-ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redningja.com/dev/rubys-object-tap-a-better-use" rel="noopener noreferrer"&gt;https://redningja.com/dev/rubys-object-tap-a-better-use&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/aviabird/ruby-tap-that-method-90c8a801fd6a" rel="noopener noreferrer"&gt;https://medium.com/aviabird/ruby-tap-that-method-90c8a801fd6a&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.engineyard.com/blog/five-ruby-methods-you-should-be-using" rel="noopener noreferrer"&gt;https://www.engineyard.com/blog/five-ruby-methods-you-should-be-using&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ruby-forum.com/t/tap-method-good-or-bad-practice/228410" rel="noopener noreferrer"&gt;https://www.ruby-forum.com/t/tap-method-good-or-bad-practice/228410&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rubyguides.com/2017/10/7-powerful-ruby-methods/" rel="noopener noreferrer"&gt;https://www.rubyguides.com/2017/10/7-powerful-ruby-methods/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub repo for the character-generator API use case:&lt;/strong&gt; &lt;a href="https://github.com/isalevine/rails-tap-example-character-api" rel="noopener noreferrer"&gt;https://github.com/isalevine/rails-tap-example-character-api&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Got any tips, tricks, or instances for using Ruby's .tap method? Please feel free to comment and share below! &amp;lt;3
&lt;/h3&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>healthydebate</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Using Service Objects to Make your Rails Controllers (and Models) Skinny</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Mon, 18 Nov 2019 00:54:35 +0000</pubDate>
      <link>https://dev.to/isalevine/using-rails-service-objects-to-make-skinny-controllers-and-models-3k9c</link>
      <guid>https://dev.to/isalevine/using-rails-service-objects-to-make-skinny-controllers-and-models-3k9c</guid>
      <description>&lt;p&gt;&lt;strong&gt;DISCLAIMER: I really dislike the anthropomorphic descriptors "fat" and "skinny" being used in this context, and I find it fatphobic that "skinny" is implicitly the "good" way to write your code. &lt;em&gt;Surely there is a better alternative to these terms&lt;/em&gt;, and I welcome suggestions in the comments! (I will be happy to update the language in this post.)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When studying Rails design patterns, you may have heard the axiom "fat model, skinny controller" (or the inverse, "fat controller, skinny model"). Both of these describe a pattern of where to put business logic, which tends to take up a lot of lines of code.&lt;/p&gt;

&lt;p&gt;Neither case is ideal--we want our controllers &lt;em&gt;and&lt;/em&gt; our models to be "skinny", with as little code as possible, so that they are easy to understand and maintain. If only we had another place in our Rails app to put this logic...&lt;/p&gt;

&lt;h1&gt;
  
  
  Service Objects: Another place to put your business logic
&lt;/h1&gt;

&lt;p&gt;...oh wait, there &lt;em&gt;totally&lt;/em&gt; is: &lt;strong&gt;Service Objects!&lt;/strong&gt; Service objects live in the &lt;code&gt;/app/services/&lt;/code&gt; directory, and any classes defined therein will be autoloaded (along with the rest of the &lt;code&gt;/app/&lt;/code&gt; directory).&lt;/p&gt;

&lt;p&gt;So, we can define a service object class, move our business logic to it, and thus keep our controllers and models "skinny" by keeping only controller- or model-related code in them. All that extra business logic can be invoked by instantiating a service object &lt;em&gt;only when needed&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Thus, we achieve the trifecta: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;our &lt;strong&gt;controllers&lt;/strong&gt; are "skinny", and can focus on controller-logic&lt;/li&gt;
&lt;li&gt;our &lt;strong&gt;models&lt;/strong&gt; are also "skinny", and can focus on model-logic&lt;/li&gt;
&lt;li&gt;our &lt;strong&gt;business logic&lt;/strong&gt; is wrapped in a service object (with the bonus that service objects are &lt;strong&gt;easily testable&lt;/strong&gt;...but I'll save that for a future post!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Example: Refactoring our Magic: the Gathering art-displaying app
&lt;/h1&gt;

&lt;p&gt;A perfect example of a "fat controller" we could refactor with service objects is &lt;a href="https://dev.to/isalevine/let-s-use-rails-partial-views-to-render-art-from-magic-the-gathering-cards-5d8l"&gt;my previous post's app to render Magic: the Gathering card art&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Here, we defined a bunch of business logic in our &lt;code&gt;pages_controller.rb&lt;/code&gt; to query the &lt;a href="https://scryfall.com/docs/api" rel="noopener noreferrer"&gt;Scryfall API&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /app/controller/pages_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="c1"&gt;# refactor with ||= pointed out by Andrew Brown&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_scryfall_images&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RestClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;def&lt;/span&gt; &lt;span class="nf"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;data_array&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;card_hash&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"image_uris"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;img_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"image"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"image_uris"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"art_crop"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="s2"&gt;"artist"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;img_array&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;img_hash&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;if&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"next_page"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"next_page"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="n"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_scryfall_images&lt;/span&gt;
    &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://api.scryfall.com/cards/search?q="&lt;/span&gt;
    &lt;span class="n"&gt;img_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;creature_search_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"merfolk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"goblin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"angel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sliver"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;creature_search_array&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;creature_str&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;search_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"t%3Alegend+t%3A"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;creature_str&lt;/span&gt;
      &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# per the API documentation: https://scryfall.com/docs/api&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&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;But pretty much everything below the &lt;code&gt;private&lt;/code&gt; keyword &lt;strong&gt;is NOT related to the controller&lt;/strong&gt;--it's just logic to query a specific API. &lt;/p&gt;

&lt;p&gt;What if we wanted to query a different card game's API, and wrote a bunch of new functions to fetch and parse its data? Our controller could get out of control (and hard to understand/maintain) VERY quickly!&lt;/p&gt;

&lt;p&gt;So, let's create a new &lt;code&gt;/app/services/&lt;/code&gt; directory, and a dedicated service object to handle the Scryfall API-querying logic--let's call it &lt;code&gt;scryfall_query_service.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="c1"&gt;# /app/services/scryfall_query_service.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScryfallQueryService&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RestClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;def&lt;/span&gt; &lt;span class="nf"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;data_array&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;card_hash&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"image_uris"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;img_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"image"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"image_uris"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"art_crop"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="s2"&gt;"artist"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;img_array&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;img_hash&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;if&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"next_page"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"next_page"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_scryfall_images&lt;/span&gt; 
        &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://api.scryfall.com/cards/search?q="&lt;/span&gt;
        &lt;span class="n"&gt;img_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;creature_search_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"merfolk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"goblin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"angel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sliver"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;creature_search_array&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;creature_str&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;search_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"t%3Alegend+t%3A"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;creature_str&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# per the API documentation: https://scryfall.com/docs/api&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&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;&lt;strong&gt;Note that we need to add &lt;code&gt;self.&lt;/code&gt; before our calls to these instance methods! (&lt;code&gt;self.get_json&lt;/code&gt; and &lt;code&gt;self.parse_cards&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And now, we can make our &lt;code&gt;pages_controller.rb&lt;/code&gt; much "skinnier" by completely removing those same methods:&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/controllers/pages_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;scryfall_query_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ScryfallQueryService&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scryfall_query_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_scryfall_images&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@refresh_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&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;Now, we can instantiate a &lt;code&gt;ScryfallQueryService&lt;/code&gt; object, and all its &lt;code&gt;get_scryfall_images&lt;/code&gt; method to handle all of the API-querying and data-parsing outside of the controller. This makes our controller a lot easier to read and understand!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Although the examples here are exclusively related to making "skinny" &lt;em&gt;controllers&lt;/em&gt;, the same types of logic/methods can be moved from &lt;em&gt;models&lt;/em&gt; to service objects as well!&lt;/p&gt;

&lt;h1&gt;
  
  
  Service Objects and Modular Design: Adding a new API-querying service object
&lt;/h1&gt;

&lt;p&gt;One of the benefits of having this API-querying business logic wrapped in the &lt;code&gt;ScryfallQueryService&lt;/code&gt; object is that we can now expand our app to include other service objects that query &lt;em&gt;other&lt;/em&gt; APIs, without interrupting the functionality of our &lt;code&gt;ScryfallQueryService&lt;/code&gt; or adding more logic to our &lt;code&gt;pages_controller.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's say we want to add a route to our app that will render &lt;a href="https://en.wikipedia.org/wiki/Star_Wars_Customizable_Card_Game" rel="noopener noreferrer"&gt;card art from the Star Wars CCG&lt;/a&gt; instead. This will require us to query a different API altogether, and thus parse the JSON we get back in a different way than &lt;code&gt;ScryfallQueryService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The API we'll be using for card art from the Star Wars CCG is &lt;a href="https://swccgdb.com/api/" rel="noopener noreferrer"&gt;SWCCGDB&lt;/a&gt;, a fan-maintained database for the now-fan-maintained game that was "officially" discontinued in 2001.&lt;/p&gt;

&lt;p&gt;Since the JSON we get back from the API is structured differently, both our &lt;code&gt;parse_cards&lt;/code&gt; and &lt;code&gt;get_swccgdb_images&lt;/code&gt; methods will be different from &lt;code&gt;ScryfallQueryService&lt;/code&gt;. Here's the code:&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/services/swccgdb_query_service.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SWCCGDBQueryService&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RestClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;def&lt;/span&gt; &lt;span class="nf"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;   &lt;span class="c1"&gt;# api returns all cards in a single array at top level of json&lt;/span&gt;
        &lt;span class="n"&gt;data_array&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;card_hash&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"type_code"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"character"&lt;/span&gt;
                &lt;span class="n"&gt;img_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"image"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="s2"&gt;"artist"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Lucasfilm / Decipher / Wizards of the Coast / SWCCG Player's Committee"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;img_array&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;img_hash&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_swccgdb_images&lt;/span&gt;
        &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://swccgdb.com/api/public/cards/"&lt;/span&gt;
        &lt;span class="n"&gt;img_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;set_search_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"pr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"hoth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"cc"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;     &lt;span class="c1"&gt;# premiere, hoth, and cloud city sets&lt;/span&gt;

        &lt;span class="n"&gt;set_search_array&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;set_str&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;search_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;set_str&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;".json?_format=json"&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&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;Most of our code can be copied directly from &lt;code&gt;scryfall_query_service.rb&lt;/code&gt;, but note several differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;in &lt;code&gt;get_swccgdb_images&lt;/code&gt;,&lt;/strong&gt; we are now searching through different game sets ("Premiere", "Hoth", and "Cloud City") instead of creature types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;also in &lt;code&gt;get_swccgdb_images&lt;/code&gt;,&lt;/strong&gt; the API URL is different &lt;em&gt;(obviously)&lt;/em&gt;, and our &lt;code&gt;search_url&lt;/code&gt; string is constructed differently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;in &lt;code&gt;parse_cards&lt;/code&gt;,&lt;/strong&gt; we assign the JSON directly to the &lt;code&gt;data_array&lt;/code&gt; variable due to the structure of the API's JSON response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;also in &lt;code&gt;parse_cards&lt;/code&gt;,&lt;/strong&gt; we are now filtering cards for &lt;code&gt;card_hash["type_code"] == "creature"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we can build another route in our &lt;code&gt;pages_controller.rb&lt;/code&gt; and &lt;code&gt;routes.rb&lt;/code&gt; files to use the &lt;code&gt;ScryfallQueryService&lt;/code&gt; at &lt;code&gt;localhost:3000/mtg&lt;/code&gt;, and &lt;code&gt;SWCCGDBQueryService&lt;/code&gt; at &lt;code&gt;localhost:3000/swccg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's update &lt;code&gt;pages_controller.rb&lt;/code&gt; first. Instead of one &lt;code&gt;index&lt;/code&gt; method, let's define one for &lt;code&gt;mtg&lt;/code&gt; (Magic: the Gathering) and one for &lt;code&gt;swccg&lt;/code&gt; (Star Wars CCG):&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;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="c1"&gt;# def index&lt;/span&gt;
  &lt;span class="c1"&gt;#   session[:img_array] ||= []&lt;/span&gt;

  &lt;span class="c1"&gt;#   if session[:img_array].empty? || params["button_action"] == "refresh"&lt;/span&gt;
  &lt;span class="c1"&gt;#     scryfall_query_service = ScryfallQueryService.new&lt;/span&gt;
  &lt;span class="c1"&gt;#     session[:img_array] = scryfall_query_service.get_scryfall_images&lt;/span&gt;
  &lt;span class="c1"&gt;#   end&lt;/span&gt;


  &lt;span class="c1"&gt;#   session[:refresh_counter] ||= 0&lt;/span&gt;

  &lt;span class="c1"&gt;#   if params["button_action"] == "refresh"&lt;/span&gt;
  &lt;span class="c1"&gt;#     session[:refresh_counter] += 1&lt;/span&gt;
  &lt;span class="c1"&gt;#   end&lt;/span&gt;

  &lt;span class="c1"&gt;#   @refresh_counter = session[:refresh_counter]&lt;/span&gt;
  &lt;span class="c1"&gt;# end&lt;/span&gt;


  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mtg&lt;/span&gt;
    &lt;span class="c1"&gt;# use ScryfallQueryService here&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;swccg&lt;/span&gt;
    &lt;span class="c1"&gt;# use SWCCGDBQueryService here&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;Now, let's copy the logic from &lt;code&gt;index&lt;/code&gt; into our two new methods. Under &lt;code&gt;swccg&lt;/code&gt;, we'll also change our service object to be an instance of &lt;code&gt;SWCCGDBQueryService&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="c1"&gt;# /app/controller/pages_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mtg&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;scryfall_query_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ScryfallQueryService&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scryfall_query_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_scryfall_images&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@refresh_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&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;def&lt;/span&gt; &lt;span class="nf"&gt;swccg&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;swccgdb_query_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SWCCGDBQueryService&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;swccgdb_query_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_swccgdb_images&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@refresh_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&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;Next, let's add an &lt;code&gt;if&lt;/code&gt; block to the top of both methods so our &lt;code&gt;session&lt;/code&gt; can keep track of which game its storing card art for--that way, if we switch from &lt;code&gt;localhost:3000/mtg&lt;/code&gt; to &lt;code&gt;localhost:3000/swccg&lt;/code&gt;, the API will know to empty out our &lt;code&gt;session[:img_array]&lt;/code&gt; and fetch new cards from the correct game:&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/controller/pages_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mtg&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"swccg"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# empty img_array if switching games&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mtg"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;scryfall_query_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ScryfallQueryService&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scryfall_query_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_scryfall_images&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@refresh_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&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;def&lt;/span&gt; &lt;span class="nf"&gt;swccg&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"mtg"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# empty img_array if switching games&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"swccg"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;swccgdb_query_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SWCCGDBQueryService&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;swccgdb_query_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_swccgdb_images&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@refresh_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&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;This is great, but our controller isn't very "skinny" now, which is why we're creating service objects in the first place! The problem is that our code &lt;strong&gt;is not DRY&lt;/strong&gt;--we have mostly duplicated the code from &lt;code&gt;index&lt;/code&gt;, and it now appears twice in our controller. &lt;/p&gt;

&lt;p&gt;Let's refactor the session-checking-and-setting logic into its own private method &lt;code&gt;update_session&lt;/code&gt;, where we can pass the parameter &lt;code&gt;"mtg"&lt;/code&gt; or &lt;code&gt;"swccg"&lt;/code&gt; to set the &lt;code&gt;session[:game]&lt;/code&gt; variable and use the correct service object:&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/controllers/pages_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mtg&lt;/span&gt;
    &lt;span class="n"&gt;update_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mtg"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@refresh_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&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;def&lt;/span&gt; &lt;span class="nf"&gt;swccg&lt;/span&gt;
    &lt;span class="n"&gt;update_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"swccg"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@refresh_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_game&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;current_game&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# empty img_array if switching games&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:game&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_game&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_game&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"mtg"&lt;/span&gt;
        &lt;span class="n"&gt;scryfall_query_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ScryfallQueryService&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scryfall_query_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_scryfall_images&lt;/span&gt;

      &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;current_game&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"swccg"&lt;/span&gt;
        &lt;span class="n"&gt;swccgdb_query_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SWCCGDBQueryService&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;swccgdb_query_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_swccgdb_images&lt;/span&gt;

      &lt;span class="c1"&gt;# add additional games here&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&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;Perfect! We can now add additional games into our app by adding another &lt;code&gt;elsif current_game == String&lt;/code&gt; branch inside our &lt;code&gt;update_session&lt;/code&gt; method, and adding another public method (with only &lt;strong&gt;two lines of code inside it!&lt;/strong&gt;) to &lt;code&gt;pages_controller.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's update our &lt;code&gt;routes.rb&lt;/code&gt; with a Get and Post route to both &lt;code&gt;localhost:3000/mtg&lt;/code&gt; and &lt;code&gt;localhost:3000/swccg&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="c1"&gt;# /config/routes.rb&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/mtg"&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;"pages#mtg"&lt;/span&gt;
  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s2"&gt;"/mtg"&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;"pages#mtg"&lt;/span&gt;

  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/swccg"&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;"pages#swccg"&lt;/span&gt;
  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s2"&gt;"/swccg"&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;"pages#swccg"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, let's go ahead and make a set of &lt;code&gt;mtg.html.erb&lt;/code&gt; and &lt;code&gt;swccg.html.erb&lt;/code&gt; views (in the same &lt;code&gt;/app/views/pages/&lt;/code&gt; directory where &lt;code&gt;index.html.erb&lt;/code&gt; currently lives). These will automatically render at the end of the &lt;code&gt;mtg&lt;/code&gt; and &lt;code&gt;swccg&lt;/code&gt; controller methods respectively, and we can customize them with the name of each game:&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;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/mtg.html.erb %&amp;gt;

&amp;lt;h1&amp;gt;Pages#&lt;/span&gt;&lt;span class="n"&gt;mtg&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&amp;gt;
&amp;lt;p&amp;gt;Find me in app/&lt;/span&gt;&lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mtg&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;

&amp;lt;h2&amp;gt;Let's add some Magic: the Gathering card art!&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"card_container"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render partial: '/pages/cards/card', collection: session[:img_array], as: :img_hash %&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class=&lt;/span&gt;&lt;span class="s2"&gt;"refresh_form"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render partial: '/pages/forms/refresh_button' %&amp;gt;
    &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'/pages/forms/refresh_counter'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;counter: &lt;/span&gt;&lt;span class="vi"&gt;@refresh_counter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/swccg.html.erb %&amp;gt;

&amp;lt;h1&amp;gt;Pages#&lt;/span&gt;&lt;span class="n"&gt;swccg&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&amp;gt;
&amp;lt;p&amp;gt;Find me in app/&lt;/span&gt;&lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;swccg&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;

&amp;lt;h2&amp;gt;Let's add some Star Wars CCG card art!&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"card_container"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render partial: '/pages/cards/card', collection: session[:img_array], as: :img_hash %&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class=&lt;/span&gt;&lt;span class="s2"&gt;"refresh_form"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render partial: '/pages/forms/refresh_button' %&amp;gt;
    &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'/pages/forms/refresh_counter'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;counter: &lt;/span&gt;&lt;span class="vi"&gt;@refresh_counter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Let's test out our new routes. In your console, run &lt;code&gt;rails s&lt;/code&gt;. Navigate to &lt;code&gt;localhost:3000&lt;/code&gt; and you will see the default Rails index page (since we got rid of our &lt;code&gt;index&lt;/code&gt; method in &lt;code&gt;pages_controller.rb&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now let's check out &lt;code&gt;localhost:3000/mtg&lt;/code&gt; to see our Magic: the Gathering card art...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F22tkemz5tjh0dpqlgjt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F22tkemz5tjh0dpqlgjt6.png" alt="screenshot of localhost:3000/mtg showing magic: the gathering card art"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now, let's go to &lt;code&gt;localhost:3000/swccg&lt;/code&gt; to see our Star Wars CCG card art...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F65oxohilotbd1dsrqasu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F65oxohilotbd1dsrqasu.png" alt="screenshot of localhost:3000/swccg showing star wars ccg card art"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! As you can see, &lt;strong&gt;creating individual service objects to handle each API&lt;/strong&gt; allows us to separate our concerns, so that the &lt;em&gt;controller can focus on handling controller logic&lt;/em&gt; and our &lt;em&gt;API-querying business logic is handled within its own API-specific service object&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Rails service objects allow us to move business logic from controllers (or models) to a separate class, which helps us keep our controllers (and models) "skinny" and focused on their own logic. They also allow us to make our codebase more modular, as we can build our individual service objects instead of cramming more logic into our controllers indefinitely.&lt;/p&gt;

&lt;p&gt;Next time you see a lot of logic in your controllers (or models), think about moving some of it to a service object!&lt;/p&gt;

&lt;h3&gt;
  
  
  Special thanks to &lt;a href="https://www.antfeedr.com/" rel="noopener noreferrer"&gt;Anthony Hernandez&lt;/a&gt; for introducing me to service objects! :)
&lt;/h3&gt;

&lt;h3&gt;
  
  
  And additional thanks to &lt;a href="https://dev.to/andrewbrown"&gt;Andrew Brown&lt;/a&gt; for feedback/refactor suggestions on my previous post!
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Further reading/links/resources for Rails service objects
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub repo for this article's code examples:&lt;/strong&gt; &lt;a href="https://github.com/isalevine/devto-rails-partial-view-demo" rel="noopener noreferrer"&gt;https://github.com/isalevine/devto-rails-partial-view-demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@scottdomes/service-objects-in-rails-75ca74214b77" rel="noopener noreferrer"&gt;https://medium.com/@scottdomes/service-objects-in-rails-75ca74214b77&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/selleo/essential-rubyonrails-patterns-part-1-service-objects-1af9f9573ca1" rel="noopener noreferrer"&gt;https://medium.com/selleo/essential-rubyonrails-patterns-part-1-service-objects-1af9f9573ca1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.engineyard.com/blog/keeping-your-rails-controllers-dry-with-services" rel="noopener noreferrer"&gt;https://www.engineyard.com/blog/keeping-your-rails-controllers-dry-with-services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/marmolabs/skinny-models-skinny-controllers-fat-services-e04cfe2d6ae" rel="noopener noreferrer"&gt;https://medium.com/marmolabs/skinny-models-skinny-controllers-fat-services-e04cfe2d6ae&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/16907578/should-i-move-my-custom-methods-to-model-from-controller/16907670#16907670" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/16907578/should-i-move-my-custom-methods-to-model-from-controller/16907670#16907670&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial" rel="noopener noreferrer"&gt;https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Got any tips, tricks, or instances for using service objects? Please feel free to comment and share below! &amp;lt;3
&lt;/h3&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>HELP NEEDED: Understanding Rails ActionController::Live Module (and Async Limitations)</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Mon, 11 Nov 2019 06:38:52 +0000</pubDate>
      <link>https://dev.to/isalevine/help-needed-understanding-rails-actioncontroller-live-module-and-async-limitations-4em8</link>
      <guid>https://dev.to/isalevine/help-needed-understanding-rails-actioncontroller-live-module-and-async-limitations-4em8</guid>
      <description>&lt;h1&gt;
  
  
  The Situation: Practicing Data Ingestion in Rails (and Trying to be Async)
&lt;/h1&gt;

&lt;p&gt;So, for reasons that (&lt;strong&gt;ahem&lt;/strong&gt;) may or may not be job-interview-related, I'm practicing building a Rails server for ingesting, parsing, and storing data in a database.&lt;/p&gt;

&lt;p&gt;My goal was to make two Rails apps, and have them talk to each other:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A server that &lt;strong&gt;broadcasts hashes&lt;/strong&gt; containing randomly-generated characters (ideally repeating every 1 second, indefinitely)&lt;/li&gt;
&lt;li&gt;A server that &lt;strong&gt;listens for and receives the hashes,&lt;/strong&gt; and parses them as they're received to store in a database&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I intended to implement this behavior asynchronously with the &lt;a href="https://api.rubyonrails.org/classes/ActionController/Live.html" rel="noopener noreferrer"&gt;&lt;strong&gt;ActionController::Live&lt;/strong&gt;&lt;/a&gt; module. The intended behavior is for the &lt;strong&gt;Broadcast server&lt;/strong&gt; to emit a hash &lt;em&gt;(as a string)&lt;/em&gt; every 1 second, and for the &lt;strong&gt;Receiver server&lt;/strong&gt; to parse and store each hash as they come in. (For my tests, I have this looping 5 times.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My problem is that the character-hashes are rendered 1-by-1 when testing in my browser and in the Broadcast server's console...&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2sqkbkcwq08ta3uvni7x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2sqkbkcwq08ta3uvni7x.gif" alt="gif of hashes being rendered in Chrome browser one by one, with a 1 second delay"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;working fine in Chrome...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0vklf508fs3l7kxmj97u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0vklf508fs3l7kxmj97u.gif" alt="gif of hashes being rendered in console one by one, with a 1 second delay"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;working fine in console...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;...but in the Receiver server's console, all the character-hashes come at once in the HTTP response!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq0n3kyfool02ip4qhwlx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq0n3kyfool02ip4qhwlx.gif" alt="gif of hashes being returned as one http res.body response in Rails console"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;all the JSON is received as one response body!&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  So why is the async behavior (apparently) working in some places, and not in others?
&lt;/h3&gt;
&lt;h1&gt;
  
  
  Broadcaster server
&lt;/h1&gt;

&lt;p&gt;The Broadcaster server is a simple Rails app that uses a &lt;code&gt;BroadcasterController&lt;/code&gt; with &lt;code&gt;ActionController::Live&lt;/code&gt; and its &lt;a href="https://api.rubyonrails.org/classes/ActionController/Live/SSE.html" rel="noopener noreferrer"&gt;server-side event (&lt;code&gt;SSE&lt;/code&gt;) module&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;index&lt;/code&gt; method generates a random &lt;code&gt;character_hash&lt;/code&gt;, writes it to the current SSE &lt;code&gt;response.stream&lt;/code&gt; in the variable &lt;code&gt;sse&lt;/code&gt;, and pauses for 1 second to illustrate async behavior.&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;# Broadcaster app&lt;/span&gt;
&lt;span class="c1"&gt;# /app/controllers/broadcaster_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BroadcasterController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Live&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
        &lt;span class="n"&gt;name_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Ryu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Peco"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Rei"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Momo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Garr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Nina"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;hp_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;132&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;71&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="mi"&gt;1&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="mi"&gt;325&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;magic_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Frost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Typhoon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Magic Ball"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Ascension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Rejuvinate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Weretiger"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"text/event-stream"&lt;/span&gt;
        &lt;span class="n"&gt;sse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SSE&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;begin&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="k"&gt;do&lt;/span&gt;      
                &lt;span class="n"&gt;character_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"uuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s2"&gt;"hp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hp_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s2"&gt;"magic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;magic_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;sse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;character: &lt;/span&gt;&lt;span class="n"&gt;character_hash&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;IOError&lt;/span&gt;
            &lt;span class="c1"&gt;# client disconnected&lt;/span&gt;
        &lt;span class="k"&gt;ensure&lt;/span&gt;
            &lt;span class="n"&gt;sse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Broadcaster app&lt;/span&gt;
&lt;span class="c1"&gt;# /config/routes.rb&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'broadcaster'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'broadcaster#index'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we start the server with &lt;code&gt;rails s&lt;/code&gt;, we can use &lt;code&gt;curl -i http://localhost:3000/broadcaster&lt;/code&gt; in the command line to send a Get request to the &lt;code&gt;index&lt;/code&gt; method. The response will return each character with a 1 second delay in-between:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0vklf508fs3l7kxmj97u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0vklf508fs3l7kxmj97u.gif" alt="gif of hashes being rendered in console one by one, with a 1 second delay"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since navigating to &lt;code&gt;http://localhost:3000/broadcaster&lt;/code&gt; in the browser will also send a Get request, we see the same behavior here in Chrome:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2sqkbkcwq08ta3uvni7x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2sqkbkcwq08ta3uvni7x.gif" alt="gif of hashes being rendered in Chrome browser one by one, with a 1 second delay"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So far, so good...&lt;/p&gt;

&lt;h1&gt;
  
  
  Receiver server
&lt;/h1&gt;

&lt;p&gt;The other Rails app is a Receiver server that sends a Get request to the Broadcaster at &lt;code&gt;http://localhost:3000/broadcaster&lt;/code&gt;, and parses its response to store the received characters in the database. &lt;/p&gt;

&lt;p&gt;We also have it &lt;code&gt;puts&lt;/code&gt; a readout to show us the &lt;code&gt;res.body&lt;/code&gt; characters arriving all at once, instead of asynchronously as we saw above.&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;# Receiver app&lt;/span&gt;
&lt;span class="c1"&gt;# /app/controllers/listener_controller.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'net/http'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListenerController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://localhost:3000/broadcaster'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Get&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&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="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&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="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;READOUT&lt;/span&gt;&lt;span class="sh"&gt;

    res.body:
    ==============================
    &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

&lt;/span&gt;&lt;span class="no"&gt;        READOUT&lt;/span&gt;

        &lt;span class="n"&gt;char_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;char_array&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;data_str&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;data_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&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="c1"&gt;# slice to remove leading "data: " substring&lt;/span&gt;
            &lt;span class="n"&gt;char_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="no"&gt;Character&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="s2"&gt;"uuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"hp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:hp&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"magic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:magic&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Receiver app&lt;/span&gt;
&lt;span class="c1"&gt;# /config/routes.rb&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'listener'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'listener#index'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thus, when we start the server with &lt;code&gt;rails server -p 3001&lt;/code&gt; and send a Get request with &lt;code&gt;curl -i http://localhost:3001/listener&lt;/code&gt;, we call the ListenerController's &lt;code&gt;index&lt;/code&gt; method. &lt;/p&gt;

&lt;p&gt;Here, &lt;code&gt;index&lt;/code&gt; sends a Get request to our Broadcaster server at &lt;code&gt;localhost:3000/broadcaster&lt;/code&gt;. &lt;strong&gt;But instead of seeing the asynchronous behavior we saw before, it all arrives at once:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq0n3kyfool02ip4qhwlx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq0n3kyfool02ip4qhwlx.gif" alt="gif of hashes being returned as one http res.body response in Rails console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, instead of parsing each character &lt;em&gt;as they come in as separate objects&lt;/em&gt;, we have to split the &lt;code&gt;res.body&lt;/code&gt; into separate strings. &lt;strong&gt;And of course, we have to wait until all 5 characters are finished generating before we receive them&lt;/strong&gt;. So much for scaling it up to send an unlimited number of characters!&lt;/p&gt;

&lt;h1&gt;
  
  
  Where I'm At
&lt;/h1&gt;

&lt;p&gt;From the research I've done, I &lt;em&gt;think&lt;/em&gt; the async behavior is being limited by Rails' use of the standard HTTP request/response cycle as the basis for &lt;code&gt;ActionController::Live&lt;/code&gt;. As such, &lt;strong&gt;each request only gets one response,&lt;/strong&gt; and that's why all the characters have to come back as one &lt;code&gt;res.body&lt;/code&gt; string!&lt;/p&gt;

&lt;p&gt;Per &lt;a href="https://medium.com/@fbazzarella/streaming-data-with-server-sent-events-b0694e6e0b32" rel="noopener noreferrer"&gt;this excellent article by Eric Bidelman covering SSEs in HTML5&lt;/a&gt;, I &lt;em&gt;thought&lt;/em&gt; I was moving toward implementing &lt;strong&gt;long polling&lt;/strong&gt;...but apparently not.&lt;/p&gt;

&lt;p&gt;Further, the tutorials I'm following usually expect us to build a JavaScript event listener to catch the async data from the Broadcaster server. &lt;strong&gt;So, is it just browser-magic that's making the ActionController::Live async behavior work in Chrome?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;But then, why do the characters still &lt;strong&gt;appear&lt;/strong&gt; to be coming in asynchronously when we use &lt;code&gt;curl&lt;/code&gt; directly on &lt;code&gt;localhost:3000/broadcaster&lt;/code&gt;...&lt;/p&gt;

&lt;p&gt;...and &lt;strong&gt;NOT&lt;/strong&gt; when using &lt;code&gt;curl&lt;/code&gt; &lt;strong&gt;&lt;em&gt;indirectly&lt;/em&gt;&lt;/strong&gt; through &lt;code&gt;localhost:3001/listener&lt;/code&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  Any help, advice, or insight is greatly appreciated! &amp;lt;3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Links/Tutorials used:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Main tutorial followed:&lt;/strong&gt; &lt;a href="https://tenderlovemaking.com/2012/07/30/is-it-live.html" rel="noopener noreferrer"&gt;https://tenderlovemaking.com/2012/07/30/is-it-live.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/17678068/long-polling-push-events-between-rails-and-ruby-client/17707334#17707334" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/17678068/long-polling-push-events-between-rails-and-ruby-client/17707334#17707334&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/35057075/rails-actioncontrollerlive-sends-everything-at-once-instead-of-async" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/35057075/rails-actioncontrollerlive-sends-everything-at-once-instead-of-async&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/21763527/asynchronous-responses-in-ruby-on-rails-4-using-sse" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/21763527/asynchronous-responses-in-ruby-on-rails-4-using-sse&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.html5rocks.com/en/tutorials/eventsource/basics/" rel="noopener noreferrer"&gt;https://www.html5rocks.com/en/tutorials/eventsource/basics/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@fbazzarella/streaming-data-with-server-sent-events-b0694e6e0b32" rel="noopener noreferrer"&gt;https://medium.com/@fbazzarella/streaming-data-with-server-sent-events-b0694e6e0b32&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>help</category>
      <category>rails</category>
    </item>
    <item>
      <title>BONUS: A Quick Intro to Ruby's SortedSet</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Sun, 03 Nov 2019 19:13:32 +0000</pubDate>
      <link>https://dev.to/isalevine/bonus-a-quick-intro-to-ruby-s-sortedset-13mk</link>
      <guid>https://dev.to/isalevine/bonus-a-quick-intro-to-ruby-s-sortedset-13mk</guid>
      <description>&lt;p&gt;To build on our coverage of Ruby's Set, let's look at another Ruby data structure that implements set: a SortedSet! &lt;/p&gt;

&lt;p&gt;The key difference between a Set and SortedSet is that, well, SortedSets are &lt;em&gt;sorted&lt;/em&gt;, meaning their contents &lt;strong&gt;must respond to being sorted by the &amp;lt;=&amp;gt; operator (or a custom-defined sort method)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To review, some key details about sets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All objects in a Set are &lt;strong&gt;guaranteed unique&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;del&gt;Objects in a Set are &lt;strong&gt;not&lt;/strong&gt; ordered&lt;/del&gt; &lt;/li&gt;
&lt;li&gt;Sets are built on top of Hashes for &lt;strong&gt;super-fast object lookup&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And SortedSets add to this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Objects in a SortedSet &lt;strong&gt;are ordered by index&lt;/strong&gt;, and can be accessed with the &lt;code&gt;at&lt;/code&gt; or &lt;code&gt;[]&lt;/code&gt; methods, and negative indices count backwards from the end (same as Arrays)&lt;/li&gt;
&lt;li&gt;All objects in a SortedSet must be &lt;strong&gt;mutually comparable&lt;/strong&gt;, meaning that &amp;lt;=&amp;gt; or its custom sort must not return nil for &lt;em&gt;all&lt;/em&gt; objects in the SortedSet (otherwise, an ArgumentError will be thrown)&lt;/li&gt;
&lt;li&gt;SortedSets are built on top of Sets, and have access to most Set methods, including &lt;code&gt;subset?&lt;/code&gt; and &lt;code&gt;superset?&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see them in action!&lt;/p&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;In this article, we'll introduce this basic functionality for SortedSets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating new SortedSets, and seeing their default sorting behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  New SortedSets and default sorts
&lt;/h1&gt;

&lt;h2&gt;
  
  
  require 'set'
&lt;/h2&gt;

&lt;p&gt;Because SortedSets are built on top of sets, we must start by requiring the set module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'set'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we're ready to build some SortedSets!&lt;/p&gt;

&lt;h2&gt;
  
  
  SortedSets with Strings and Integers
&lt;/h2&gt;

&lt;p&gt;We instantiate a new SortedSet with &lt;code&gt;SortedSet.new()&lt;/code&gt;, and pass it a collection object. For our examples, we'll create some SortedSets out of arrays &lt;em&gt;and&lt;/em&gt; sets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's start by building some arrays to test:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;str_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"am"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"watching"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"office"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"now"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"now"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"now"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;int_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;2&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="mi"&gt;1&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;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="mi"&gt;0&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now, let's use &lt;code&gt;SortedSet.new()&lt;/code&gt; to create SortedSets:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;str_sorted_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SortedSet&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;str_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_sorted_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SortedSet&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;int_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#&amp;lt;SortedSet: {"am", "i", "now", "office", "right", "the", "watching"}&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;#&amp;lt;SortedSet: {0, 1, 2, 3, 4, 5, 6, 7}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As expected, creating a SortedSet both &lt;strong&gt;1) filters out  duplicates&lt;/strong&gt; and &lt;strong&gt;2) sorts contents using default &amp;lt;=&amp;gt; behavior&lt;/strong&gt;, here meaning alphabetically and lowest-to-highest value. &lt;em&gt;(Also, we get some refrigerator-magnet-style poetry out of the string sorting!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now, let's create regular ol' Sets out of the Arrays, and pass the Sets to &lt;code&gt;SortedSet.new()&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;str_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&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;str_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&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;int_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;str_sorted_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SortedSet&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;str_set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_sorted_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SortedSet&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;int_set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#&amp;lt;SortedSet: {"am", "i", "now", "office", "right", "the", "watching"}&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;#&amp;lt;SortedSet: {0, 1, 2, 3, 4, 5, 6, 7}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Same behavior!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And for good measure, let's use the Vector collection too:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'matrix'&lt;/span&gt;

&lt;span class="n"&gt;str_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"am"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"watching"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"office"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"now"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"now"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"now"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;int_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&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="mi"&gt;1&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;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="mi"&gt;0&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="n"&gt;str_sorted_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SortedSet&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;str_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_sorted_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SortedSet&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;int_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#&amp;lt;SortedSet: {"am", "i", "now", "office", "right", "the", "watching"}&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;#&amp;lt;SortedSet: {0, 1, 2, 3, 4, 5, 6, 7}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And same behavior again! So, &lt;strong&gt;we can expect the same result from &lt;code&gt;SortedSet.new()&lt;/code&gt; regardless of the type of collection!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ArgumentError with Mixed Types
&lt;/h2&gt;

&lt;p&gt;If we try to create a SortedSet containing objects that &lt;em&gt;cannot&lt;/em&gt; be compared with &amp;lt;=&amp;gt;, we will get an ArgumentError. If we try to print the following &lt;code&gt;mixed_sorted_set&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;mixed_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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"i"&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="s2"&gt;"am"&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="s2"&gt;"watching"&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="s2"&gt;"the"&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="s2"&gt;"office"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;mixed_sorted_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SortedSet&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;mixed_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# /Users/isalevine/.rvm/rubies/ruby-2.6.1/lib/ruby/2.6.0/set.rb:782:&lt;/span&gt;
&lt;span class="c1"&gt;# in `sort!': comparison of Integer with String failed (ArgumentError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We see that SortedSet is trying to call a &lt;code&gt;sort!&lt;/code&gt;, but throws an Argument error when trying to compare an Integer with a String.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/SortedSet"&gt;Check out this RubyDoc.info guide for more information in implementing custom sort behavior on SortedSets.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's the RubyDoc.info &lt;strong&gt;regarding Set methods available (and not available) for SortedSets&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;SortedSet&lt;/code&gt; supports the same basic set-theoretic operations as &lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/Set"&gt;Set&lt;/a&gt;, including &lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/SortedSet#union-instance_method"&gt;#union&lt;/a&gt;, &lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/SortedSet#intersection-instance_method"&gt;#intersection&lt;/a&gt;, &lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/SortedSet#difference-instance_method"&gt;#difference&lt;/a&gt;, and &lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/SortedSet#exclusion-instance_method"&gt;#exclusion&lt;/a&gt;, as well as &lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/SortedSet#subset%3F-instance_method"&gt;#subset?&lt;/a&gt;, &lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/SortedSet#superset%3F-instance_method"&gt;#superset?&lt;/a&gt;, and so on. Unlike &lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/Set"&gt;Set&lt;/a&gt;, it does not define comparison operators like &lt;code&gt;#&amp;gt;&lt;/code&gt; or &lt;code&gt;#&amp;lt;&lt;/code&gt; as aliases for the superset/subset predicates. Instead, these comparison operators do a item-by-item comparison between the &lt;code&gt;SortedSet&lt;/code&gt; and another sequential collection. (See &lt;code&gt;Array#&amp;lt;=&amp;gt;&lt;/code&gt; for details.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Now you're equipped with two versions of sets in Ruby: Sets and SortedSets! Between them, you have the ability to guarantee unique collections of objects, in both an unordered and ordered/indexed form as needed. Think about using them the next time you have arrays of unique objects!&lt;/p&gt;

&lt;h2&gt;
  
  
  Links and Sources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ruby-doc.org/stdlib-2.6.3/libdoc/set/rdoc/SortedSet.html"&gt;Ruby-Doc - SortedSet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rubydoc.info/github/immutable-ruby/immutable-ruby/Immutable/SortedSet"&gt;RubyDoc.info - SortedSet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/4622206/how-to-pass-a-custom-comparator-to-sort"&gt;StackOverflow - options for implementing custom sorts (not specific to SortedSet)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/isalevine/ruby-set-practice-devto/blob/master/sortedsets.rb"&gt;GitHub with code snippets used&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Got any tips, tricks, or instances where you like to use sets? Please feel free to share in the comments below!
&lt;/h3&gt;

</description>
      <category>ruby</category>
      <category>tutorial</category>
      <category>set</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>Let's Use Rails Partials To Render Art from Magic: the Gathering!</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Sun, 27 Oct 2019 20:52:42 +0000</pubDate>
      <link>https://dev.to/isalevine/let-s-use-rails-partial-views-to-render-art-from-magic-the-gathering-cards-5d8l</link>
      <guid>https://dev.to/isalevine/let-s-use-rails-partial-views-to-render-art-from-magic-the-gathering-cards-5d8l</guid>
      <description>&lt;p&gt;When I was first learning Rails in bootcamp, I spent most of my time learning routing with regular views--and they were, uh, &lt;em&gt;simple&lt;/em&gt;. At that point, I wasn't familiar with making reusable frontend components, rendering them in a dynamic and nested way.&lt;/p&gt;

&lt;p&gt;But all that was before I learned React! Now that I'm returning to frontend Rails work, I've been spending much more time with partial views, or simply &lt;strong&gt;partials&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials" rel="noopener noreferrer"&gt;Rails Guides&lt;/a&gt; describes partials this way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Partial templates - usually just called "partials" - are another device for breaking the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;In this article, we'll create some partials to render art from Magic: the Gathering , queried from the &lt;a href="https://scryfall.com/docs/api/" rel="noopener noreferrer"&gt;Scryfall API&lt;/a&gt;. We'll cover these topics along the way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rails naming conventions for views and partials&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;session&lt;/code&gt; to store data from external APIs &lt;em&gt;(not specific to partials, but part of the use case)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;render partial:&lt;/code&gt; within views and other partials&lt;/li&gt;
&lt;li&gt;Passing variables to partials with &lt;code&gt;locals:&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Rendering partials repeatedly by iterating through a collection with &lt;code&gt;collection:&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Views and Partials
&lt;/h1&gt;

&lt;p&gt;Rails' convention-over-configuration gives us a lazy option for rendering views: if a controller's method has a view with a matching name, and there's no other &lt;code&gt;render&lt;/code&gt; or &lt;code&gt;redirect_to&lt;/code&gt; invoked, Rails will automatically render the view when that method is called. Or, more eloquently put from &lt;a href="https://guides.rubyonrails.org/layouts_and_rendering.html#rendering-by-default-convention-over-configuration-in-action" rel="noopener noreferrer"&gt;Rails Guides with the following code example&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BooksController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@books&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;all&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;blockquote&gt;
&lt;p&gt;Note that we don't have explicit render at the end of the index action in accordance with "convention over configuration" principle. The rule is that if you do not explicitly render something at the end of a controller action, Rails will automatically look for the action_name.html.erb template in the controller's view path and render it. So in this case, Rails will render the app/views/books/index.html.erb file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Partials Love Underscores
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Partials&lt;/strong&gt;, however, are named with underscores at the beginning. This convention gives two advantages: we can differentiate partials from regular views, and it also allows us to drop the underscore when invoking &lt;code&gt;render partial:&lt;/code&gt; in ERB.&lt;/p&gt;

&lt;p&gt;We'll explore this more once we have some examples in front of us. :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Use Case: Querying the Scryfall API for Magic: the Gathering Card Art
&lt;/h1&gt;

&lt;p&gt;I'm a sucker for art from Magic: the Gathering. So, our Rails app will query the &lt;a href="https://scryfall.com/docs/api/" rel="noopener noreferrer"&gt;Scryfall API&lt;/a&gt; for card art (along with identifying names and artists), and render a sampling of 9 images--with a button to refresh and re-query the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails App Setup
&lt;/h2&gt;

&lt;p&gt;Let's get started by using &lt;code&gt;rails new&lt;/code&gt; to create our app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails new partial-view-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, let's add a Pages controller, along with an empty &lt;code&gt;index&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails g controller pages index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, this will populate our &lt;code&gt;routes.rb&lt;/code&gt; file with a &lt;code&gt;get 'pages/index&lt;/code&gt; and &lt;code&gt;get page/index&lt;/code&gt; route. For our use case, we'll want both Get and Post requests to &lt;code&gt;/&lt;/code&gt; to go to the &lt;code&gt;index&lt;/code&gt; method on our Pages controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /config/routes.rb&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'pages#index'&lt;/span&gt;
  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'pages#index'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, because we'll be using &lt;code&gt;RestClient&lt;/code&gt; in our API query, go ahead and add &lt;code&gt;gem 'rest-client&lt;/code&gt; to the Gemfile, and update with Bundler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's test our &lt;code&gt;/pages/index.html.erb&lt;/code&gt; view with our routes to make sure everything's working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;views&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;index.html.erb&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Pages#index&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Find me in app/views/pages/index.html.erb&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Let's add some Magic: the Gathering card art!&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;rails s&lt;/code&gt; and open up &lt;code&gt;localhost:3000&lt;/code&gt; in your browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frslpgivzqxrxo617sare.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frslpgivzqxrxo617sare.png" alt="screenshot of localhost:3000 showing rails app loading index view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! Now let's create some partials as components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Card Components: _card, _card_image, _card_name, _card_artist
&lt;/h2&gt;

&lt;p&gt;First, we'll add components to render each piece of art on its own card component. These cards will simply have an image, a name, and the artist.&lt;/p&gt;

&lt;p&gt;To keep our components organized, create a new &lt;code&gt;/views/pages/cards&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;We name partials with underscores at the beginning, so we'll create 4 new files in our new directory: &lt;code&gt;_card.html.erb&lt;/code&gt;, &lt;code&gt;_card_image.html.erb&lt;/code&gt;, &lt;code&gt;_card_name.html.erb&lt;/code&gt;, and &lt;code&gt;_card_artist.html.erb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Heres how our views are looking:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmy2nebsghz2lx33vktur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmy2nebsghz2lx33vktur.png" alt="screenshot of vscode window with new partials in the cards directory highlighted"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll fill out these partials with HTML and ERB like any other view once we have some API data to render.&lt;/p&gt;

&lt;h2&gt;
  
  
  Form Components: _refresh_button, _refresh_counter
&lt;/h2&gt;

&lt;p&gt;We'll also add a &lt;code&gt;/views/pages/forms&lt;/code&gt; directory, where we'll stash a button to refresh our 9 pieces of art, as well as a counter to keep track of how many times we've hit the button. &lt;em&gt;(This will help illustrate how we can pass variables through &lt;code&gt;render partial:&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Add two files to our new directory: &lt;code&gt;_refresh_button.html.erb&lt;/code&gt;, and &lt;code&gt;_refresh_counter.html.erb&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv9l3h3g3cwm9k8vy8bn0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv9l3h3g3cwm9k8vy8bn0.png" alt="screenshot of vscode window with new partials in the forms directory highlighted"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll fill these out with our card components shortly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controller Methods to Query the API
&lt;/h2&gt;

&lt;p&gt;Before we dive into rendering, here's a quick snapshot of the code used to query the Scryfall API and return 9 random pieces of art:&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/controller/pages_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:img_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_scryfall_images&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RestClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;def&lt;/span&gt; &lt;span class="nf"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;data_array&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;card_hash&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"image_uris"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;img_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"image"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"image_uris"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"art_crop"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="s2"&gt;"artist"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;card_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;img_array&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;img_hash&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;if&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"next_page"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"next_page"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="n"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_scryfall_images&lt;/span&gt;
    &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://api.scryfall.com/cards/search?q="&lt;/span&gt;
    &lt;span class="n"&gt;img_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;creature_search_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"merfolk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"goblin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"angel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sliver"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;creature_search_array&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;creature_str&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;search_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"t%3Alegend+t%3A"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;creature_str&lt;/span&gt;
      &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;parse_cards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# per the API documentation: https://scryfall.com/docs/api&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;img_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&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;Here, we're using the &lt;code&gt;session&lt;/code&gt; variable to store an array of &lt;code&gt;img_hash&lt;/code&gt; objects containing the "image" URL, the card's "name", and the "artist" (all as strings).&lt;/p&gt;

&lt;p&gt;The API query is set up to look for "legend" card that are also creatures of the type "merfolk", "goblin", "angel", or "sliver"--my favorite creature types! &lt;em&gt;(Sorry, my old beloved elf deck...)&lt;/em&gt; Also note that, per the API documentation, a 0.1 second delay is built in-between any searches, for good citizenship.&lt;/p&gt;

&lt;p&gt;If we print the contents of &lt;code&gt;session[:img_array]&lt;/code&gt; at the end of the &lt;code&gt;index&lt;/code&gt; method, here's what we have when we re-navigate to &lt;code&gt;localhost:3000&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="c1"&gt;# session[:img_array]&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/b/c/bc4c0d5b-6424-44bd-8445-833e01bb6af4.jpg?1562275603"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Tuktuk the Explorer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Volkan Baǵa"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/9/a/9a8aea2f-1e1d-4e0d-8370-207b6cae76e3.jpg?1562740084"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Tiana, Ship's Caretaker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Eric Deschamps"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/3/7/37ed04d3-cfa1-4778-aea6-b4c2c29e6e0a.jpg?1559959382"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Krenko, Tin Street Kingpin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Mark Behm"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/4/2/4256dcc1-0eee-4385-9a5c-70abb212bf49.jpg?1562397424"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Slobad, Goblin Tinkerer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Kev Walker"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/2/7/27907985-b5f6-4098-ab43-15a0c2bf94d5.jpg?1562728142"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Bruna, the Fading Light"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Clint Cearley"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/7/2/722b1e02-2268-4e02-8d09-9b337da2a844.jpg?1562405249"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Vial Smasher the Fierce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Deruchenko Alexander"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/8/b/8bd37a04-87b1-42ad-b3e2-f17cd8998f9d.jpg?1562923246"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Sliver Legion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Ron Spears"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/d/d/dd199a48-5ac8-4ab9-a33c-bbce6f7c9d1b.jpg?1559959197"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Zegana, Utopian Speaker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Slawomir Maniak"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"https://img.scryfall.com/cards/art_crop/front/d/d/ddb92ef6-0ef8-4b1d-8a45-3064fea23926.jpg?1562854687"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Avacyn, Angel of Hope"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Jason Chan"&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;Cool! We have our array of 9 &lt;code&gt;img_hash&lt;/code&gt; objects, each with a URL, name, and artist. &lt;strong&gt;Now let's render them!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Rendering Partials Inside Views and Other Partials
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Render a partial inside a view
&lt;/h2&gt;

&lt;p&gt;Back in our &lt;code&gt;/views/pages/index.html.erb&lt;/code&gt; view, we can now use &lt;code&gt;render partial:&lt;/code&gt; to access the partials we created.&lt;/p&gt;

&lt;p&gt;Let's start by simply rendering a &lt;code&gt;_card_image&lt;/code&gt; partial with &lt;code&gt;session[:img_array][0]["image"]&lt;/code&gt; supplying the URL:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_card_image.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/cards/_card_image.html.erb %&amp;gt;

&amp;lt;%= image_tag(session[:img_array][0]["image"]) %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;index.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/index.html.erb %&amp;gt;

&amp;lt;h1&amp;gt;Pages#&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&amp;gt;
&amp;lt;p&amp;gt;Find me in app/&lt;/span&gt;&lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;index&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;

&amp;lt;h2&amp;gt;Let's add some Magic: the Gathering card art!&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render partial: '/pages/cards/card_image' %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that in &lt;code&gt;render partial:&lt;/code&gt;, we drop the underscore from the beginning of &lt;code&gt;_card_image.html.erb&lt;/code&gt; and simply call it  as &lt;code&gt;card_image&lt;/code&gt; (plus its path relative to the &lt;code&gt;views&lt;/code&gt; directory).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhbahpl6puvvuty79qipy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhbahpl6puvvuty79qipy.png" alt="screenshot of rendered card art labeled "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cool! Our partial is rendering with the URL from our &lt;code&gt;img_hash&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Render a partial inside another partial
&lt;/h2&gt;

&lt;p&gt;Let's make use of our &lt;code&gt;_card.html.erb&lt;/code&gt; partial, and render the &lt;code&gt;_card_image.html.erb&lt;/code&gt; partial inside it. We'll also wrap each partial's contents in a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; so we can see the DOM tree more clearly in our inspector:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/index.html.erb %&amp;gt;

&amp;lt;div class="card_container"&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card' %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;_card.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/cards/_card.html.erb %&amp;gt;

&amp;lt;div class="card"&amp;gt;
    &amp;lt;h3&amp;gt;This is the _card partial&amp;lt;/h3&amp;gt;

    &amp;lt;%= render partial: '/pages/cards/card_image' %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;_card_image.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/cards/_card_image.html.erb %&amp;gt;

&amp;lt;div class="card_image"&amp;gt;
    &amp;lt;h4&amp;gt;This is the _card_image partial&amp;lt;/h4&amp;gt;

    &amp;lt;%= image_tag(session[:img_array][0]["image"]) %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our browser, with inspector open, we can see that the &lt;code&gt;_card_image&lt;/code&gt; partial is its own &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; within the &lt;code&gt;_card&lt;/code&gt; partial's &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgw60l1a40o5ioyreh077.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgw60l1a40o5ioyreh077.png" alt="screenshot showing rendered card highlighted and developer tools with divs nested in DOM tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is exactly the nested behavior we expected!&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering partials multiple times
&lt;/h2&gt;

&lt;p&gt;We can also use &lt;code&gt;render partial:&lt;/code&gt; to render the same partial multiple times:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/index.html.erb %&amp;gt;

&amp;lt;div class="card_container"&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card' %&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card' %&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card' %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This results in three cards being created as sibling DOM elements:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxq7gokx1zfja01sw21qx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxq7gokx1zfja01sw21qx.png" alt="screenshot showing three identical cards being rendered, with bottom one highlighted to show it as a separate sibling div"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Passing Variables to Partials with &lt;code&gt;locals:&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Instead of accessing our long-winded &lt;code&gt;session[:img_array]&lt;/code&gt; variable repeatedly, we can pass variables directly to partials with the &lt;code&gt;locals:&lt;/code&gt; option inside &lt;code&gt;render partial:&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;index.html.erb&lt;/code&gt;, let's change our three &lt;code&gt;render partial:&lt;/code&gt; lines to include a different &lt;code&gt;img_hash&lt;/code&gt; from &lt;code&gt;sessions[:img_array]&lt;/code&gt; in each card:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/index.html.erb %&amp;gt;

&amp;lt;div class="card_container"&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card', locals: {img_hash: session[:img_array][0]} %&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card', locals: {img_hash: session[:img_array][1]} %&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card', locals: {img_hash: session[:img_array][2]} %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, each &lt;code&gt;_card&lt;/code&gt; partial now has a different piece of art to render!&lt;/p&gt;

&lt;p&gt;Let's go back and build out our &lt;code&gt;_card&lt;/code&gt; partial to render the &lt;code&gt;_card_image&lt;/code&gt;, &lt;code&gt;_card_name&lt;/code&gt;, and &lt;code&gt;_card_artist&lt;/code&gt; partials. Each &lt;code&gt;_card&lt;/code&gt; will also use &lt;code&gt;locals:&lt;/code&gt; to pass the contents of its &lt;code&gt;img_hash&lt;/code&gt; to those partials. &lt;em&gt;(Note that, for ease of reading, we are nesting all these partials inside one &lt;code&gt;&amp;lt;div class="card&amp;gt;&lt;/code&gt; tag.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_card.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/cards/_card.html.erb %&amp;gt;

&amp;lt;div class="card"&amp;gt;
    &amp;lt;h3&amp;gt;This is the _card partial&amp;lt;/h3&amp;gt;

    &amp;lt;%= render partial: '/pages/cards/card_image', locals: {image: img_hash["image"]} %&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card_name', locals: {name: img_hash["name"]} %&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card_artist', locals: {artist: img_hash["artist"]} %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;_card_image.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/cards/_card_image.html.erb %&amp;gt;

&amp;lt;%= image_tag(image) %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;_card_name.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/cards/_card_name.html.erb %&amp;gt;

&amp;lt;p&amp;gt; Name: &amp;lt;%= name %&amp;gt; &amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;_card_artist.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/cards/_card_artist.html.erb %&amp;gt;

&amp;lt;p&amp;gt; Artist: &amp;lt;%= artist %&amp;gt; &amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check back on &lt;code&gt;localhost:3000&lt;/code&gt; to see if we've got some different cards now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbkybh26fm4jm1u8kifpt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbkybh26fm4jm1u8kifpt.png" alt="screenshot showing three different cards being rendered, including names and artists"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! Now, let's try &lt;strong&gt;iterating&lt;/strong&gt; through our whole &lt;code&gt;sessions[:img_array]&lt;/code&gt; to display all 9 cards!&lt;/p&gt;

&lt;h1&gt;
  
  
  Rendering Partials Repeatedly by Iterating with &lt;code&gt;collection:&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;We can refactor our card-rendering code in &lt;code&gt;index.html.erb&lt;/code&gt; by adding &lt;code&gt;collection: session[:img_array], as: :img_hash&lt;/code&gt;. This will tell Rails to use the array stored in &lt;code&gt;session[:img_array]&lt;/code&gt; and pass each object aliased as &lt;code&gt;img_hash&lt;/code&gt; to its partial:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/index.html.erb %&amp;gt;

&amp;lt;div class="card_container"&amp;gt;
    &amp;lt;%= render partial: '/pages/cards/card', collection: session[:img_array], as: :img_hash %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we should expect 9 different cards to be rendered at &lt;code&gt;localhost:3000&lt;/code&gt;. Since we previously used &lt;code&gt;locals:&lt;/code&gt; to pass our hashes with the alias &lt;code&gt;img_hash&lt;/code&gt;, our other partials should need no changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbgsl214p77ndofwgdur2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbgsl214p77ndofwgdur2.png" alt="screenshot showing four different cards being rendered, with the browser cut off but indicating that more are rendered underneath"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success!!&lt;/p&gt;

&lt;h1&gt;
  
  
  Finishing Up: Adding a Form with a Refresh Button and Counter
&lt;/h1&gt;

&lt;p&gt;Okay, you're probably bored of looking at Tuktuk by now--I know I am! So, let's go ahead and add our &lt;code&gt;_refresh_button&lt;/code&gt; and &lt;code&gt;_refresh_counter&lt;/code&gt; partials to our &lt;code&gt;index.html.erb&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/index.html.erb %&amp;gt;

&amp;lt;div class="refresh_form"&amp;gt;
    &amp;lt;%= render partial: '/pages/forms/refresh_button' %&amp;gt;
    &amp;lt;%= render partial: '/pages/forms/refresh_counter', locals: {counter: @refresh_counter} %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we are passing a &lt;code&gt;@refresh_counter&lt;/code&gt; variable through &lt;code&gt;locals:&lt;/code&gt;, lets go ahead and define that in our Pages controller:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pages_controller.rb&lt;/strong&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="c1"&gt;# /app/controllers/pages/page_controller.rb&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"button_action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"refresh"&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@refresh_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:refresh_counter&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;Great! Now our &lt;code&gt;session&lt;/code&gt; will keep track of the number of times we've hit the refresh button. &lt;/p&gt;

&lt;p&gt;And in our form partials:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_refresh_button.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/forms/_refresh_button.html.erb %&amp;gt;

&amp;lt;%= form_for :form_data do |f| %&amp;gt;
    &amp;lt;%= f.button "Refresh", name: "button_action", value: "refresh" %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;_refresh_counter.html.erb&lt;/strong&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%# /app/views/pages/forms/_refresh_counter.html.erb %&amp;gt;

&amp;lt;p&amp;gt;Refresh counter: &amp;lt;%= counter %&amp;gt; &amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, our page on displays a Refresh button along with the counter's value:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmx8j3i50vogv8zzjg9ym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmx8j3i50vogv8zzjg9ym.png" alt="screenshot showing bottom of the rendered cards, with refresh button and counter highlighted"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And hitting refresh will update our cards (and increment the counter by one)!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcwo7jl0je9xlnkz2nji1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcwo7jl0je9xlnkz2nji1.png" alt="screenshot showing bottom of the rendered cards with different cards than before, with highlighted refresh counter showing number 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We've covered how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create partial views in Rails&lt;/li&gt;
&lt;li&gt;render them with &lt;code&gt;render partial:&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;pass variables to them with &lt;code&gt;locals:&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;iterate through collections to repeatedly render a partial with &lt;code&gt;collection:&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;(Hopefully, you've seen some good Magic: the Gathering art along the way too!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the GitHub repo if you're interested in seeing the code or playing around with it yourself: &lt;a href="https://github.com/isalevine/devto-rails-partial-view-demo" rel="noopener noreferrer"&gt;https://github.com/isalevine/devto-rails-partial-view-demo&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Got any tips or tricks for using Rails partial views? Please feel free to comment and share below! :)
&lt;/h3&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Quick Intro to Ruby's Set, Part 2: Object Lookups and Efficiency</title>
      <dc:creator>Isa Levine</dc:creator>
      <pubDate>Sun, 20 Oct 2019 05:13:27 +0000</pubDate>
      <link>https://dev.to/isalevine/a-quick-intro-to-ruby-s-set-part-2-object-lookups-and-efficiency-lc</link>
      <guid>https://dev.to/isalevine/a-quick-intro-to-ruby-s-set-part-2-object-lookups-and-efficiency-lc</guid>
      <description>&lt;p&gt;Continuing from the previous article, we will now explore some of Set's methods, and compare their benchmarks with equivalent operations on arrays.&lt;/p&gt;

&lt;p&gt;To review, some key details about sets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All objects in a Set are &lt;strong&gt;guaranteed unique&lt;/strong&gt; &lt;em&gt;(arrays can have duplicates)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Objects in a Set are &lt;strong&gt;not&lt;/strong&gt; ordered &lt;em&gt;(arrays are ordered by index)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Sets are built on top of Hashes for &lt;strong&gt;super-fast object lookup&lt;/strong&gt; &lt;em&gt;(arrays are just dynamic arrays under-the-hood)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or, &lt;a href="https://www.rubyguides.com/2018/08/ruby-set-class/"&gt;summarized by RubyGuides' article on the Set class:&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A set is a class that stores items like an array…&lt;/p&gt;

&lt;p&gt;But with some special attributes that make it  &lt;strong&gt;10x faster&lt;/strong&gt;  in specific situations!&lt;/p&gt;

&lt;p&gt;On top of that:&lt;/p&gt;

&lt;p&gt;All the items in a set are  &lt;strong&gt;guaranteed to be unique&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As we will see, my particular benchmarks don't quite reach 10x faster efficiency--but we'll explore situations where sets are certainly &lt;strong&gt;more&lt;/strong&gt; efficient than an array!&lt;/p&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;In this second article, we'll compare the following operations between Ruby sets and arrays:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operations where sets are &lt;em&gt;faster&lt;/em&gt; than arrays:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;set.include?()&lt;/code&gt; vs. &lt;code&gt;array.include?()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set.add()&lt;/code&gt; vs. &lt;code&gt;array.push()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set.delete()&lt;/code&gt; vs. &lt;code&gt;array.delete()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set.subset?()&lt;/code&gt; vs. &lt;code&gt;(array1 &amp;amp; array2) == array2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operations where sets are &lt;em&gt;slower&lt;/em&gt; than arrays:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;set.replace()&lt;/code&gt; vs. &lt;code&gt;array.replace()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set1 == set2&lt;/code&gt; vs. &lt;code&gt;array1 == array2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Operations where sets are &lt;em&gt;faster&lt;/em&gt; than arrays
&lt;/h1&gt;

&lt;p&gt;Let's start with methods where sets are more efficient than arrays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: If you're coding along at home, remember to include &lt;code&gt;require 'benchmark'&lt;/code&gt; to avoid an error with the &lt;code&gt;Benchmark&lt;/code&gt; class!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;set.include?()&lt;/code&gt; vs. &lt;code&gt;array.include?()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Since sets are built on top of Ruby's hash, the &lt;code&gt;.include()&lt;/code&gt; method is where they really shine. It simply checks if an element is present, returning a boolean.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.rubyguides.com/2018/08/ruby-set-class/"&gt;RubyGuides article above&lt;/a&gt; reports these benchmarks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;set&lt;/span&gt;   &lt;span class="ss"&gt;include: &lt;/span&gt;&lt;span class="mf"&gt;8381985.2&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;s&lt;/span&gt;
&lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="ss"&gt;include: &lt;/span&gt;&lt;span class="mf"&gt;703305.5&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;11.92&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;  &lt;span class="n"&gt;slower&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And goes on to explain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The reason for this difference is that  &lt;strong&gt;an array has to check every single element&lt;/strong&gt;...&lt;/p&gt;

&lt;p&gt;...if you have a 1 million element array it's going to be checking 1 million elements every time you call  &lt;code&gt;include?&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A set doesn't have to do that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's how we'll be testing it, using the same sets and arrays from our previous article. Each operation will be performed within &lt;code&gt;Benchmark.bm&lt;/code&gt; 5 million times, and will be reported with &lt;code&gt;.report&lt;/code&gt;. &lt;em&gt;(The large spacing is for pretty-printing in the console!)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;

&lt;span class="n"&gt;filter_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&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="s2"&gt;"Set   .include? (beginning)       :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a"&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="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="s2"&gt;"Array .include? (beginning)       :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a"&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="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="s2"&gt;"Set   .include? (middle)          :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"they"&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="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="s2"&gt;"Array .include? (middle)          :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"they"&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="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="s2"&gt;"Set   .include? (end)             :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"at"&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="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="s2"&gt;"Array .include? (end)             :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"at"&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Above, we have the array checking for an element at the beginning, middle, and end of the array. For kicks, we have the set looking up elements that are &lt;em&gt;hard-coded&lt;/em&gt; in the same order--but, &lt;strong&gt;because sets are built on top of hashes&lt;/strong&gt;, there is no object order in a set!&lt;/p&gt;

&lt;p&gt;Here's our output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                         user     system      total        real
Set   .include? (beginning)       :  0.796039   0.001551   0.797590 (  0.799956)
Array .include? (beginning)       :  0.632907   0.000869   0.633776 (  0.637059)
Set   .include? (middle)          :  0.786981   0.000610   0.787591 (  0.788172)
Array .include? (middle)          :  1.294061   0.001416   1.295477 (  1.298378)
Set   .include? (end)             :  0.792309   0.000866   0.793175 (  0.795283)
Array .include? (end)             :  2.476954   0.002786   2.479740 (  2.484839)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As expected, the array takes longer to find the closer the element is to the end. Meanwhile, the set's lookup time is essentially constant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the worst-case scenario, &lt;code&gt;set.include?()&lt;/code&gt; will perform over 3x faster than &lt;code&gt;array.include?()&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;set.add()&lt;/code&gt; vs. &lt;code&gt;array.push()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Next, let's compare the &lt;code&gt;set.add()&lt;/code&gt; method introduced in the previous article to &lt;code&gt;array.push()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The array &lt;code&gt;push&lt;/code&gt; method will add an element to the end of an array--this is the most efficient way to add elements (assuming you &lt;a href="https://www.sitepoint.com/rubys-missing-data-structure/"&gt;don't have to increase the array's capacity under-the-hood&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;&lt;code&gt;set.add()&lt;/code&gt;, on the other hand, simply adds a key-value pair to its under-the-hood hash, with the key being the added element, and its value being a boolean &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In this and several following tests, you'll notice that these operations include a first step of duplicating the original set/array. This is to ensure that the &lt;em&gt;same&lt;/em&gt; operation is being performed--as opposed to the set adding an element &lt;em&gt;only once&lt;/em&gt; and then rejecting it the other 4999999 times!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;

&lt;span class="n"&gt;filter_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&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="s2"&gt;"Set   .add  (successful)          :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;
            &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"big"&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="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="s2"&gt;"Array .push (successful)          :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"big"&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="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="s2"&gt;"Set   .add  (failed)              :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;
            &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"they"&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="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="s2"&gt;"Array .push (if not present)      :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;   
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="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;Here's our output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                         user     system      total        real
Set   .add  (successful)          :  0.929103   0.000749   0.929852 (  0.931718)
Array .push (successful)          :  0.909708   0.094546   1.004254 (  1.005897)
Set   .add  (failed)              :  0.979433   0.029193   1.008626 (  1.011531)
Array .push (if not present)      :  1.327389   0.001286   1.328675 (  1.330673)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;At  best, our set adds elements approximately as fast as an array's &lt;code&gt;push&lt;/code&gt;. &lt;strong&gt;In situations where we need to check an array for an element before adding it, we see sets outperforming arrays by about 36%.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;set.delete()&lt;/code&gt; vs. &lt;code&gt;array.delete()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Both &lt;code&gt;set.delete()&lt;/code&gt; and &lt;code&gt;array.delete()&lt;/code&gt; require a lookup for an element before removing it, so this is a great comparison of their equivalent abilities!&lt;/p&gt;

&lt;p&gt;Here's our code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;

&lt;span class="n"&gt;filter_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&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="s2"&gt;"Set   .delete (beginning)         :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
            &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;
            &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="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="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Array .delete (beginning)         :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="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="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Set   .delete (middle)            :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
            &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;
            &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"they"&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="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="s2"&gt;"Array .delete (middle)            :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"they"&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="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="s2"&gt;"Set   .delete (end)               :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
            &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;
            &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"at"&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="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="s2"&gt;"Array .delete (end)               :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;
            &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"at"&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once again, we're measuring an array's ability to delete elements at their beginning, middle, and end (and illustrating that sets have no such order).&lt;/p&gt;

&lt;p&gt;Here's our output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                         user     system      total        real
Set   .delete (beginning)         :  0.812703   0.000889   0.813592 (  0.816582)
Array .delete (beginning)         :  1.932468   0.001862   1.934330 (  1.938841)
Set   .delete (middle)            :  0.811337   0.000912   0.812249 (  0.814055)
Array .delete (middle)            :  2.053270   0.001698   2.054968 (  2.059913)
Set   .delete (end)               :  0.813986   0.000986   0.814972 (  0.816330)
Array .delete (end)               :  2.203437   0.002076   2.205513 (  2.210077)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;As expected, sets can delete elements in constant time.&lt;/strong&gt; Interestingly, arrays are able to delete elements at the end almost as fast as ones at their beginning!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;set.subset?()&lt;/code&gt; vs. &lt;code&gt;(array1 &amp;amp; array2) == array2&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One of sets' most useful features is the &lt;code&gt;subset?&lt;/code&gt; method. A subset is a set where all of its elements are also elements of its superset.&lt;/p&gt;

&lt;p&gt;While arrays don't have an exactly equivalent method, &lt;a href="https://stackoverflow.com/a/10567468"&gt;this excellent StackOverflow answer&lt;/a&gt; illustrates how the array &lt;code&gt;&amp;amp;&lt;/code&gt; method can be used for the same effect.&lt;/p&gt;

&lt;p&gt;Here's our code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;

&lt;span class="n"&gt;filter_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;sub_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;sub_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;replace_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"big"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"knight"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;replace_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"big"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"knight"&lt;/span&gt;&lt;span class="p"&gt;]&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&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="s2"&gt;"Set   .subset?          (true)    :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;sub_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subset?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_set&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="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="s2"&gt;"Array (a1 &amp;amp; a2) == a2   (true)    :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;sub_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sub_array&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="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="s2"&gt;"Set   .subset?          (false)   :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;replace_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subset?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_set&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="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="s2"&gt;"Array (a1 &amp;amp; a2) == a2   (false)   :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;replace_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;replace_array&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our code compares sets and arrays in situations where &lt;code&gt;subset?&lt;/code&gt; and &lt;code&gt;(a1 &amp;amp; a2) == a2&lt;/code&gt; return both true and false.&lt;/p&gt;

&lt;p&gt;Here's our output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                         user     system      total        real
Set   .subset?          (true)    :  2.076477   0.001450   2.077927 (  2.080519)
Array (a1 &amp;amp; a2) == a2   (true)    :  7.961141   0.180642   8.141783 (  8.158982)
Set   .subset?          (false)   :  1.554540   0.001360   1.555900 (  1.558715)
Array (a1 &amp;amp; a2) == a2   (false)   :  5.280627   0.006986   5.287613 (  5.297066)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Wow! &lt;strong&gt;In both situations, we see sets performing &lt;code&gt;subset?&lt;/code&gt; over 3x as fast as the array's equivalent!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Operations where sets are &lt;em&gt;slower&lt;/em&gt; than arrays
&lt;/h1&gt;

&lt;p&gt;Now, let's turn our attention to some situations where sets &lt;em&gt;do not&lt;/em&gt; perform as well as arrays.&lt;/p&gt;

&lt;p&gt;Unfortunately, I cannot offer under-the-hood explanations for these results at this time. &lt;strong&gt;Please feel free to comment below and share your wisdom of why arrays outperform sets in the following operations! :)&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;set.replace()&lt;/code&gt; vs. &lt;code&gt;array.replace()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In both structures, the &lt;code&gt;replace&lt;/code&gt; method &lt;a href="https://ruby-doc.org/stdlib-2.6.5/libdoc/set/rdoc/Set.html#method-i-replace"&gt;completely replaces the contents of the given structure with the elements from a supplied enumerable&lt;/a&gt;. In our code, we will be replacing a set with a set, and an array with an array.&lt;/p&gt;

&lt;p&gt;Here's our code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;

&lt;span class="n"&gt;filter_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;replace_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"big"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"knight"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;replace_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"big"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"knight"&lt;/span&gt;&lt;span class="p"&gt;]&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&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="s2"&gt;"Set   .replace                    :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replace_set&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="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="s2"&gt;"Array .replace                    :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replace_array&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here's our output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                         user     system      total        real
Set   .replace                    :  2.318287   0.001841   2.320128 (  2.327267)
Array .replace                    :  0.441079   0.000528   0.441607 (  0.442994)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ouch! &lt;strong&gt;Arrays implement &lt;code&gt;replace&lt;/code&gt; 5x faster than sets!&lt;/strong&gt; As stated above, I'd love to have someone fill me in on why!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;set1 == set2&lt;/code&gt; vs. &lt;code&gt;array1 == array2&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Finally, let's look at &lt;code&gt;==&lt;/code&gt;, where each pair of structures is checked for having the same elements--and for arrays, the same order. Once again, we will benchmark both true and false situations for each.&lt;/p&gt;

&lt;p&gt;Here's our code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;

&lt;span class="n"&gt;filter_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;match_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;match_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"an"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"the"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"be"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"they"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"their"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"them"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"had"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"but"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;replace_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"big"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"knight"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;replace_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"big"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"knight"&lt;/span&gt;&lt;span class="p"&gt;]&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&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="s2"&gt;"Set   ==  (true)                  :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;match_set&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="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="s2"&gt;"Array ==  (true)                  :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;match_array&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="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="s2"&gt;"Set   ==  (false)                 :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_set&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;replace_set&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="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="s2"&gt;"Array ==  (false)                 :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;filter_array&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;replace_array&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here's our output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                         user     system      total        real
Set   ==  (true)                  :  7.219614   0.004376   7.223990 (  7.234709)
Array ==  (true)                  :  4.119128   0.003112   4.122240 (  4.128884)
Set   ==  (false)                 :  1.097661   0.000751   1.098412 (  1.099235)
Array ==  (false)                 :  0.391299   0.000482   0.391781 (  0.393029)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In the best case, array's perform better than sets by about 75% when checking for true cases--and at worst, for false situations, arrays check about 275% faster than sets!&lt;/strong&gt;  Equality is definitely a situation to prefer arrays over sets. (Anyone care to explain why? &amp;lt;3)&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Now you're more familiar with Set's methods and cases where they may be advantageous to arrays! The two key situations to look for are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Needing to check via &lt;code&gt;include?&lt;/code&gt; in a collection of unique elements&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Needing to check for subsets of elements&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;h2&gt;
  
  
  Links and Sources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://spin.atomicobject.com/2012/09/04/when-is-a-set-better-than-an-array-in-ruby/"&gt;Excellent introduction to Ruby Sets by Al Scott&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-doc.org/stdlib-2.6.5/libdoc/set/rdoc/Set.html"&gt;Ruby docs - Sets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rubyguides.com/2018/08/ruby-set-class/"&gt;RubyGuides intro to Sets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.dotnetperls.com/set-ruby"&gt;DotNetPerls - Ruby Set examples&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Got any tips, tricks, or instances where you like to use sets? Please feel free to share in the comments below!
&lt;/h3&gt;

</description>
      <category>ruby</category>
      <category>tutorial</category>
      <category>set</category>
      <category>datastructures</category>
    </item>
  </channel>
</rss>
