<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jason Dinsmore</title>
    <description>The latest articles on Forem by Jason Dinsmore (@dinjas).</description>
    <link>https://forem.com/dinjas</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%2F116156%2F29f5acbb-2220-4a94-b3e2-c7aa36407c38.jpeg</url>
      <title>Forem: Jason Dinsmore</title>
      <link>https://forem.com/dinjas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dinjas"/>
    <language>en</language>
    <item>
      <title>Active Record Encryption in Rails 7</title>
      <dc:creator>Jason Dinsmore</dc:creator>
      <pubDate>Wed, 07 Jul 2021 04:47:04 +0000</pubDate>
      <link>https://forem.com/hint/active-record-encryption-22j3</link>
      <guid>https://forem.com/hint/active-record-encryption-22j3</guid>
      <description>&lt;p&gt;Rails 7 will be introducing a very cool new feature for ActiveRecord - application-level encryption, summoned by the mighty &lt;code&gt;encrypts&lt;/code&gt; declaration in a model. This new feature provides a layer of encryption that sits between our application code and the database. In essence, when our data using Active Record Encryption has been loaded into an AR object, it will be unencrypted, and when it is sitting in the database it will be encrypted.&lt;/p&gt;

&lt;p&gt;In this post, we will take a high-level look at how to use this functionality, discuss some cool things it can do, and acknowledge some of its limitations.&lt;/p&gt;

&lt;p&gt;Before we dive in, I would be remiss if I didn't point to the excellent documentation in the &lt;a href="https://guides.rubyonrails.org/active_record_encryption.html" rel="noopener noreferrer"&gt;Rails guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the duration of the post, I will refer to the feature as &lt;strong&gt;encrypts&lt;/strong&gt; for brevity (and also because I am not sure what else to call it 😉).&lt;/p&gt;

&lt;h2&gt;
  
  
  Backstory
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;encrypts&lt;/strong&gt; was merged into Rails in &lt;a href="https://github.com/rails/rails/pull/41659" rel="noopener noreferrer"&gt;PR 41659&lt;/a&gt; by &lt;a href="https://github.com/jorgemanrubia" rel="noopener noreferrer"&gt;@jorgemanrubia&lt;/a&gt;. In the PR description he states that the functionality is an extraction from &lt;a href="https://hey.com/" rel="noopener noreferrer"&gt;HEY&lt;/a&gt;, which had the implementation reviewed by a security firm. If you are interested in hearing more of the story behind the feature, I'd recommend &lt;a href="https://world.hey.com/jorge/a-story-of-rails-encryption-ce104b67" rel="noopener noreferrer"&gt;Jorge's blog post&lt;/a&gt; on the subject - it's an interesting read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deterministic Sidebar
&lt;/h2&gt;

&lt;p&gt;Let's take a quick second to discuss the difference between deterministic and non-deterministic encryption. It's really pretty simple, but is a central tenet in understanding how to use &lt;strong&gt;encrypts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of encryption as a function that is applied to some input (text), resulting in some output (encrypted text).&lt;/p&gt;

&lt;p&gt;If the function is deterministic, then any time the function is applied to the same text, it will output the same result.&lt;/p&gt;

&lt;p&gt;If the encryption function is non-deterministic, then we can't predict what the output will be the second time we encrypt a given value. In theory, it is possible we would get the same result we got the first time, but the odds of that happening are very, very low. In the context of &lt;strong&gt;encrypts&lt;/strong&gt; with its default non-deterministic configurations, subsequent encryptions of the same plaintext are almost certain to produce different ciphertexts.&lt;/p&gt;

&lt;p&gt;If we are using deterministic encryption with &lt;strong&gt;encrypts&lt;/strong&gt; for a model attribute, then any two rows in the database that would have the same plaintext value will also have the same stored encrypted value. If we using non-deterministic encryption, then two rows that have the same plaintext value will generally have different encrypted values. As we'll see in a bit, this has implications as to whether we can query the encrypted data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;There's not a ton of configuration required to get going with &lt;strong&gt;encrypts&lt;/strong&gt;, but there are some things to be aware of.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keys
&lt;/h3&gt;

&lt;p&gt;The main requisite is that we will need to generate a set of keys and add them to our credentials file(s). We can generate the keys to add by running: &lt;code&gt;bin/rails db:encryption:init&lt;/code&gt;, which will output something like:&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;Add&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="ss"&gt;environment:

active_record_encryption:
  primary_key: &lt;/span&gt;&lt;span class="n"&gt;zxMXS0hBbpa5BzRKPv9HOSF9etBySiHQ&lt;/span&gt;
  &lt;span class="ss"&gt;deterministic_key: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;pM2UHHBQr1kf1irO6JgakcSOXu0r1Vn&lt;/span&gt;
  &lt;span class="ss"&gt;key_derivation_salt: &lt;/span&gt;&lt;span class="no"&gt;I5AkViD0UJhSqK3NY49Zvsls3ZoifyXx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;primary key&lt;/code&gt; is used to derive the root encryption key for non-deterministic encryption. Note that the &lt;code&gt;primary_key&lt;/code&gt; value in the credentials file can also be a list of keys.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;deterministic_key&lt;/code&gt; is used for deterministic encryption. If you recall from the section on determinism above, we'll get the same result if we encrypt the same data with this key multiple times. Currently, &lt;strong&gt;encrypts&lt;/strong&gt; does not support using a list of keys for deterministic encryption. If we want to completely disable deterministic encryption, not providing the key is a sure-fire off switch.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;key_derivation_salt&lt;/code&gt; is used to derive encryption keys.&lt;/p&gt;

&lt;h3&gt;
  
  
  App Configuration
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://guides.rubyonrails.org/v7.0.0/active_record_encryption.html#configuration-options" rel="noopener noreferrer"&gt;&lt;strong&gt;encrypts&lt;/strong&gt; API&lt;/a&gt; provides several configuration options. All of the options are defined in the &lt;code&gt;config.active_record.encryption&lt;/code&gt; namespace and I'd encourage reading through them if you are going to use this feature. I believe you'll find that most of the options have pretty reasonable defaults.&lt;/p&gt;

&lt;p&gt;I will mention the &lt;code&gt;config.active_record.encryption.extend_queries&lt;/code&gt; option as it defaults to &lt;code&gt;false&lt;/code&gt;, but enabling it has several implications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enables querying unencrypted data in an encrypted column (also need to enable &lt;code&gt;config.active_record.encryption.support_unencrypted_data&lt;/code&gt; for this)&lt;/li&gt;
&lt;li&gt;allows supporting multiple encryption schemes&lt;/li&gt;
&lt;li&gt;enables  support for uniqueness validations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database
&lt;/h3&gt;

&lt;p&gt;When an encrypted string or text attribute is stored in the database, it isn't stored as an ordinary string or text - it is stored as a more complex data structure that is serialized when written and deserialized when read. This data structure allows some meta information to be stored along with the encrypted text, which gives the app some clues about how the text was encrypted.&lt;/p&gt;

&lt;p&gt;This extra meta-info introduces some storage overhead - up to 250 bytes.&lt;/p&gt;

&lt;p&gt;The guide recommends increasing the size of a 255 byte string field to 512 bytes if using encryption on a column. It also says that for a text field, the overhead is generally negligible.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Invocation
&lt;/h2&gt;

&lt;p&gt;Finally, we can talk about actually using the thing!&lt;/p&gt;

&lt;p&gt;In the most basic use case, to encrypt a single column, we simply add an &lt;strong&gt;encrypts&lt;/strong&gt; declaration to our model for the attribute we want to encrypt. For example, if we had a &lt;code&gt;Dog&lt;/code&gt; model with a &lt;code&gt;toy_location&lt;/code&gt; field (dogs like to hide their toys, you know) that needs encryption, our model would look like:&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;Dog&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;encrypts&lt;/span&gt; &lt;span class="ss"&gt;:toy_location&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty simple, eh?&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing
&lt;/h2&gt;

&lt;p&gt;Writing an encrypted attribute is completely transparent. We just do what we would normally do in Rails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Bruno'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;toy_location: &lt;/span&gt;&lt;span class="s1"&gt;'top secret'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we were to look at the content sitting in the database directly, we would see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT toy_location FROM dogs LIMIT 1'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.4&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;toy_location&lt;/span&gt; &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dogs&lt;/span&gt; &lt;span class="no"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"toy_location"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;oVgEJvRaX6DJvA==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iv&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;WYypcKysgBY05Tum&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;at&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;OaBswq+wyriuRQO8yCVD3w==&lt;/span&gt;&lt;span class="se"&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;/div&gt;



&lt;p&gt;The value here is just serialized JSON, let's go ahead and parse 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="o"&gt;&amp;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;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'toy_location'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"p"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"oVgEJvRaX6DJvA=="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"h"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"iv"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"WYypcKysgBY05Tum"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"OaBswq+wyriuRQO8yCVD3w=="&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gave us a Hash. Most of the keys in this Hash are defined in the &lt;a href="https://github.com/rails/rails/blob/c9a0f1ab9616ca8e94f03327259ab61d22f04b51/activerecord/lib/active_record/encryption/properties.rb#L23-L30" rel="noopener noreferrer"&gt;&lt;code&gt;ActiveRecord::Encryption::Properties::DEFAULT_PROPERTIES&lt;/code&gt;&lt;/a&gt; constant. &lt;code&gt;p&lt;/code&gt; is the payload, aka the encrypted plaintext. &lt;code&gt;h&lt;/code&gt; is a Hash of headers that contain information relating to the encryption operation. Here, &lt;code&gt;iv&lt;/code&gt; is the initialization vector the plaintext was encrypted with - more about this in the next section on searching, and &lt;code&gt;at&lt;/code&gt; is an &lt;code&gt;auth_tag&lt;/code&gt; that will be used during the decryption process to verify that the encrypted text hasn't been altered. You may notice other headers from the &lt;code&gt;DEFAULT_PROPERTIES&lt;/code&gt; Hash above depending on how your encryption is set up and being used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading
&lt;/h2&gt;

&lt;p&gt;When we load a model with an encrypted attribute, Rails will seamlessly decrypt the encrypted value. Let's find the &lt;code&gt;Dog&lt;/code&gt; we created above by his name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Dog&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;name: &lt;/span&gt;&lt;span class="s1"&gt;'Bruno'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toy_location&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="no"&gt;Dog&lt;/span&gt; &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Bruno"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;toy_location: &lt;/span&gt;&lt;span class="s2"&gt;"top secret"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;created_at: &lt;/span&gt;&lt;span class="s2"&gt;"2021-05-28 22:41:23.142635000 +0000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;updated_at: &lt;/span&gt;&lt;span class="s2"&gt;"2021-05-28 22:41:23.142635000 +0000"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the encrypted value was automatically translated to a readable attribute on our model instance - pretty slick.&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching
&lt;/h2&gt;

&lt;p&gt;What if we wanted to find Bruno by his &lt;code&gt;toy_location&lt;/code&gt; instead of his name? We can do that just like we would if the field were not encrypted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&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;toy_location: &lt;/span&gt;&lt;span class="s1"&gt;'top secret'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Dog&lt;/span&gt; &lt;span class="no"&gt;Load&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="s2"&gt;"dogs"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;*&lt;/span&gt; &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="s2"&gt;"dogs"&lt;/span&gt; &lt;span class="no"&gt;WHERE&lt;/span&gt; &lt;span class="s2"&gt;"dogs"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"toy_location"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="no"&gt;LIMIT&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;  &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;"toy_location"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;oVgEJvRaX6DJvA==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iv&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;WYypcKysgBY05Tum&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;at&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;OaBswq+wyriuRQO8yCVD3w==&lt;/span&gt;&lt;span class="se"&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="s2"&gt;"LIMIT"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;Dog id: 1, name: "Bruno", toy_location: "top secret", created_at: "2021-05-28 22:41:23.142635000 +0000", updated_at: "2021-05-28 22:41:23.142635000 +0000"&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that our query string was automatically converted into the encrypted JSON string we saw when we looked in the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialization Vector/Determinism
&lt;/h3&gt;

&lt;p&gt;When using deterministic encryption, all records with the same plaintext value will use the same initialization vector to encrypt. This is so ActiveRecord will generate the same ciphertext for the same input, which is a prerequisite for being able to search the encrypted data. Under the hood, Rails uses the plaintext to generate the initialization vector for deterministically encrypted data - otherwise the IV is randomly generated.&lt;/p&gt;

&lt;p&gt;If two rows with the same plaintext were to use different initialization vectors to perform the encryption, the serialized JSON that ends up in the database would be completely different.&lt;/p&gt;

&lt;p&gt;In order to be able to perform searches on the encrypted data, the stored values need to be exactly the same.&lt;/p&gt;

&lt;p&gt;This means that all of the stored values in the serialized hash need to be identical for two rows that have the same text value, AND that Rails can re-compute the exact same hash on the fly to find rows that are matches for the search string.&lt;/p&gt;

&lt;p&gt;Determinism at its finest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Searching Plaintext
&lt;/h3&gt;

&lt;p&gt;What if we did not have the luxury of starting with pristine data? For example, if our &lt;code&gt;Dog&lt;/code&gt; table already existed and had a pre-existing &lt;code&gt;toy_location&lt;/code&gt; column on it that was not encrypted?&lt;/p&gt;

&lt;p&gt;Well, as we can see by the query generated above, if we had a &lt;code&gt;Dog&lt;/code&gt; record with &lt;code&gt;top secret&lt;/code&gt; (unencrypted) as its &lt;code&gt;toy_location&lt;/code&gt;, that query aint gonna find it. Also, it seems pretty likely that if we try to load a &lt;code&gt;Dog&lt;/code&gt; record with stored plaintext into memory, Rails is going to have problems when it attempts to decrypt the plaintext.&lt;/p&gt;

&lt;p&gt;One option we have would be to convert our plaintext data to encrypted data, which seems ideal to me. We may have reasons to want to avoid doing a data migration like that, however.&lt;/p&gt;

&lt;p&gt;In that case, &lt;strong&gt;encrypts&lt;/strong&gt; will allow us to keep storing the plaintext values as plaintext, and will encrypt any new or updated data. To opt-in to supporting a mix of encrypted/unencrypted data, enable the &lt;code&gt;config.active_record.encryption.support_unencrypted_data&lt;/code&gt; configuration option.&lt;/p&gt;

&lt;p&gt;Enabling this behavior will prevent the errors we would get when it tries to decrypt the plaintext and will also allow us to perform searches when a column contains a mismatch of plaintext and encrypted data.&lt;/p&gt;

&lt;p&gt;If we enable the setting and re-run our search query above, we'll see:&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;Dog&lt;/span&gt; &lt;span class="no"&gt;Load&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="s2"&gt;"dogs"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;*&lt;/span&gt; &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="s2"&gt;"dogs"&lt;/span&gt; &lt;span class="no"&gt;WHERE&lt;/span&gt; &lt;span class="s2"&gt;"dogs"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"toy_location"&lt;/span&gt; &lt;span class="no"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;?,&lt;/span&gt; &lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="no"&gt;LIMIT&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;  &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;"toy_location"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Bd+/TzEysF2CCQ==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iv&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;R2IUJJ+EmnDnZvQP&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;at&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;zqG5WAJql1zgctRCPpoBkQ==&lt;/span&gt;&lt;span class="se"&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="s2"&gt;"toy_location"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"top secret"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"LIMIT"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, it is looking for records that have the encrypted content or have the plaintext version of that content. Perfect!&lt;/p&gt;

&lt;h3&gt;
  
  
  Case Insensitive Searches
&lt;/h3&gt;

&lt;p&gt;By default, searching is case sensitive. If we need to ignore case when searching for some reason, we have a few options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can query for all of the case variations we need to match - eg. &lt;code&gt;Dog.where(toy_location: ['Top secret', 'top secret'])&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can specify &lt;code&gt;downcase: true&lt;/code&gt; on our &lt;strong&gt;encrypts&lt;/strong&gt; declaration. This will cause the text to be downcased before it is stored. ActiveRecord will automatically downcase our search text when performing queries. The downside here is that all case information is lost when it is downcased. Sorry to be a downer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 3:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can specify &lt;code&gt;ignore_case: true&lt;/code&gt; on the &lt;strong&gt;encrypts&lt;/strong&gt; declaration and add an &lt;code&gt;original_column_name&lt;/code&gt; column to our database (eg. &lt;code&gt;original_toy_location&lt;/code&gt;). With this in place, if we created a dog with an uppercase letter in the encrypted field:&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;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Max'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;toy_location: &lt;/span&gt;&lt;span class="s1"&gt;'Top secret'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the &lt;code&gt;toy_location&lt;/code&gt; column would be populated with the encrypted form of &lt;code&gt;'top secret'&lt;/code&gt; (the value downcased), and the &lt;code&gt;original_toy_location&lt;/code&gt; column will have the encrypted form of &lt;code&gt;'Top secret'&lt;/code&gt; (the value we set).&lt;/p&gt;

&lt;p&gt;Any searches would be done against the &lt;code&gt;toy_location&lt;/code&gt; column, and the model's &lt;code&gt;toy_location&lt;/code&gt; attribute would be populated from the &lt;code&gt;original_toy_location&lt;/code&gt; column when it is loaded into memory.&lt;/p&gt;

&lt;p&gt;One thing to note here - while the &lt;code&gt;toy_location&lt;/code&gt; column is encrypted deterministically in this situation (so it can be searched), the &lt;code&gt;original_toy_location&lt;/code&gt; column appears to be encrypted non-deterministically. This makes sense, since that column does not need to support searching. This can be confirmed by comparing the &lt;code&gt;toy_location&lt;/code&gt; and &lt;code&gt;original_toy_location&lt;/code&gt; values for two records with the same plaintext value. As you can see below, they have the same stored values (initialization vector, payload, etc) for the &lt;code&gt;toy_location&lt;/code&gt; column (searchable and downcased), and different stored values for the &lt;code&gt;original_toy_location&lt;/code&gt; column (not searchable, case preserved):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"toy_location"&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Bd+/TzEysF2CCQ==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iv&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;R2IUJJ+EmnDnZvQP&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;at&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;zqG5WAJql1zgctRCPpoBkQ==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"original_toy_location"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;5syLqDK6GCbBDw==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iv&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;KBGp4FrI7oL4/a3p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;at&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;JnH6hxLX35cAwroImk2XqQ==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;"toy_location"&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Bd+/TzEysF2CCQ==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iv&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;R2IUJJ+EmnDnZvQP&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;at&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;zqG5WAJql1zgctRCPpoBkQ==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"original_toy_location"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;0246w4+SSqqlJw==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;iv&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;1uEnjlCNot9sYNgR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;at&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;UhkhK6YlOTxJg75juqIMGA==&lt;/span&gt;&lt;span class="se"&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;/div&gt;



&lt;h2&gt;
  
  
  Other Cool things
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;encrypts&lt;/strong&gt; does even more than we have looked at so far. In the interest of not writing a book, I'm not going to go into much detail here, but would like to mention some of its other capabilities.&lt;/p&gt;

&lt;p&gt;We have only looked at encrypting simple strings, but &lt;strong&gt;encrypts&lt;/strong&gt; can encrypt rich text attributes too.&lt;/p&gt;

&lt;p&gt;It also provides support for &lt;a href="https://guides.rubyonrails.org/v7.0.0/active_record_encryption.html#support-for-previous-encryption-schemes" rel="noopener noreferrer"&gt;previous encryption schemes&lt;/a&gt;. This means that we can start out using non-deterministic encryption on a column and change to using deterministic encryption later on. I would definitly recommend reading the fine print before using this feature.&lt;/p&gt;

&lt;p&gt;We can &lt;a href="https://guides.rubyonrails.org/v7.0.0/active_record_encryption.html#rotating-keys" rel="noopener noreferrer"&gt;rotate our (non-deterministic) keys&lt;/a&gt;. This is pretty cool, just note that it is not currently supported for deterministic encryption.&lt;/p&gt;

&lt;p&gt;Related to the rotating keys, we can configure &lt;strong&gt;encrypts&lt;/strong&gt; to &lt;a href="https://guides.rubyonrails.org/v7.0.0/active_record_encryption.html#storing-key-references" rel="noopener noreferrer"&gt;store a reference&lt;/a&gt; to the key used to encrypt in the encrypted data itself.&lt;/p&gt;

&lt;p&gt;If using deterministic encryption, &lt;strong&gt;encrypts&lt;/strong&gt; supports using &lt;a href="https://guides.rubyonrails.org/v7.0.0/active_record_encryption.html#unique-constraints" rel="noopener noreferrer"&gt;&lt;code&gt;unique&lt;/code&gt; constraints&lt;/a&gt;. If we need to ensure uniqueness in any of our encrypted columns, there are some things to be aware of. Be sure to read up on it first.&lt;/p&gt;

&lt;p&gt;Encrypted columns are automatically filtered from Rails logs by default. &lt;strong&gt;encrypts&lt;/strong&gt; provides a way to disable this functionality.&lt;/p&gt;

&lt;p&gt;This might be a good place to mention that the implementation is modular, and allows quite a bit of customization. Many of &lt;strong&gt;encrypts&lt;/strong&gt; options can be set on a per-attribute basis, or at more global levels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;Even with all of its glory, &lt;strong&gt;encrypts&lt;/strong&gt; does have limitations. We'll take a look at a few that stood out to me. Given the variety of applicable use cases and the breadth of the functionality, I am sure other folks will have their own list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fuzzy searching&lt;/strong&gt; - The search functionality &lt;strong&gt;encrypts&lt;/strong&gt; provides requires an exact match on search text. This means doing a &lt;code&gt;LIKE&lt;/code&gt; query, for example, won't work. It also means that any queries done on encrypted columns will need to go through Rails and ActiveRecord vs being manually crafted in SQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich text search&lt;/strong&gt; - While it is rad that &lt;strong&gt;encrypts&lt;/strong&gt; can encrypt rich text, it can only do so non-deterministically right now. This means that we won't be able to search rich text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic searching does not support multiple keys&lt;/strong&gt; - Something good to be aware of going in - if using deterministic encryption/searching, we won't have the ability to use more than one key at a time. If we need to change keys, we'll likely need to do something fancy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rails console exposes data&lt;/strong&gt; - This may seem obvious, but if a malicious person gets access to our Rails console, they can load encrypted data into objects and view the plaintext all day long. In &lt;a href="https://world.hey.com/jorge/a-story-of-rails-encryption-ce104b67" rel="noopener noreferrer"&gt;Jorge's post&lt;/a&gt;, he mentioned that HEY is using a console extension that sits on top of the encryption feature that protects and audits console access. Unfortunately, that is a private gem (named console1984) and not available in Rails (at this time).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic encryption reduces security&lt;/strong&gt; - I don't think this is a fault of the implementation per se, but if we use deterministic encryption, then any two rows that have the same value for an encrypted attribute will have the same value stored in the database. While we can't necessarily reverse engineer how it was encrypted, if we know what one of the row's plaintext value is, then we know what the other row's plaintext value is as well. Non-deterministic encryption doesn't have this same weakness.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;TLDR: I am pretty excited and intrigued by this feature. I think it will be cool to see how people use it and how it evolves over time. My hunch is that some of the current limitations will go away (like not supporting multiple keys for deterministically encrypted attributes) as more people begin using it and digging through the code.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>security</category>
    </item>
    <item>
      <title>What's Cooking in Rails 7?</title>
      <dc:creator>Jason Dinsmore</dc:creator>
      <pubDate>Wed, 21 Apr 2021 16:16:09 +0000</pubDate>
      <link>https://forem.com/hint/what-s-cooking-in-rails-7-a42</link>
      <guid>https://forem.com/hint/what-s-cooking-in-rails-7-a42</guid>
      <description>&lt;p&gt;A new version of Rails always brings new and exciting features. This writeup takes a look at some of the things Rails 7 has in store for us.&lt;/p&gt;

&lt;p&gt;To find these examples, I dug through the &lt;a href="https://edgeguides.rubyonrails.org/7_0_release_notes.html" rel="noopener noreferrer"&gt;Changelogs&lt;/a&gt; and selected some of the features that have me most excited so far, primarily based on how I currently use Rails.&lt;/p&gt;

&lt;p&gt;I encourage you to do the same - you may find something awesome that impacts a feature you use regulary or provides functionality you've been lying awake at night fantasizing about.&lt;/p&gt;

&lt;p&gt;For each feature, I have included the PR that introduced it and the GitHub user who submitted the PR.&lt;/p&gt;

&lt;p&gt;Note that several of these features have followup PRs from people making essential contributions by updating documentation, adding changelog entries, tweaking implementations, etc. If you are one of those folks, please know that while you're not given explicit credit here, we are very grateful for all you do! 🙏&lt;/p&gt;

&lt;h1&gt;
  
  
  Railties
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The &lt;em&gt;main&lt;/em&gt; Branch
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/40254" rel="noopener noreferrer"&gt;PR 40254&lt;/a&gt; by &lt;a href="https://github.com/prateekkish" rel="noopener noreferrer"&gt;@prateekkish&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;When generating a new Rails project or plugin without the &lt;code&gt;--skip-git&lt;/code&gt; flag, the default branch of the created repository will now be &lt;code&gt;main&lt;/code&gt; instead of &lt;code&gt;master&lt;/code&gt;. If you've got a different branch set in your git config's &lt;code&gt;init.defaultBranch&lt;/code&gt;, that default will be used instead.&lt;/p&gt;

&lt;p&gt;Rails began initializing the Git repository &lt;a href="https://guides.rubyonrails.org/v5.1.0/5_1_release_notes.html#railties-notable-changes" rel="noopener noreferrer"&gt;in 5.1&lt;/a&gt; via &lt;a href="https://github.com/rails/rails/pull/27632" rel="noopener noreferrer"&gt;PR 27632&lt;/a&gt; by &lt;a href="https://github.com/dixpac" rel="noopener noreferrer"&gt;@dixpac&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;benchmark&lt;/em&gt; Anywhere
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/40734" rel="noopener noreferrer"&gt;PR 40734&lt;/a&gt; by &lt;a href="https://github.com/semaperepelitsa" rel="noopener noreferrer"&gt;@semaperepelitsa&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This PR adds the &lt;code&gt;ActiveSupport::Benchmarkable.benchmark&lt;/code&gt; method to the &lt;code&gt;Rails&lt;/code&gt; namespace, which allows you to easily benchmark a block of code anywhere without having to declare a &lt;code&gt;logger&lt;/code&gt; or &lt;code&gt;extend&lt;/code&gt;/&lt;code&gt;include&lt;/code&gt; the &lt;code&gt;ActiveSupport::Benchmarkable&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;To use the new functionality, you'd do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Print bar'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# code to benchmark&lt;/span&gt;
    &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'bar'&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;After your code executes, the message you provided and the time it took the code to execute in milliseconds will be passed along to the Rails logger, ie: &lt;code&gt;Print bar (0.6ms)&lt;/code&gt;. The &lt;code&gt;benchmark&lt;/code&gt; call will return the return value of the block, &lt;code&gt;100&lt;/code&gt; in this case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support Stats For Stylesheets and ERB Views
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/40597" rel="noopener noreferrer"&gt;PR 40597&lt;/a&gt; by &lt;a href="https://github.com/joelhawksley" rel="noopener noreferrer"&gt;@joelhawksley&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If you've run &lt;code&gt;rails stats&lt;/code&gt;, you know that it provides various statistics about your app's codebase. This new functionality adds &lt;code&gt;Views&lt;/code&gt; and &lt;code&gt;Stylesheets&lt;/code&gt; to the output, providing stats for files in &lt;code&gt;app/views&lt;/code&gt; and &lt;code&gt;app/assets/stylesheets&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that stylesheets need to have a &lt;code&gt;.css&lt;/code&gt; or &lt;code&gt;.scss&lt;/code&gt; extension, and view files need to have the &lt;code&gt;.erb&lt;/code&gt; extension in order to be processed.&lt;/p&gt;

&lt;h1&gt;
  
  
  ActionPack
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Redirect Back or To
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/40671" rel="noopener noreferrer"&gt;PR 40671&lt;/a&gt; by &lt;a href="https://github.com/dhh" rel="noopener noreferrer"&gt;@dhh&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Adds &lt;code&gt;redirect_back_or_to(fallback, **)&lt;/code&gt; as a nice shorthand for &lt;code&gt;redirect_back(fallback_location:, **)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This avoids having to specify the &lt;code&gt;fallback_location&lt;/code&gt; kwarg (keyword argument) to &lt;code&gt;redirect_back&lt;/code&gt;. &lt;code&gt;redirect_back_or_to&lt;/code&gt; will redirect the user back to whence they came if it can, and will redirect to the fallback location if it can't.&lt;/p&gt;

&lt;p&gt;The old syntax still works and hasn't been explicitly deprecated, but now &lt;code&gt;redirect_back&lt;/code&gt; calls &lt;code&gt;redirect_back_or_to&lt;/code&gt; &lt;a href="https://github.com/rails/rails/blob/07233426e693ea0b8292050fb926de4d52fbfeeb/actionpack/lib/action_controller/metal/redirecting.rb#L69-L71" rel="noopener noreferrer"&gt;under the hood&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unpermitted Parameter Context
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/41809" rel="noopener noreferrer"&gt;PR 41809&lt;/a&gt; by &lt;a href="https://github.com/bbuchalter" rel="noopener noreferrer"&gt;@bbuchalter&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This PR provides controller and request context when the parameters object is initialized so that when unpermitted attributes are logged, the controller, action, request, and filtered parameters will be logged as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  ActionView
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Lazy Load Images
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/38452" rel="noopener noreferrer"&gt;PR 38452&lt;/a&gt; by &lt;a href="https://github.com/jonathanhefner" rel="noopener noreferrer"&gt;@jonathanhefner&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Currently, apps can take advantage of the HTML standard's lazy loading of images by manually specifying the &lt;code&gt;loading&lt;/code&gt; attribute as &lt;code&gt;lazy&lt;/code&gt; in calls to &lt;code&gt;image_tag&lt;/code&gt;. This new configuration allows you to set the default to &lt;code&gt;"lazy"&lt;/code&gt; app-wide, so that the attribute will only need to be specified for places you want an image to be eager loaded.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Tag Attributes From Hash
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/40657" rel="noopener noreferrer"&gt;PR 40657&lt;/a&gt; by &lt;a href="https://github.com/seanpdoyle" rel="noopener noreferrer"&gt;@seanpdoyle&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This cool addition provides a slick way to tranform a hash of attributes into attributes on an ERB tag.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= tag.attributes({ id: 'percent-loaded', role: 'progressbar', aria: { valuenow: '75' }}) %&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Would render:&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;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"percent-loaded"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"progressbar"&lt;/span&gt; &lt;span class="na"&gt;aria-valuenow=&lt;/span&gt;&lt;span class="s"&gt;"75"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perhaps not the greatest example, but if the hash were generated programmatically and required logic, this could be pretty useful.&lt;/p&gt;

&lt;h1&gt;
  
  
  ActiveModel
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Add Numericality In Range Validation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/41022" rel="noopener noreferrer"&gt;PR 41022&lt;/a&gt; by &lt;a href="https://github.com/mpapis" rel="noopener noreferrer"&gt;@mpapis&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;When validating &lt;code&gt;numericality&lt;/code&gt; of a model attribute, this new syntax lets you specify a range for the numericality validator versus having to sandwich between the &lt;code&gt;greater_than_or_equal_to&lt;/code&gt; and &lt;code&gt;less_than_or_equal_to&lt;/code&gt; options.&lt;/p&gt;

&lt;p&gt;Example usage:&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;validates&lt;/span&gt; &lt;span class="ss"&gt;:latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;numericality: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;in: &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  ActiveRecord
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Invert a &lt;em&gt;where&lt;/em&gt; Clause
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/40249" rel="noopener noreferrer"&gt;PR 40249&lt;/a&gt; by &lt;a href="https://github.com/kddeisz" rel="noopener noreferrer"&gt;@kddeisz&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This addition provides a handy way to get at the inverse of a where clause by via &lt;code&gt;invert_where&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;good_students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Student&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;grade: &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# SELECT \"students\".* FROM \"students\" WHERE \"students\".\"grade\" BETWEEN 80 AND 100&lt;/span&gt;

&lt;span class="n"&gt;bad_students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;good_students&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invert_where&lt;/span&gt;
&lt;span class="c1"&gt;# SELECT \"students\".* FROM \"students\" WHERE NOT (\"students\".\"grade\" BETWEEN 80 AND 100)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exclude a Record From Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/41439" rel="noopener noreferrer"&gt;PR 41439&lt;/a&gt; by &lt;a href="https://github.com/GlenCrawford" rel="noopener noreferrer"&gt;@GlenCrawford&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Ever need a way to get all records matching some condition except a record you already have? &lt;code&gt;excluding&lt;/code&gt; may be your jam.&lt;/p&gt;

&lt;p&gt;Instead of:&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;other_users&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;primary_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can:&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;other_users&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;excluding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build or Create Association on Has One Through
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/40007" rel="noopener noreferrer"&gt;PR 40007&lt;/a&gt; by &lt;a href="https://github.com/perezperret" rel="noopener noreferrer"&gt;@perezperret&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Enables the &lt;code&gt;build_association&lt;/code&gt; and &lt;code&gt;create_association&lt;/code&gt; functionality for &lt;code&gt;has_one through:&lt;/code&gt; relations. Previously these were not available on &lt;code&gt;through&lt;/code&gt; associations.&lt;/p&gt;

&lt;p&gt;For example, if you had:&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;Dog&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:toys&lt;/span&gt;
  &lt;span class="n"&gt;has_one&lt;/span&gt; &lt;span class="ss"&gt;:toy_box&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Toy&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:dog&lt;/span&gt;
  &lt;span class="n"&gt;has_one&lt;/span&gt; &lt;span class="ss"&gt;:toy_box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;through: :dog&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToyBox&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:dog&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll now be able to:&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;toy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_toy_box&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;ToyBox:0x00007f572007e170 id: nil, dog_id: 3&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&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;toy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_toy_box&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;ToyBox:0x00005601f2ac09a0 id: 5, dog_id: 3&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Respect Column Type When Calculating Average
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/40351" rel="noopener noreferrer"&gt;PR 40351&lt;/a&gt; by &lt;a href="https://github.com/schmijos" rel="noopener noreferrer"&gt;@schmijos&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In prior versions of Rails, calling &lt;code&gt;ActiveRecord::Caculations#calculate&lt;/code&gt; with &lt;code&gt;:average&lt;/code&gt; would result in a &lt;code&gt;BigDecimal&lt;/code&gt;, even if a column was of a &lt;code&gt;Float&lt;/code&gt; or &lt;code&gt;Integer&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Coordinate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
&lt;span class="c1"&gt;# Float&lt;/span&gt;
&lt;span class="no"&gt;Coordinate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:average&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
&lt;span class="c1"&gt;# BigDecimal&lt;/span&gt;
&lt;span class="no"&gt;Coordinate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:average&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 0.13002356e3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the new behavior, it honors the type of the database column:&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;Coordinate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:average&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
&lt;span class="c1"&gt;# Float&lt;/span&gt;
&lt;span class="no"&gt;Coordinate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:average&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 130.02356&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the motivators for this change was that JSON conversions of &lt;code&gt;BigDecimal&lt;/code&gt; result in a string value, whereas &lt;code&gt;Float&lt;/code&gt; results in a numeric value.&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;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="ss"&gt;avg_longitude: &lt;/span&gt;&lt;span class="mf"&gt;130.02356&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_d&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# { "avg_longitude" =&amp;gt; "130.02356" }&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="ss"&gt;avg_longitude: &lt;/span&gt;&lt;span class="mf"&gt;130.02356&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# { "avg_longitude" =&amp;gt; 130.02356 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automatically Encrypt/Decrypt a Model Attribute
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/41659" rel="noopener noreferrer"&gt;PR 41659&lt;/a&gt; by &lt;a href="https://github.com/jorgemanrubia" rel="noopener noreferrer"&gt;@jorgemanrubia&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, we get to &lt;code&gt;encrypts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is deserving of a post all its own. The TLDR; version is that this feature adds a mechanism to auto-encrypt/decrypt an &lt;code&gt;ActiveRecord&lt;/code&gt; attribute by declaring &lt;code&gt;encrypts attr_name&lt;/code&gt; in the model. When a record is written to the database, it will automatically be encrypted, and when it is loaded from the database, the attribute will automatically be decrypted.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;encrypts&lt;/code&gt; also provides a workflow for changing the encryption scheme being used, without needing to re-encrypt all previously encrypted records under the new scheme.&lt;/p&gt;

&lt;p&gt;Data can be encrypted in a non-deterministic manner (encrypting the same text twice will result in different ciphertexts), or can be encrypted deterministically - which allows querying the encrypted attribute.&lt;/p&gt;

&lt;p&gt;Other niceties are the capability to encrypt &lt;code&gt;Action Text&lt;/code&gt; and to filter encrypted attributes from the application log files.&lt;/p&gt;

&lt;p&gt;It really does quite a lot and provides several ways to customize behavior, depending on your needs.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Else?
&lt;/h1&gt;

&lt;p&gt;I touched on the things I am most excited about so far, but there is other great work underway. &lt;a href="https://github.com/rails/rails/blob/main/actionpack/CHANGELOG.md" rel="noopener noreferrer"&gt;ActionPack&lt;/a&gt;, &lt;a href="https://github.com/rails/rails/blob/main/actionview/CHANGELOG.md" rel="noopener noreferrer"&gt;ActionView&lt;/a&gt;, &lt;a href="https://github.com/rails/rails/blob/main/activerecord/CHANGELOG.md" rel="noopener noreferrer"&gt;ActiveRecord&lt;/a&gt;, and &lt;a href="https://github.com/rails/rails/blob/main/activesupport/CHANGELOG.md" rel="noopener noreferrer"&gt;ActiveSupport&lt;/a&gt; in particular, all have rapidly growing lists of changes.&lt;/p&gt;

&lt;p&gt;Thanks again to all the contributors who are making this next release possible. It's shaping up nicely!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>5 Ways to Splat in Ruby</title>
      <dc:creator>Jason Dinsmore</dc:creator>
      <pubDate>Tue, 05 May 2020 15:14:28 +0000</pubDate>
      <link>https://forem.com/hint/5-ways-to-splat-in-ruby-4pgm</link>
      <guid>https://forem.com/hint/5-ways-to-splat-in-ruby-4pgm</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://hint.io/blog/5-ways-to-splat-in-ruby" rel="noopener noreferrer"&gt;Hint's blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In honor of it being 5/5 today (aka Cinco de Mayo), I thought we'd continue the pattern and take a quick look at 5 different ways to use Ruby's infamous splat (&lt;code&gt;*&lt;/code&gt;) operator.&lt;/p&gt;

&lt;p&gt;Let's hop right into it! &lt;/p&gt;

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

&lt;h2&gt;
  
  
  1. Interpolate Into an Array
&lt;/h2&gt;

&lt;p&gt;Splat can be used to expand the contents of one array into another:&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;middle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%i(bar baz)&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;middle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:qux&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# [:foo, :bar, :baz, :qux]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Capture an Argument List
&lt;/h2&gt;

&lt;p&gt;You can use splat to capture a list of arguments into an Array. Below, we're capturing the entire list of arguments being passed to &lt;code&gt;foo&lt;/code&gt; into an Array named &lt;code&gt;bar&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&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;bar&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;name&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;bar&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="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Array: [:a, :b, :c]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Capture First/Last or First/Rest Values
&lt;/h2&gt;

&lt;p&gt;Splat can also be leveraged to split up an array into parts. Kinda like pattern matching:&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;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&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;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;first&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;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="c1"&gt;# 1&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="c1"&gt;# 5&lt;/span&gt;

&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="c1"&gt;# 1&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="c1"&gt;# [2, 3, 4, 5]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Coerce a Value Into an Array
&lt;/h2&gt;

&lt;p&gt;If you want to ensure the thing you are dealing with is an Array, you can splat it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="c1"&gt;# [1]&lt;/span&gt;

&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&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;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="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="c1"&gt;# [1, 2, 3]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Convert an Array Into a Hash
&lt;/h2&gt;

&lt;p&gt;Lastly, splat can convert an Array into a Hash. Your Array needs to contain an even number of elements for this to work. If the Array were grouped into pairs, the first element in each pair will become a key in your Hash, and the second element of the pair will be the corresponding value.&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;arr&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:bar&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="ss"&gt;:baz&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="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# { foo: 1, bar: 2, baz: 3 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ruby</category>
      <category>beginners</category>
    </item>
    <item>
      <title>10 New Things in Active Record</title>
      <dc:creator>Jason Dinsmore</dc:creator>
      <pubDate>Wed, 20 Nov 2019 20:14:05 +0000</pubDate>
      <link>https://forem.com/hint/10-new-things-in-active-record-5112</link>
      <guid>https://forem.com/hint/10-new-things-in-active-record-5112</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://hint.io/blog/10-New-Things-in-Active-Record" rel="noopener noreferrer"&gt;Hint's blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this post, we will take a look at 10 new additions to Active Record in Rails 6. For each addition, I'll include a link to the PR the feature was introduced in, a link to the author's GitHub profile, and a brief description of what the feature provides.&lt;/p&gt;

&lt;p&gt;We've got a lot to cover, so let's get going!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;rails db:prepare&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/35768" rel="noopener noreferrer"&gt;PR 35678&lt;/a&gt; by &lt;a href="https://github.com/robertomiranda" rel="noopener noreferrer"&gt;@robertomiranda&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;When this rake task is run, if the database exists, it runs any pending migrations. If a database does not exist, it runs the &lt;code&gt;db:setup&lt;/code&gt; rake task.&lt;/p&gt;

&lt;p&gt;This feature was designed to be idempotent, allowing it to be run over and over again until it completes successfully.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;rake db:prepare&lt;/code&gt; task functions like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukmkeq8j2zabcejmpjhx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukmkeq8j2zabcejmpjhx.jpg" alt="rake db:prepare flowchart" width="758" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're not familiar with it, the &lt;code&gt;db:setup&lt;/code&gt; rake task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates the database&lt;/li&gt;
&lt;li&gt;Loads the &lt;code&gt;schema.rb&lt;/code&gt; or &lt;code&gt;structure.sql&lt;/code&gt; file (whichever your application is configured to use)&lt;/li&gt;
&lt;li&gt;Runs the &lt;code&gt;db:seed&lt;/code&gt; task, which executes the code in the &lt;code&gt;db/seeds.rb&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;rails db:seed:replant&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/34779" rel="noopener noreferrer"&gt;PR 34779&lt;/a&gt; by &lt;a href="https://github.com/bogdanvlviv" rel="noopener noreferrer"&gt;@bogdanvlviv&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This new &lt;code&gt;db:seed:replant&lt;/code&gt; rake task does two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs &lt;code&gt;truncate&lt;/code&gt; on all tables managed by ActiveRecord in the Rails environment the rake task is being run under. (Note that &lt;code&gt;truncate&lt;/code&gt; deletes all of the data in the table, but does not reset the table's auto-increment (ID) counter)&lt;/li&gt;
&lt;li&gt;Runs the &lt;code&gt;db:seed&lt;/code&gt; Rails rake task to populate seed data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Automatic Database Switching
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/35073" rel="noopener noreferrer"&gt;PR 35073&lt;/a&gt; by &lt;a href="https://github.com/eileencodes" rel="noopener noreferrer"&gt;@eileencodes&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Rails 6 provides a framework for auto-routing incoming requests to either the primary database connection, or a read replica.&lt;/p&gt;

&lt;p&gt;By default, this new functionality allows your app to automatically route read requests (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;) to a read-relica database if it has been at least 2 seconds since the last write request (any request that is &lt;em&gt;not&lt;/em&gt; a &lt;code&gt;GET&lt;/code&gt; or &lt;code&gt;HEAD&lt;/code&gt; request) was made.&lt;/p&gt;

&lt;p&gt;The logic that specifies when a read request should be routed to a replica is specified in a resolver class, &lt;code&gt;ActiveRecord::Middleware::DatabaseSelector::Resolver&lt;/code&gt; by default, which you would override if you wanted custom behavior.&lt;/p&gt;

&lt;p&gt;The middleware also provides a session class, &lt;code&gt;ActiveRecord::Middleware::DatabaseSelector::Resolver::Session&lt;/code&gt; that is tasked with keeping track of when the last write request was made. Like the resolver, this class can also be overridden.&lt;/p&gt;

&lt;p&gt;To enable the default behavior, you would add the following configuration options to one of your app's environment files - &lt;code&gt;config/environments/production.rb&lt;/code&gt; for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;database_selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;delay: &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;seconds&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;database_resolver&lt;/span&gt; &lt;span class="o"&gt;=&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;Middleware&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DatabaseSelector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Resolver&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;database_operations&lt;/span&gt; &lt;span class="o"&gt;=&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;Middleware&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DatabaseSelector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Resolver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you decide to override the default functionality, you can use these configuration options to specify the delay you'd like to use, the name of your custom resolver class, and the name of your custom session class, both of which should be descendants of the default classes.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Negative Scopes for Enums
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/35381" rel="noopener noreferrer"&gt;PR 35381&lt;/a&gt; by &lt;a href="https://github.com/dhh" rel="noopener noreferrer"&gt;@dhh&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;While &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/Enum.html" rel="noopener noreferrer"&gt;&lt;code&gt;enum&lt;/code&gt;&lt;/a&gt; has traditionally provided scopes to find items by their enum value, it has not provided scopes to find items not matching a specific enum value.&lt;/p&gt;

&lt;p&gt;For example, given a &lt;code&gt;Post&lt;/code&gt; model for a blog, with an enum on the &lt;code&gt;status&lt;/code&gt; field:&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;enum&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="sx"&gt;%i(draft published archived)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following scopes have been provided automatically for some time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &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="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:archived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;2&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;Now, the following negative scopes are also available:&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;scope&lt;/span&gt; &lt;span class="ss"&gt;:not_draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &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="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:not_published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:not_archived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;2&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;This makes it easy, for example, to find unpublished posts:&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;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not_published&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. &lt;code&gt;#extract_associated&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/35784" rel="noopener noreferrer"&gt;PR 35784&lt;/a&gt; by &lt;a href="https://github.com/dhh" rel="noopener noreferrer"&gt;@dhh&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The new &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/QueryMethods.html#method-i-extract_associated" rel="noopener noreferrer"&gt;&lt;code&gt;#extract_associated&lt;/code&gt;&lt;/a&gt; method is literally just a shorthand for &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/QueryMethods.html#method-i-preload" rel="noopener noreferrer"&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/a&gt; plus &lt;a href="https://rubydocs.org/d/ruby-2-6-5/classes/Enumerable.html#method-i-collect" rel="noopener noreferrer"&gt;&lt;code&gt;map/collect&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's the source code of the method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_associated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;association&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;association&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;association&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;Be aware that &lt;code&gt;preload&lt;/code&gt; does not allow you to specify conditions on the association being "preloaded". You'd want to use a different eager-loading mechanism for that, likely &lt;code&gt;includes&lt;/code&gt;, &lt;code&gt;eager_load&lt;/code&gt; or &lt;code&gt;joins&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Usage of &lt;code&gt;#extract_associated&lt;/code&gt; might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;commented_posts&lt;/span&gt; &lt;span class="o"&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;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_associated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. &lt;code&gt;#annotate&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/35617" rel="noopener noreferrer"&gt;PR 35617&lt;/a&gt; by &lt;a href="https://github.com/mattyoho" rel="noopener noreferrer"&gt;@mattyoho&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is a nifty addition that could be used to add useful information to your application's log files. The &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/QueryMethods.html#method-i-annotate" rel="noopener noreferrer"&gt;&lt;code&gt;#annotate&lt;/code&gt;&lt;/a&gt; method provides a mechanism to embed comments into the SQL generated by ActiveRecord queries. As an added benefit, the comments it generates could be completely dynamic.&lt;/p&gt;

&lt;p&gt;Inserting &lt;code&gt;annotate&lt;/code&gt; into your query chain like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'there can be only one!'&lt;/span&gt;&lt;span class="p"&gt;)&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;highlander: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Would generate the following SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"highlander"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="cm"&gt;/* there can be only one! */&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nv"&gt;"highlander"&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="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;"LIMIT"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. &lt;code&gt;#touch_all&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/31513" rel="noopener noreferrer"&gt;PR 31513&lt;/a&gt; by &lt;a href="https://github.com/fatkodima" rel="noopener noreferrer"&gt;@fatkodima&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Another &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/Relation.html" rel="noopener noreferrer"&gt;&lt;code&gt;ActiveRecord::Relation&lt;/code&gt;&lt;/a&gt; method, &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/Relation.html#method-i-touch_all" rel="noopener noreferrer"&gt;&lt;code&gt;#touch_all&lt;/code&gt;&lt;/a&gt; touches all records in the current scope, updating their timestamps.&lt;/p&gt;

&lt;p&gt;You can pass an array of columns to touch, and optionally provide a time value to use. &lt;code&gt;touch_all&lt;/code&gt; defaults to the current time in whatever timezone the app's config has set for &lt;code&gt;config.active_record.default_timezone&lt;/code&gt; (the setting defaults to UTC).&lt;/p&gt;

&lt;p&gt;For example, to update the &lt;code&gt;updated_at&lt;/code&gt; field of all comments associated with a given blog post, &lt;code&gt;@post&lt;/code&gt;, you could:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;touch_all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To update a given field on the comments, say &lt;code&gt;:reviewed_at&lt;/code&gt;, you would provide the column name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;touch_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:reviewed_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, to specify a time value, you would:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;touch_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:reviewed_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;time: &lt;/span&gt;&lt;span class="n"&gt;the_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. &lt;code&gt;#destroy_by&lt;/code&gt; and &lt;code&gt;#delete_by&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/35316" rel="noopener noreferrer"&gt;PR 35316&lt;/a&gt; by &lt;a href="https://github.com/abhaynikam" rel="noopener noreferrer"&gt;@abhaynikam&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/Relation.html#method-i-destroy_by" rel="noopener noreferrer"&gt;&lt;code&gt;destroy_by&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/Relation.html#method-i-delete_by" rel="noopener noreferrer"&gt;&lt;code&gt;delete_by&lt;/code&gt;&lt;/a&gt; methods are intended to provide symmetry (&lt;a href="https://github.com/rails/rails/issues/35304#issuecomment-464489289" rel="noopener noreferrer"&gt;"in spirit"&lt;/a&gt;) with  &lt;code&gt;ActiveRecord&lt;/code&gt;'s &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/FinderMethods.html#method-i-find_by" rel="noopener noreferrer"&gt;&lt;code&gt;find_by&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://rubydocs.org/d/rails-6-0-0/classes/ActiveRecord/Relation.html#method-i-find_or_create_by" rel="noopener noreferrer"&gt;&lt;code&gt;find_or_create_by&lt;/code&gt;&lt;/a&gt; methods.&lt;/p&gt;

&lt;p&gt;I believe there is an important distinction you should be aware of. &lt;code&gt;find_by&lt;/code&gt; returns one record, or &lt;code&gt;nil&lt;/code&gt;, whereas &lt;code&gt;destroy_by&lt;/code&gt; and &lt;code&gt;delete_by&lt;/code&gt; will match on entire collections of records.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;find_by&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&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;admin: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generates the following SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;  &lt;span class="nv"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"admin"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nv"&gt;"admin"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whereas, using &lt;code&gt;delete_by&lt;/code&gt; with the same parameters:&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;admin: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Results in the following SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"admin"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;  &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nv"&gt;"admin"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Definitely something to keep in mind when using these methods!&lt;/p&gt;

&lt;p&gt;Also, it's worth noting that there are no bang ( &lt;code&gt;!&lt;/code&gt;) versions of the &lt;code&gt;delete_by/destroy_by&lt;/code&gt; methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Endless Ranges in &lt;code&gt;#where&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/34906" rel="noopener noreferrer"&gt;PR 34906&lt;/a&gt; by &lt;a href="https://github.com/gregnavis" rel="noopener noreferrer"&gt;@gregnavis&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Ruby 2.6 introduced infinite ranges. This new feature lets you use them in Rails' &lt;code&gt;#where&lt;/code&gt; clauses.&lt;/p&gt;

&lt;p&gt;For example, when trying to find a Post with more than 10 comments (contrived example, I know).&lt;/p&gt;

&lt;p&gt;Before you would have to use SQL like:&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;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'num_comments &amp;gt; ?'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can use syntax that is more idiomatic Ruby:&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;num_comments: &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  10. Implicit Ordering
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/34480" rel="noopener noreferrer"&gt;PR 34480&lt;/a&gt; by &lt;a href="https://github.com/tekin" rel="noopener noreferrer"&gt;@tekin&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This feature makes implicit ordering configurable for a database table. Rails  implicitly orders results by the table's primary key, which can give surprising results when the primary key is something that isn't an auto-incrementing  integer (like a UUID).&lt;/p&gt;

&lt;p&gt;Setting a table's implicit order column allows you to specify a default order,  without using a default scope, meaning that you don't need to use &lt;code&gt;reorder&lt;/code&gt; in  your code to change the order downstream, you can use &lt;code&gt;order&lt;/code&gt; (assuming you have not already specified explicit ordering earlier in the query chain).&lt;/p&gt;

&lt;p&gt;For example, if you declare an implicit ordering on your &lt;code&gt;Post&lt;/code&gt; table:&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;Post&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;implicit_order_column&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'title'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be aware, that if you declare implicit ordering on a column that does not ensure unique values, your results may not be what you expect.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Have you called your method object lately?</title>
      <dc:creator>Jason Dinsmore</dc:creator>
      <pubDate>Mon, 10 Jun 2019 20:20:12 +0000</pubDate>
      <link>https://forem.com/hint/have-you-called-your-method-object-lately-1ebg</link>
      <guid>https://forem.com/hint/have-you-called-your-method-object-lately-1ebg</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://hint.io/blog/procto-method-object" rel="noopener noreferrer"&gt;Hint's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Allow me to introduce you to another library that has proven itself useful when writing Ruby: the &lt;a href="https://github.com/snusnu/procto" rel="noopener noreferrer"&gt;Procto&lt;/a&gt; gem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Procto Overview
&lt;/h2&gt;

&lt;p&gt;Procto is a gem that lets you turn a Ruby class into a method object. This helps clean up code that instantiates a class to perform a single task, calculation, etc.&lt;/p&gt;

&lt;p&gt;To better understand what it's trying to facilitate, perhaps a review of method objects in Ruby would help.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ruby Method Objects
&lt;/h3&gt;

&lt;p&gt;A method object is essentially an object that has a bound context and provides a single method.&lt;/p&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;meth_obj&lt;/span&gt; &lt;span class="o"&gt;=&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="ss"&gt;:bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:baz&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="c1"&gt;#size()&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;meth_obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Method&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Object&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;meth_obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, &lt;code&gt;meth_obj&lt;/code&gt; is a method object that is bound to the array containing &lt;code&gt;[:foo, :bar, :baz]&lt;/code&gt;. In this case, when &lt;code&gt;call&lt;/code&gt; is executed on the method object, the &lt;code&gt;size&lt;/code&gt; message is passed to the context the method object is bound to (&lt;code&gt;[:foo, :bar, :baz]&lt;/code&gt;) and the result is &lt;code&gt;3&lt;/code&gt;. Interestingly (though unrelated), Ruby allows you to unbind and rebind in cases like this.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;meth_obj&lt;/span&gt; &lt;span class="o"&gt;=&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="ss"&gt;:bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:baz&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;meth_obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;meth_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meth_obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unbind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:waldo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:fred&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;meth_obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use
&lt;/h2&gt;

&lt;p&gt;To use Procto, you'll need to ensure you've got the gem installed and that it has been loaded by your program or application. If the gem is available, but hasn't been loaded, you can just use &lt;code&gt;require 'procto'&lt;/code&gt; at the top of your Ruby class.&lt;/p&gt;

&lt;p&gt;Once the gem is loaded and available for you to use, you'll just need to: &lt;code&gt;include Procto.call&lt;/code&gt; right after your class definition, ala:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'procto'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Procto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bar&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;call&lt;/span&gt;
    &lt;span class="c1"&gt;# do stuff&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;When invoking your class, you will supply &lt;code&gt;call&lt;/code&gt; with the parameters you would normally pass to your class's initializer.&lt;/p&gt;

&lt;p&gt;E.g.:&lt;/p&gt;

&lt;p&gt;Instead of &lt;code&gt;Foo.new(bar).call&lt;/code&gt;, you would do &lt;code&gt;Foo.call(bar)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blocks
&lt;/h2&gt;

&lt;p&gt;Although Procto is &lt;em&gt;very&lt;/em&gt; useful, it's not the answer for everything. One thing you might find yourself wanting to do is pass a block to your &lt;code&gt;call&lt;/code&gt; method. Unfortunately, Procto does not support this.&lt;/p&gt;

&lt;p&gt;Consider the following 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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FooWithBlock&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Procto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bar&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;call&lt;/span&gt;
    &lt;span class="c1"&gt;# do something&lt;/span&gt;

    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:bar&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might expect to be able to use the above code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;FooWithBlock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I'm in a block!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sadly, your block will never be executed.&lt;/p&gt;

&lt;p&gt;If you &lt;em&gt;need&lt;/em&gt; to do the above, you'll have to do so without using Procto, so you'd need to remove &lt;code&gt;include Procto.call&lt;/code&gt; from your class, and invoke your class as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;FooWithBlock&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="s1"&gt;'Hello'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I'm in a block!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;If you haven't used it before, I also highly recommend the &lt;a href="https://github.com/mbj/concord" rel="noopener noreferrer"&gt;Concord gem&lt;/a&gt;. When used in conjunction with Procto, it really creates a nice interface for invoking method objects.&lt;/p&gt;

&lt;p&gt;Concord abstracts away having to define the initializer and attribute accessors for your class and its attributes. It also happens to play very nicely with Procto.&lt;/p&gt;

&lt;p&gt;Using both, you can turn:&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;Foo&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;
    &lt;span class="vi"&gt;@baz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;baz&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;call&lt;/span&gt;
    &lt;span class="c1"&gt;# do stuff&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:baz&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;For&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;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Into:&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;Foo&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Concord&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;:bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Procto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;
    &lt;span class="c1"&gt;# do stuff&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I should mention that Concord limits your initializer to three parameters. You can work around this by passing a more complex data structure as one (or more) of the parameters, and extracting the parameters from that object, or by using the [Anima gem (&lt;a href="https://github.com/mbj/anima" rel="noopener noreferrer"&gt;https://github.com/mbj/anima&lt;/a&gt;) instead, which does not cap the number of parameters you can pass, but takes an initialization hash (keyword arguments) instead of parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Procto is a fantastic tool for cleaning up your class's interface. When used in conjunction with the Concord gem, you'll find yourself writing significantly less boilerplate code to get your classes up and running. Your code will also end up generating a smaller abstract syntax tree (AST), which is generally a very good thing.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Gems, Some of My Favorites - PaperTrail + PaperTrailScrapbook</title>
      <dc:creator>Jason Dinsmore</dc:creator>
      <pubDate>Mon, 06 May 2019 22:16:12 +0000</pubDate>
      <link>https://forem.com/hint/gems-some-of-my-favorites-papertrail-papertrailscrapbook-5d0p</link>
      <guid>https://forem.com/hint/gems-some-of-my-favorites-papertrail-papertrailscrapbook-5d0p</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://hint.io/blog/paper-trail-scrapbook" rel="noopener noreferrer"&gt;Hint's blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the first in a series of blog posts taking an introductory look at various Ruby gems that we find useful and regularly use or have used in our projects over the years.&lt;/p&gt;

&lt;p&gt;Today I am going to cover two related gems: PaperTrail, and PaperTrailScrapbook. These gems facilitate keeping an auditable trail of changes made to rows of a tracked table in your Rails application's database.&lt;/p&gt;




&lt;h2&gt;
  
  
  PaperTrail
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/airblade/paper_trail" rel="noopener noreferrer"&gt;PaperTrail gem&lt;/a&gt; allows you to track changes made to your Rails models over time. Any time a tracked record in your database is changed, it creates a corresponding entry in a &lt;code&gt;PaperTrail::Version&lt;/code&gt; table. It accomplishes this by tying into the ActiveRecord callback chain and storing a new version when the record is created, updated, or destroyed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;To start using PaperTrail, you'll need to follow the steps outlined in the README on the project's GitHub page, which essentially guide you through the process of adding the gem to your gemfile, bundling, and then generating and running a migration that creates the &lt;code&gt;PaperTrail::Version&lt;/code&gt; table. This table will be leveraged to store an entry for each "version" of your model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use
&lt;/h3&gt;

&lt;p&gt;Once you've got the gem set up, you can start using it. To start tracking versions for a model, you'll just need to add &lt;code&gt;has_paper_trail&lt;/code&gt; to your model file (usually near the top, inside the class):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyModel&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_paper_trail&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that your versions are being tracked, you will be able to go into your Rails console and view the various versions of your models using PaperTrail's API.&lt;/p&gt;

&lt;p&gt;For a specific object, &lt;code&gt;obj&lt;/code&gt;, you can access a collection of its versions using: &lt;code&gt;obj.versions&lt;/code&gt;. This will show you a list of versions that each have this structure:&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;#&amp;lt;PaperTrail::Version:0x007fe111b03f70&amp;gt; {&lt;/span&gt;
                    &lt;span class="ss"&gt;:id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;314159&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="ss"&gt;:item_type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Invoice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="ss"&gt;:item_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;555&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;:event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="ss"&gt;:whodunnit&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;:object&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;id: 5455&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;actor_id: 2183&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;sale_id: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;amount: !ruby/object:BigDecimal 27:0.1276E3&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;authorization_id: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;paid_on: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;created_at: 2018-02-15 17:14:16.718224000 Z&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;updated_at: 2018-02-16 17:14:16.718224000 Z&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;status: active&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;:created_at&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="no"&gt;Feb&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:object_changes&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;sale_id:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; - 123&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;paid_on:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- 2018-02-20 20:39:19.956256107 Z&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;authorization_id:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- 412&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;updated_at:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- 2018-02-19 20:39:19.956256107 Z&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- 2018-02-19 19:59:49.270910041 Z&lt;/span&gt;&lt;span class="se"&gt;\n&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;/div&gt;



&lt;p&gt;The fields of a version break down like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The primary key for this &lt;code&gt;PaperTrail::Version&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;item_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type of record being tracked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;item_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The primary key of the record being tracked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;event&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The type of change that occurred&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;whodunnit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;id&lt;/code&gt; of the person making the change (you can set the class of the &lt;code&gt;whodunnit&lt;/code&gt; in your PaperTrail configuration, ie. &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Actor&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;object&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A serialized version of the object being updated in its pre-update state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;created_at&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When this version was created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;object_changes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A serialized list of changes that were applied to the object as shown in the &lt;code&gt;object&lt;/code&gt; field of the version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PaperTrail also provides the ability to revert an object to a previous state. To restore an object to a previous version, you can  call &lt;code&gt;reify&lt;/code&gt; (which means to make something abstract more concrete or real) on the version.&lt;/p&gt;

&lt;p&gt;For example, using the version from our last example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;last_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;versions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;  &lt;span class="c1"&gt;# gets PaperTrail::Version shown above&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;last_version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reify&lt;/span&gt;

&lt;span class="no"&gt;Invoice&lt;/span&gt; &lt;span class="mi"&gt;314159&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="ss"&gt;:id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;314159&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;:actor_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2183&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="ss"&gt;:sale_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;:amount&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;27.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:authorization_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="ss"&gt;:paid_on&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:created_at&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="no"&gt;Feb&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:updated_at&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="no"&gt;Feb&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;:status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;reify&lt;/code&gt; will give you an instance of the object that corresponds to the version before the &lt;code&gt;object_changes&lt;/code&gt; have been applied, which matches what is in the &lt;code&gt;object&lt;/code&gt; field of the version. &lt;/p&gt;

&lt;p&gt;Calling &lt;code&gt;reify&lt;/code&gt; does not affect the object's current state in the database unless you call &lt;code&gt;save&lt;/code&gt; on the reified object. Doing so will persist the reified version to the database, creating a new version for the object in the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;p&gt;PaperTrail falters a bit when tracking changes to a model's associations. For example, consider an &lt;code&gt;Invoice&lt;/code&gt; class that  &lt;code&gt;has_many :line_items&lt;/code&gt; where &lt;code&gt;LineItem&lt;/code&gt; &lt;code&gt;belongs_to :invoice&lt;/code&gt;. If you had an &lt;code&gt;invoice&lt;/code&gt; that has several &lt;code&gt;line_items&lt;/code&gt; associated with it, you could remove the line items by calling some code like:&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;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you did so and took a look at the tracked versions of one of the &lt;code&gt;line_items&lt;/code&gt; that had been associated with the &lt;code&gt;invoice&lt;/code&gt;, you'd see that the removal of the &lt;code&gt;invoice_id&lt;/code&gt; from the &lt;code&gt;line_item&lt;/code&gt; was not captured in a version.&lt;/p&gt;

&lt;p&gt;PaperTrail has some information in the project's &lt;a href="https://github.com/airblade/paper_trail#4b1-known-issues" rel="noopener noreferrer"&gt;README&lt;/a&gt; mentioning this and suggests some "experimental" workarounds, but your mileage may vary.&lt;/p&gt;

&lt;p&gt;In my opinion, PaperTrail's API is a bit cumbersome and does not provide a concise summary of the changes that have been made to a model over time. The information is definitely there, but extracting it into a meaningful, complete history can be painful.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://github.com/tjchambers/paper_trail_scrapbook" rel="noopener noreferrer"&gt;PaperTrailScrapbook&lt;/a&gt; comes in.&lt;/p&gt;




&lt;h2&gt;
  
  
  PaperTrailScrapbook
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/tjchambers/paper_trail_scrapbook" rel="noopener noreferrer"&gt;PaperTrailScrapbook&lt;/a&gt; was conceptualized by &lt;a href="https://github.com/tjchambers" rel="noopener noreferrer"&gt;Tim Chambers&lt;/a&gt; in 2017. Its intent is to provide a human-readable summary of changes made to a model tracked by PaperTrail. It accomplishes this by providing a simple interface to obtain either a complete history of an object, or a list of changes a specific person has made. &lt;em&gt;Disclaimer: I am a contributor on this project :)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;To set up PaperTrailScrapbook, you'll just need to add &lt;code&gt;gem 'paper_trail_scrapbook'&lt;/code&gt; to your Gemfile and bundle your application. The other thing you'll need to do is specify the class of your app's &lt;code&gt;whodunnit&lt;/code&gt;. Most often, this is going to be &lt;code&gt;User&lt;/code&gt;. I'd recommend adding an initializer (e.g. &lt;code&gt;config/initializers/paper_trail_scrapbook.rb&lt;/code&gt;) for PaperTrailScrapbook.&lt;/p&gt;

&lt;p&gt;If your whodunnit class is &lt;code&gt;User&lt;/code&gt;, your configuration would look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;PaperTrailScrapbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&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;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;whodunnit_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&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;Once you've got the configuration in place, restart your app server and you should be good to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use
&lt;/h3&gt;

&lt;p&gt;PaperTrailScrapbook currently has two modes of use, &lt;code&gt;LifeHistory&lt;/code&gt; and &lt;code&gt;UserJournal&lt;/code&gt;. Each mode provides a &lt;code&gt;story&lt;/code&gt; for a given object or &lt;code&gt;whodunnit&lt;/code&gt;. &lt;/p&gt;

&lt;h4&gt;
  
  
  LifeHistory
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;LifeHistory&lt;/code&gt; module takes a model instance as its argument, and generates a list of changes made to that object over time. &lt;/p&gt;

&lt;p&gt;For example, the following query for history:&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;widget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;PaperTrailScrapbook&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LifeHistory&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;widget&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;story&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Could output the following story:&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;On&lt;/span&gt; &lt;span class="no"&gt;Friday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="no"&gt;AM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Fred&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;fred&lt;/span&gt;&lt;span class="vi"&gt;@flinstone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;com&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="ss"&gt;info:
 &lt;/span&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Tricky&lt;/span&gt; &lt;span class="no"&gt;Spinner&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;description:
 &lt;/span&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;notes: &lt;/span&gt;&lt;span class="no"&gt;Recommended&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ages&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="ss"&gt;by: &lt;/span&gt;&lt;span class="no"&gt;Fred&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;pattern: &lt;/span&gt;&lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="no"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;386&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;12.34&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;inactive&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;

&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Barney&lt;/span&gt; &lt;span class="no"&gt;Rubble&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;barney&lt;/span&gt;&lt;span class="vi"&gt;@rubble&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;com&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="ss"&gt;info:
 &lt;/span&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Recommended&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ages&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Recommended&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ages&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;spinning&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;keep&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="n"&gt;fingers&lt;/span&gt; &lt;span class="n"&gt;busy!&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;12.34&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;12.99&lt;/span&gt;

&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Thursday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Wilma&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;wilma&lt;/span&gt;&lt;span class="vi"&gt;@flinstone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;com&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="ss"&gt;info:
 &lt;/span&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;12.99&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;11.99&lt;/span&gt;

&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;03&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="no"&gt;AM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Betty&lt;/span&gt; &lt;span class="no"&gt;Rubble&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;betty&lt;/span&gt;&lt;span class="vi"&gt;@rubble&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;com&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="ss"&gt;info:
 &lt;/span&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Tricky&lt;/span&gt; &lt;span class="no"&gt;Spinner&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Trick&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;11.99&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;9.99&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;inactive&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;

&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;05&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&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;24&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Fred&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;fred&lt;/span&gt;&lt;span class="vi"&gt;@flinstone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;com&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="ss"&gt;info:
 &lt;/span&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Trick&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Brontosaurus&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;11.99&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;9.99&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;inactive&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  UserJournal
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;UserJournal&lt;/code&gt; module takes a &lt;code&gt;whodunnit&lt;/code&gt; instance as its primary argument and generates a summary of changes made by that person over time. It also provides options to scope the summary to a specific class and/or date range. This can be really useful as your app ages since the number of changes made by a person could grow quite large over time.&lt;/p&gt;

&lt;p&gt;For example, searching by a specific &lt;code&gt;whodunnit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;fred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&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="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;PaperTrailScrapbook&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserJournal&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;fred&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;story&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Could output the following history:&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;Between&lt;/span&gt; &lt;span class="no"&gt;Friday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="no"&gt;AM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;05&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&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;24&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Fred&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt; &lt;span class="n"&gt;made&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="ss"&gt;changes:

&lt;/span&gt;&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Friday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="no"&gt;AM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Tricky&lt;/span&gt; &lt;span class="no"&gt;Spinner&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;description:
 &lt;/span&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;notes: &lt;/span&gt;&lt;span class="no"&gt;Recommended&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ages&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="ss"&gt;by: &lt;/span&gt;&lt;span class="no"&gt;Fred&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;pattern: &lt;/span&gt;&lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="no"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;386&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;12.34&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;inactive&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;

&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&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;31&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="no"&gt;Wobble&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;538&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;on_hand: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;

&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Thursday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="no"&gt;User&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="err"&gt;•&lt;/span&gt; &lt;span class="n"&gt;favorite&lt;/span&gt; &lt;span class="ss"&gt;saying: &lt;/span&gt;&lt;span class="no"&gt;Yabba&lt;/span&gt; &lt;span class="n"&gt;dabba&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yabba&lt;/span&gt; &lt;span class="n"&gt;dabba&lt;/span&gt; &lt;span class="n"&gt;doo!&lt;/span&gt;

&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;05&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&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;24&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Trick&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Brontosaurus&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;11.99&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;9.99&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;inactive&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also scope changes to a specific class to hone in on the changes you're after. Consider the following where we look for changes Fred has made to instances of the &lt;code&gt;Widget&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;fred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&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="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;PaperTrailScrapbook&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserJournal&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;fred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;klass: &lt;/span&gt;&lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;story&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which would output the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Between&lt;/span&gt; &lt;span class="no"&gt;Friday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="no"&gt;AM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;05&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&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;24&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Fred&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt; &lt;span class="n"&gt;made&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="ss"&gt;changes:

&lt;/span&gt;&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Friday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="no"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2017&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="no"&gt;AM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Tricky&lt;/span&gt; &lt;span class="no"&gt;Spinner&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;description:
 &lt;/span&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;notes: &lt;/span&gt;&lt;span class="no"&gt;Recommended&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ages&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="ss"&gt;by: &lt;/span&gt;&lt;span class="no"&gt;Fred&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;pattern: &lt;/span&gt;&lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="no"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;386&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;12.34&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;inactive&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;

&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;05&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&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;24&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Trick&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Brontosaurus&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;11.99&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;9.99&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;inactive&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;History can also be constrained to a specific period of time, using the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; parameters:&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;fred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&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="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;PaperTrailScrapbook&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserJournal&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;fred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;klass: &lt;/span&gt;&lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                                &lt;span class="ss"&gt;start: &lt;/span&gt;&lt;span class="no"&gt;Date&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;'2018-01-01'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                                                &lt;span class="ss"&gt;end:   &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;story&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which would ouput this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Between&lt;/span&gt; &lt;span class="no"&gt;Monday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt; &lt;span class="no"&gt;AM&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="no"&gt;Friday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt; &lt;span class="no"&gt;Feb&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt; &lt;span class="no"&gt;AM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Fred&lt;/span&gt; &lt;span class="no"&gt;Flintstone&lt;/span&gt; &lt;span class="n"&gt;made&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="ss"&gt;changes:

&lt;/span&gt;&lt;span class="no"&gt;On&lt;/span&gt; &lt;span class="no"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;05&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt; &lt;span class="n"&gt;at&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;24&lt;/span&gt; &lt;span class="no"&gt;PM&lt;/span&gt; &lt;span class="no"&gt;PST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Trick&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Spinning&lt;/span&gt; &lt;span class="no"&gt;Brontosaurus&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mf"&gt;11.99&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;9.99&lt;/span&gt;
 &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;inactive&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Customization
&lt;/h3&gt;

&lt;p&gt;If your system allows for instances of your whodunnit class to be deleted, you may run across a situation where the whodunnit for a version no longer exists in your database. You can customize the behavior of PaperTrailScrapbook for that scenario by providing a proc for invalid whodunnits in your configuration file, as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalid_whodunnit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"** DELETED: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;id&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;/div&gt;



&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;p&gt;This project is still in its infancy, so you may hit occasional quirks or behavior that isn't ideal for your application. If you do, I'd consider submitting an issue or even a pull request. Any contributions are definitely welcomed and appreciated.&lt;/p&gt;

&lt;p&gt;That said, there are a few known behaviors that I'd like to mention.&lt;/p&gt;

&lt;p&gt;Querying for changes made by a person can take a &lt;em&gt;very&lt;/em&gt; long time if your project has several large tables and the person has been active. If you can, scoping by class or time period will improve performance drastically.&lt;/p&gt;

&lt;p&gt;Another area that could use improvement is in the presentation of serialized fields. &lt;/p&gt;

&lt;p&gt;Imagine you have a &lt;code&gt;Container&lt;/code&gt; class with a serialized Hash field, called &lt;code&gt;capacity&lt;/code&gt;. If you were to populate the &lt;code&gt;capacity&lt;/code&gt; field with the following hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"weight"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"min"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"25"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"max"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"50"&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;and ran history on your object instance, you would see something like this for the change on &lt;code&gt;capacity&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="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;capacity: &lt;/span&gt;&lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="ss"&gt;:ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HashWithIndifferentAccess&lt;/span&gt;
&lt;span class="ss"&gt;weight: &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="ss"&gt;:ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HashWithIndifferentAccess&lt;/span&gt;
&lt;span class="ss"&gt;min: &lt;/span&gt;&lt;span class="s1"&gt;'25'&lt;/span&gt;
&lt;span class="ss"&gt;max: &lt;/span&gt;&lt;span class="s1"&gt;'50'&lt;/span&gt;
&lt;span class="n"&gt;added&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then if you updated the value to include &lt;code&gt;height&lt;/code&gt; limits so the hash was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"weight"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"min"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"max"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"40"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;"height"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"min"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"55"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"max"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"65"&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;and re-ran history, you'd see this for the change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="ss"&gt;capacity: &lt;/span&gt;&lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ivars&lt;/span&gt;&lt;span class="ss"&gt;:ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Parameters&lt;/span&gt;
&lt;span class="ss"&gt;elements:
weight: &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ivars&lt;/span&gt;&lt;span class="ss"&gt;:ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Parameters&lt;/span&gt;
&lt;span class="ss"&gt;elements:
min: &lt;/span&gt;&lt;span class="s1"&gt;'25'&lt;/span&gt;
&lt;span class="ss"&gt;max: &lt;/span&gt;&lt;span class="s1"&gt;'50'&lt;/span&gt;
&lt;span class="ss"&gt;ivars:
:@permitted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="ss"&gt;ivars:
:@permitted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="ss"&gt;:ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HashWithIndifferentAccess&lt;/span&gt;
&lt;span class="ss"&gt;weight: &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="ss"&gt;:ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HashWithIndifferentAccess&lt;/span&gt;
&lt;span class="ss"&gt;min: &lt;/span&gt;&lt;span class="s1"&gt;'20'&lt;/span&gt;
&lt;span class="ss"&gt;max: &lt;/span&gt;&lt;span class="s1"&gt;'40'&lt;/span&gt;
&lt;span class="ss"&gt;height: &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="ss"&gt;:ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HashWithIndifferentAccess&lt;/span&gt;
&lt;span class="ss"&gt;min: &lt;/span&gt;&lt;span class="s1"&gt;'55'&lt;/span&gt;
&lt;span class="ss"&gt;max: &lt;/span&gt;&lt;span class="s1"&gt;'65'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of the information is there, but as you can see, it can get a little unwieldy to wrap your head around as the hash grows in size. Serialized arrays face similar challenges, but the amount of data displayed for a change is nowhere near as verbose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;These two gems provide a great way to track changes made to your application's data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PaperTrail&lt;/code&gt; is a well established gem and is used by many applications in their production environment. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;PaperTrailScrapbook&lt;/code&gt; is hugely helpful for following changes made over time. It provides useful tools for observing how people are using your application and the sorts of changes they are making to data. I am excited to see how the project continues to evolve and grow as time goes on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Time
&lt;/h2&gt;

&lt;p&gt;In the next post in this series, we'll take a look at the handy-dandy &lt;a href="https://github.com/snusnu/procto" rel="noopener noreferrer"&gt;Procto gem&lt;/a&gt;, which provides an excellent way to turn "your ruby object into a method object".&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
