<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hint</title>
    <description>The latest articles on DEV Community by Hint (@hint).</description>
    <link>https://dev.to/hint</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%2Forganization%2Fprofile_image%2F380%2Fe83530ea-6aac-4902-b534-a387c14d80c8.png</url>
      <title>DEV Community: Hint</title>
      <link>https://dev.to/hint</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hint"/>
    <language>en</language>
    <item>
      <title>Deploy Infrastructure With CDKTF</title>
      <dc:creator>Nate Vick</dc:creator>
      <pubDate>Tue, 07 Sep 2021 20:19:54 +0000</pubDate>
      <link>https://dev.to/hint/deploy-infrastructure-with-cdktf-2202</link>
      <guid>https://dev.to/hint/deploy-infrastructure-with-cdktf-2202</guid>
      <description>&lt;p&gt;CDK or Cloud Development Kit came out of AWS in 2018 as a way to write Infrastructure as Code in software languages used day-to-day by developers (JavaScript/TypeScript, Python, Java, C#, and soon Go). Since its release, a community has built up around it, and new flavors have arrived. AWS CDK, CDKTF, and CDK8s are all based on the same core, but compile to different formats. AWS CDK compiles to CloudFormation, while CDKTF compiles to Terraform compatible JSON, and CDK8s compiles to Kubernetes config.&lt;/p&gt;

&lt;p&gt;In this post, we will walk through how to use CDKTF with DigitalOcean's Terraform provider. A Terraform provider is a "plugin" for Terraform to interact with remote systems. In this case, the provider is created and maintained by DigitalOcean. We will share a few examples of creating a Digital Ocean Project, a VPC, a Postgres Database, and a Droplet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prereqs and Install
&lt;/h2&gt;

&lt;p&gt;To use cdktf you will need the following packages installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform ≥ 0.12&lt;/li&gt;
&lt;li&gt;Node.js ≥ 12.16&lt;/li&gt;
&lt;li&gt;Yarn ≥ 1.21&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To install cdktf, we will use &lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm install --global cdktf-cli&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will also need to &lt;a href="https://docs.digitalocean.com/reference/api/create-personal-access-token/" rel="noopener noreferrer"&gt;create a personal access token&lt;/a&gt; on Digital Ocean if you would like to deploy this config.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create and Initialize the example project
&lt;/h2&gt;

&lt;p&gt;First let's make a directory and change into it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ mkdir cdk-do-example &amp;amp;&amp;amp; cd cdk-do-example&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Initialize the project with the &lt;code&gt;init&lt;/code&gt; command. In general, I use the &lt;code&gt;--local&lt;/code&gt; flag, so all state is stored locally.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ cdktf init --template=typescript --local&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will be prompted for Project Name and Description but defaults are fine.&lt;/p&gt;

&lt;p&gt;The last step of setting up the project is to add Terraform providers to the &lt;code&gt;cdktf.json&lt;/code&gt;. Open the project in your editor and let's add the DigitalOcean provider to it as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "language": "typescript",
  "app": "npm run --silent compile &amp;amp;&amp;amp; node main.js",
  "terraformProviders": [
    "digitalocean/digitalocean@~&amp;gt; 2.9"
  ],
  "terraformModules": [],
  "context": {
    "excludeStackIdFromLogicalIds": "true",
    "allowSepCharsInLogicalIds": "true"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Dependencies
&lt;/h2&gt;

&lt;p&gt;Run &lt;code&gt;cdktf get&lt;/code&gt; to download the dependencies for using DigitalOcean with Typescript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cdktf get
Generated typescript constructs &lt;span class="k"&gt;in &lt;/span&gt;the output directory: .gen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's Write Some TypeScript
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;main.ts&lt;/code&gt; in your editor and let's start by creating a DO Project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Construct } from 'constructs'
import { App, TerraformStack, Token } from 'cdktf'
import { DigitaloceanProvider } from './.gen/providers/digitalocean'
import { Project } from './.gen/providers/digitalocean'

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name)

    new DigitaloceanProvider(this, 'digitalocean', {
      token: Token.asString(process.env.DO_TOKEN) 
    })

    new Project(this, 'example-project', {
      name: 'Example Rails Project'
    })

  }
}

const app = new App()
new MyStack(app, 'cdk-do-example')
app.synth()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will create a new &lt;code&gt;DigitaloceanProvider&lt;/code&gt; and pass in the environment variable assigned to your DO personal access token. Next, we create the &lt;code&gt;Project&lt;/code&gt;, which has a required key of &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Optionally, as a test of the code above, run &lt;code&gt;cdktf deploy&lt;/code&gt; to deploy your CDKTF project. The cli will ask if you want to make the changes listed under Resources. Type 'yes', then once it finishes, you will see a green check next to the Resources it successfully created.&lt;/p&gt;

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

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

&lt;p&gt;Now we will add a VPC, a Postgres Database, and a Droplet. Once those examples are in, we will tie it all together before deploying it again.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a VPC
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Construct } from 'constructs'
import { App, TerraformStack, Token } from 'cdktf'
import { DigitaloceanProvider, Droplet, Vpc } from './.gen/providers/digitalocean'
import { Project } from './.gen/providers/digitalocean'

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name)

    new DigitaloceanProvider(this, 'digitalocean', {
      token: Token.asString(process.env.DO_TOKEN) 
    })
    ...more code above

    new Vpc(this, 'example-vpc', {
      name: 'example-vpc',
      region: 'sfo3'
    })

  }
}

const app = new App()
new MyStack(app, 'cdk-do-example')
app.synth()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;*Note: If you do not have VPC in your account already this will become the default VPC which will not be deleted when cleaning up the CDKTF Project.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a Postgres Database
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Construct } from 'constructs'
import { App, TerraformStack, Token } from 'cdktf'
import { DigitaloceanProvider, Droplet, Vpc, DatabaseCluster, DatabaseUser, DatabaseDb } from './.gen/providers/digitalocean'
import { Project } from './.gen/providers/digitalocean'

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name)

    new DigitaloceanProvider(this, 'digitalocean', {
      token: Token.asString(process.env.DO_TOKEN) 
    })
    ...more code above

    const postgres = new DatabaseCluster(this, 'example-postgres', {
      name: 'example-postgres',
      engine: 'pg',
      version: '13',
      size: 'db-s-1vcpu-1gb',
      region: 'sfo3',
      nodeCount: 1
    })

    new DatabaseUser(this, 'example-postgres-user', {
      clusterId: `${postgres.id}`,
      name: 'example'
    })

    new DatabaseDb(this, 'example-postgres-db', {
      clusterId: `${postgres.id}`,
      name: 'example-db'
    })

  }
}

const app = new App()
new MyStack(app, 'cdk-do-example')
app.synth()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Notice we assigned the &lt;code&gt;DatabaseCluster&lt;/code&gt; to a variable &lt;code&gt;const postgres&lt;/code&gt;. We then use the variable to create the &lt;code&gt;DatabaseUser&lt;/code&gt; and &lt;code&gt;DatabaseDb&lt;/code&gt; on that cluster.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a Droplet
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Construct } from 'constructs'
import { App, TerraformStack, Token } from 'cdktf'
import { DigitaloceanProvider, Droplet, Vpc, DatabaseCluster, DatabaseUser, DatabaseDb } from './.gen/providers/digitalocean'
import { Project } from './.gen/providers/digitalocean'

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name)

    new DigitaloceanProvider(this, 'digitalocean', {
      token: Token.asString(process.env.DO_TOKEN) 
    })
    ...more code above

    new Droplet(this, 'example-droplet', {
      name: 'example-droplet',
      size: 's-1vcpu-1gb',
      region: 'sfo3',
      image: 'ubuntu-20-04-x64'
    })

  }
}

const app = new App()
new MyStack(app, 'cdk-do-example')
app.synth()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Let's Put It All Together
&lt;/h2&gt;

&lt;p&gt;If you run &lt;code&gt;cdktf deploy&lt;/code&gt; now, it would create everything, but nothing created would be put into the Digital Ocean project or the VPC we create. Let's do that now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Construct } from 'constructs'
import { App, TerraformStack, Token } from 'cdktf'
import { DigitaloceanProvider, Droplet, Vpc} from './.gen/providers/digitalocean'
import { DatabaseCluster, DatabaseUser, DatabaseDb } from './.gen/providers/digitalocean'
import { Project, ProjectResources } from './.gen/providers/digitalocean'

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name)

    new DigitaloceanProvider(this, 'digitalocean', {
      token: Token.asString(process.env.DO_TOKEN) 
    })

    const project = new Project(this, 'example-project', {
      name: 'Example Rails Project'
    })

    const vpc = new Vpc(this, 'example-vpc', {
      name: 'example-vpc',
      region: 'sfo3'
    })

    const postgres = new DatabaseCluster(this, 'example-postgres', {
      name: 'example-postgres',
      engine: 'pg',
      version: '13',
      size: 'db-s-1vcpu-1gb',
      region: 'sfo3',
      nodeCount: 1,
      privateNetworkUuid: vpc.id
    })

    new DatabaseUser(this, 'example-postgres-user', {
      clusterId: `${postgres.id}`,
      name: 'example'
    })

    new DatabaseDb(this, 'example-postgres-db', {
      clusterId: `${postgres.id}`,
      name: 'example-db'
    })

    const droplet = new Droplet(this, 'example-droplet', {
      name: 'example-droplet',
      size: 's-1vcpu-1gb',
      region: 'sfo3',
      image: 'ubuntu-20-04-x64',
      vpcUuid: vpc.id
    })

    new ProjectResources(this, 'example-project-resources', {
      project: project.id,
      resources: [
        postgres.urn,
        droplet.urn
      ],
      dependsOn: [ postgres, droplet ]
    })
  }
}

const app = new App()
new MyStack(app, 'cdk-do-example')
app.synth()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by assigning the project, VPC, and droplet to variables. In the &lt;code&gt;DatabaseCluster&lt;/code&gt; definition, we add &lt;code&gt;privateNetworkUuid: [vpc.id](http://vpc.id)&lt;/code&gt; to place the database in our newly created VPC. Similarly, on the &lt;code&gt;Droplet&lt;/code&gt; definition, we place it in the VPC, by adding &lt;code&gt;vpcUuid: vpc.id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lastly, create a new &lt;code&gt;ProjectResource&lt;/code&gt; to assign other resources to the Digital Ocean project. In this small example, we will assign the database and droplet to the project using the &lt;code&gt;urn&lt;/code&gt; for each resource. We will wait to assign those until both are created using a Terraform helper &lt;code&gt;dependsOn&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With all of that in place you can deploy again. The database and droplet creation take a bit, so be patient. 🙂 Once it has finished, check your Digital Ocean Dashboard to see everything created and ready for use.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;*Make sure you run &lt;code&gt;cdktf destroy&lt;/code&gt; to remove these resources from your account or you will be charged by Digital Ocean.*&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Things To Know
&lt;/h2&gt;

&lt;p&gt;CDKTF is still a young project, so it is changing fast, has limited documentation, and you can run into unclear errors.&lt;/p&gt;

&lt;p&gt;There are few things that help with the limited documentation. You can read the provider documentation on the &lt;a href="https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs" rel="noopener noreferrer"&gt;Terraform registry site&lt;/a&gt; and use it as a guide. Also, using a language like Typescript with VSCode there are a lot of code hints, hover info, and signature information. The gif below is an example of what is shown in VSCode when you hover on the problem. Note the missing properties for &lt;code&gt;DropletConfig&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Ae5Z300UKAWhqZiHfc/source.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Ae5Z300UKAWhqZiHfc/source.gif" alt="Typescript Annotations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you run into unclear errors, prepend &lt;code&gt;CDKTF_LOG_LEVEL=debug&lt;/code&gt; to the deploy and/or destroy commands to get very verbose output.&lt;/p&gt;

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

&lt;p&gt;In this post, we used CDKTF to create some basic example resources on Digital Ocean which gives you a good primer to build more complex infrastructure in a language of your choice. You can find the code from this post in this &lt;a href="https://github.com/hintmedia/cdk-do-example" rel="noopener noreferrer"&gt;repo&lt;/a&gt;. If you would like to chat more about CDK or infrastructure as code you can ping me on Twitter &lt;a href="https://twitter.com/natron99" rel="noopener noreferrer"&gt;@natron99&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
    </item>
    <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://dev.to/hint/active-record-encryption-22j3</link>
      <guid>https://dev.to/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>WSL for Rails developers</title>
      <dc:creator>Joel Hayhurst</dc:creator>
      <pubDate>Wed, 19 May 2021 17:43:58 +0000</pubDate>
      <link>https://dev.to/hint/wsl-for-rails-developers-2a3g</link>
      <guid>https://dev.to/hint/wsl-for-rails-developers-2a3g</guid>
      <description>&lt;p&gt;It is now possible, thankfully, to develop in Rails on Windows. For many years, it seemed one needed a Mac for this purpose; and I have nothing against Macs, and enjoy the MacBook Pro, which I have used throughout my career. But I also have a Windows machine. I like to play games sometimes, and now that I work from home, along with an increasing number of people, I have access to it. I like the idea that developers should be able to enjoy their preference in environment, where reasonable. Some prefer to boot directly into Linux, which is also a perfectly fine choice. On a whim, I decided to try Rails development on Windows, and it has worked out nicely.&lt;/p&gt;

&lt;p&gt;With WSL, the Windows Subsystem for Linux, we can now run a Linux distribution &lt;em&gt;within&lt;/em&gt; Windows 10. You can use it similarly to how you might use Homebrew on a Mac, and the whole thing is surprisingly seamless, especially after some recent improvements. All of the Linux development and server tools you're used to are present, and work the same as you are familiar with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;It's relatively easy to install; first you have to enable WSL as a "Windows feature", and then you go to the Microsoft Store and install your preferred distro. I know they have Debian and Ubuntu available, as well as some others. I chose Debian.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F01a94ce590d03e7fd06a7d472a7a841f%2F494f9%2Fwsl-features.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F01a94ce590d03e7fd06a7d472a7a841f%2F494f9%2Fwsl-features.png" alt="Enabling the WSL Windows feature"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F2721cc4426397ceddd210396f62a9514%2F1fbe8%2Fwsl-microsoft-store.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F2721cc4426397ceddd210396f62a9514%2F1fbe8%2Fwsl-microsoft-store.png" alt="Debian on the Microsoft Store"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminals
&lt;/h3&gt;

&lt;p&gt;As for terminals, there's a few options. The default terminal that comes with WSL is named after the distribution you installed, so for me it is simply called "Debian". It has the Debian swirl logo, which I like, and the default color scheme and font it comes with I find pleasant. On the downside, I've found it will always scroll down if new content comes up (say you are tailing a test log), so I got in the habit of waiting for the tests to finish before examining logs. It also doesn't have tabbing, but I found I don't need tabbing as much as I thought; I'm not on a laptop screen, so I have enough space to keep separate terminals open. It is possible to use PowerShell to access WSL, by running the &lt;code&gt;wsl&lt;/code&gt; command, but I find the color scheme is a bit off. And there's other options, like the new Windows Terminal, which does have tabbing and lots of customization options, but requires editing a text file to change settings (you can do it, you're a developer!), and still feels kind of in beta. In general though, I'm fine with the default WSL terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fa2569589606893caa4307c1e9b736f0f%2Fd72d4%2Fwsl-terminal3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fa2569589606893caa4307c1e9b736f0f%2Fd72d4%2Fwsl-terminal3.png" alt="Default Debian WSL terminal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  VSCode
&lt;/h3&gt;

&lt;p&gt;WSL also integrates perfectly with VSCode, which I use for most of my code editing. You can edit files directly within WSL and have them immediately affect the app. I mention this because there used to be an issue with this in WSL 1, but it has been resolved since WSL 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fb7ec1ff558e9e80d189d5ac23b2587ef%2F3ddad%2Fwsl-vscode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fb7ec1ff558e9e80d189d5ac23b2587ef%2F3ddad%2Fwsl-vscode.png" alt="Debian WSL in VSCode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;Docker Desktop also integrates very nicely with WSL since version 2. Before WSL 2, Docker had to use Hyper-V to emulate Linux, but now we don't even need Hyper-V enabled. Docker now uses the WSL 2 backend, and it seems quite a bit faster to start as well, in my experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fc22bef4097273d285ea7dc197d847246%2F22d0a%2Fwsl-docker.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fc22bef4097273d285ea7dc197d847246%2F22d0a%2Fwsl-docker.png" alt="Booting Docker Desktop on WSL 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Filesystem
&lt;/h3&gt;

&lt;p&gt;When you use WSL, you will want to store all of your relevant code within the WSL filesystem, not on the Windows filesystem. In WSL 1 there was more crossover, but not in WSL 2; everything goes on the Linux virtual drive. It is still possible to browse these files using Windows Explorer, if you are so inclined, but in general probably best to stick with Linux tools when using this drive. Still I do open one of my WSL directories in Explorer regularly for the purpose of easily opening screenshots produced by Capybara.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F1348a707d563e5d1ee4c5991973d8286%2F928ea%2Fwsl-explorer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F1348a707d563e5d1ee4c5991973d8286%2F928ea%2Fwsl-explorer.png" alt="Viewing WSL files in Explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy-and-paste
&lt;/h3&gt;

&lt;p&gt;One other note about transitioning from Mac, you'll probably want to enable the Ctrl-Shift-C and Ctrl-Shift-V copy-paste functionality in the terminal. On Mac, we have that handy Command key, but on Windows we are stuck with just Ctrl, which is used plenty by Linux itself; so Ctrl-Shift is a necessary compromise. It's not that hard to get accustomed to, and besides that, we can also use right-click to copy and paste text.&lt;/p&gt;

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

&lt;p&gt;I've been developing with this system for over a year now and haven't run into any serious problems. Probably the biggest issue I had is that WSL, combined with the code repositories of some large apps I'm working on, used up a lot of disk space, and my C: drive only has 256 GB. I did however find a way to move WSL to a separate drive, a nice 1 TB SSD, and that solved my space issues. This can be accomplished using the &lt;code&gt;wsl --export&lt;/code&gt; and &lt;code&gt;wsl --import&lt;/code&gt; commands without much fuss.&lt;/p&gt;

&lt;p&gt;Another issue I've faced is that when working on a larger application, a lot of memory is used up by WSL. Running two operating systems simultaneously uses more resources. Even after quitting the applications, and quitting Docker, this memory would still be reserved by WSL. The solution is that when I'm done with using WSL for the day, I run &lt;code&gt;wsl --shutdown&lt;/code&gt; in PowerShell and that frees up all the memory again. I went ahead and upgraded to 32 GB of RAM to avoid these paging annoyances. However, this upgrade is only necessary because of the size of this application. Every other application I've worked in was comfortable with 16 GB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F49110ef872ccda4fd8a33ee8052f9b3d%2F889a4%2Fwsl-powershell.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F49110ef872ccda4fd8a33ee8052f9b3d%2F889a4%2Fwsl-powershell.png" alt="Shutting down WSL with PowerShell"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;These are all of the thoughts I have on WSL at this time. I hope you find it useful to be able to program in Rails on Windows, if that is your desire. If you give it a shot, you might find it's more comfortable than you expected. Thanks for reading.&lt;/p&gt;

</description>
      <category>wsl</category>
      <category>windows</category>
      <category>linux</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://dev.to/hint/what-s-cooking-in-rails-7-a42</link>
      <guid>https://dev.to/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>Ruby on Lambda: Integrating with ActiveStorage</title>
      <dc:creator>Nate Shoemaker</dc:creator>
      <pubDate>Tue, 11 Aug 2020 20:00:51 +0000</pubDate>
      <link>https://dev.to/hint/ruby-on-lambda-integrating-with-activestorage-5b11</link>
      <guid>https://dev.to/hint/ruby-on-lambda-integrating-with-activestorage-5b11</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is part of our Ruby on AWS Lambda blog series. A recent project had us migrating an existing PDF document processing system from Rails Sidekiq to AWS Lambda. The processing includes OCR, creating preview images, splicing the PDF, and more. Moving to Lambda reduced processing time by 300 times in some cases.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This series of articles will serve less as a step-by-step process to get OCR serverless infrastructure up and running and more of a highlight reel of our "Aha!" moments. In part one, we talk about creating a AWS Lambda Layer with Docker. In part two, we chatted about architecting a serverless app. In part three, we went through some strategies surrounding deployment. Here in part four, we'll investigate what's needed to integrate a Rails app that uses Active Storage with AWS Lambda. Check out the other posts in the series:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-layer-dependencies-1ooe"&gt;Layer Dependencies&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-planning-architecting-3foi"&gt;Planning and Architecting&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-package-ship-it-7ol"&gt;Package and Ship It&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: there are some assumptions made in this article regarding architecture and reading Part Two of this series, Planning &amp;amp; Architecting, may be useful before diving in here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Performance was our main reason for moving from Sidekiq to AWS Lambda. We used Active Storage on a few models and wanted to continue using it after the migration. Specifically, we wanted to take advantage of Active Storage &lt;a href="https://api.rubyonrails.org/classes/ActiveStorage/Variant.html"&gt;variants&lt;/a&gt; via a GraphQL API and React frontend. The app had multiple views that requested all of the variants at once, and because the variants are created at the time of the request, the user experience under Sidekiq was very poor. What if we could create all of those variants on Lambda as we processed the document? It sounded plausible to us, so we spiked on it.&lt;/p&gt;

&lt;p&gt;Note that we'll be touching on specific pieces of code that directly relate to the integration; this won't be a tutorial on how to use Active Storage or setup AWS Lambda. We are using S3 as our storage service, and will be referencing it as a standalone entity. Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading
&lt;/h2&gt;

&lt;p&gt;Instead of passing form params to a model, we’ll take more of a manual approach. In the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We have a &lt;code&gt;Document&lt;/code&gt; model with &lt;code&gt;has_one_attached :file&lt;/code&gt;. This allows the file to be uploaded to S3 without creating the backing Active Storage record. Delaying that allows us to process the document on Lambda, create the needed variants, and respond back with data that will be used to create those records.&lt;/p&gt;

&lt;p&gt;Lambda has no knowledge of the Rails app or Active Storage, but if we can point it to the file we just uploaded, it can reference and process it. After a &lt;code&gt;Document&lt;/code&gt; has been created, we send &lt;code&gt;document.file.key&lt;/code&gt; to the vent function (LINK TO PLANNING AND ARCHITECTURE POST) that starts the processing work. The document’s key is the name of the file on S3. On Lambda, the real fun begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variant Creation
&lt;/h2&gt;

&lt;p&gt;Essentially, we needed to trick Active Storage. When requesting a variant, Active Storage will look for &lt;code&gt;variant.jpg&lt;/code&gt; in a specific location on S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"variants/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;key&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;variant_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;key&lt;/code&gt; is the original file's key (&lt;code&gt;document.file.key&lt;/code&gt;), and &lt;code&gt;variant_key&lt;/code&gt; is a hash derived from the variant's transformation options (e.g. &lt;code&gt;{auto_orient: true, resize: '1000x1000'}&lt;/code&gt;) and the Rails app's &lt;code&gt;ENV['SECRET_KEY_BASE']&lt;/code&gt;. Let's look at the Lambda  code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;create_variant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transformation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="c1"&gt;# create message verifier and variant key&lt;/span&gt;
 &lt;span class="n"&gt;verifier_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transformation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;purpose: :variation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;variant_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verifier_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Tempfile&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;'variant.jpg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="no"&gt;MiniMagick&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Tool&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="n"&gt;convert&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;
   &lt;span class="c1"&gt;# apply transformations here, like:&lt;/span&gt;
   &lt;span class="c1"&gt;# convert.auto_orient&lt;/span&gt;
   &lt;span class="c1"&gt;# convert.resize transformation[:resize]&lt;/span&gt;
   &lt;span class="n"&gt;convert&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Aws&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;S3&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
 &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="ss"&gt;bucket:       &lt;/span&gt;&lt;span class="s2"&gt;"some-cool-bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="ss"&gt;key:          &lt;/span&gt;&lt;span class="s2"&gt;"variants/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;key&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;variant_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="ss"&gt;body:         &lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="ss"&gt;content_type: &lt;/span&gt;&lt;span class="s1"&gt;'image/jpeg'&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;&lt;code&gt;#create_variant&lt;/code&gt; takes a &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;document.file.key&lt;/code&gt;, and the transformation options as arguments. The &lt;code&gt;variant_key&lt;/code&gt; is created (we'll look at what &lt;code&gt;verifier&lt;/code&gt; is soon), and the actual image conversion is handled with ImageMagick. Note the actual ImageMagick conversions depend on the transformation options being passed in. For an example I used the options that were previously mentioned, &lt;code&gt;{auto_orient: true, resize: '1000x1000'}&lt;/code&gt;. After the image is processed, we then place it on S3, using the specific path Active Storage expects.&lt;/p&gt;

&lt;p&gt;Figuring out how to create the &lt;code&gt;verifier_key&lt;/code&gt; was quite difficult. It took a late night fueled by coffee and spelunking into the bowels of Active Storage, but it proved fruitful. Let's take a look at &lt;code&gt;#verifier&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verifier&lt;/span&gt;
 &lt;span class="n"&gt;key_generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CachingKeyGenerator&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="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;KeyGenerator&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="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SECRET_KEY_BASE'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;iterations: &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key_generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ActiveStorage'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MessageVerifier&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;key&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;Active Storage was created by people much smarter than I, and I'll be the first to admit that I did not look at &lt;em&gt;all&lt;/em&gt; the implementation details surrounding &lt;code&gt;ActiveSupport::CachingKeyGenerator&lt;/code&gt;,  than I, and I'll be the first to admit that I did not look at &lt;em&gt;all&lt;/em&gt; the implementation details surrounding &lt;code&gt;ActiveSupport::KeyGenerator&lt;/code&gt;, &lt;code&gt;ActiveSupport::MessageVerifier&lt;/code&gt;. However, their names are descriptive and give us a good bird's-eye view. A key is generated from your Rails app's &lt;code&gt;SECRET_KEY_BASE&lt;/code&gt;, and that key is then turned into a digest that is used to create the path to the variant. A few notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;iterations&lt;/code&gt; option when instantiating &lt;code&gt;ActiveSupport::KeyGenerator&lt;/code&gt; does not have to be 1,000. I, of course, didn't test &lt;em&gt;every&lt;/em&gt; number, but the natural numbers I tested all had the same result. Active Storage uses 1,000, so I thought I would too.&lt;/li&gt;
&lt;li&gt;The argument passed to &lt;code&gt;#generate_key&lt;/code&gt; is the salt, and for our intended purposes on Lambda it can be any string.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Data for the &lt;code&gt;active_storage_attachments&lt;/code&gt; record
&lt;/h2&gt;

&lt;p&gt;Early on in the spike, we hit a lot of &lt;code&gt;ActiveStorage::IntegrityError&lt;/code&gt;s. While you can find yourself raising this error on accident in many scenarios, the reason why is always the same: the hashed contents of the file don't match what's stored in the database. Since we uploaded the file straight to S3, we purposefully didn't create a record in &lt;code&gt;active_storage_attachments&lt;/code&gt;, and don't have the reference to the file anymore without downloading it (which would be a waste). We had to create the digest on Lambda while we still had a reference to the file.&lt;/p&gt;

&lt;p&gt;Here's the data we collect for each page in a document (note that &lt;code&gt;file&lt;/code&gt; is a &lt;code&gt;File&lt;/code&gt; object):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="ss"&gt;content_md5:  &lt;/span&gt;&lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MD5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;base64digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;byte_size:    &lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;key:          &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;content_type: &lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After every page is processed, our sink function collects all the data and sends a JSON response back to the Rails app.&lt;/p&gt;

&lt;h2&gt;
  
  
  All together now
&lt;/h2&gt;

&lt;p&gt;When the Rails app receives the JSON, Sidekiq workers are started that process the response. Part of the workers' responsibility is to create a record in &lt;code&gt;active_storage_attachments&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="ss"&gt;key:          &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="ss"&gt;filename:     &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="ss"&gt;checksum:     &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'content_md5'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="ss"&gt;byte_size:    &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'byte_size'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="ss"&gt;content_type: &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'content_type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="no"&gt;ActiveStorage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_before_direct_upload!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://api.rubyonrails.org/classes/ActiveStorage/Blob.html#method-c-create_before_direct_upload-21"&gt;&lt;code&gt;#create_before_direct_upload!&lt;/code&gt;&lt;/a&gt; allows us to create the record and &lt;em&gt;not&lt;/em&gt; upload the file to S3 as it already exists there. Tricking Active Storage, and all.&lt;/p&gt;

&lt;p&gt;And with that, Active Storage works just as it normally would. With our implementation, however, we process &lt;em&gt;all&lt;/em&gt; variants in parallel, improving load times and user experience for everyone!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Ruby on AWS Lambda: Package &amp; Ship It</title>
      <dc:creator>Nate Shoemaker</dc:creator>
      <pubDate>Tue, 11 Aug 2020 20:00:47 +0000</pubDate>
      <link>https://dev.to/hint/ruby-on-aws-lambda-package-ship-it-7ol</link>
      <guid>https://dev.to/hint/ruby-on-aws-lambda-package-ship-it-7ol</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is part of our Ruby on AWS Lambda series. A recent project had us migrating an existing pdf document processing system from Rails/Sidekiq to AWS Lambda. The processing includes OCR, creating preview images, splicing the pdf, and more. Moving to Lambda reduced processing time by 300% in some cases; parallelization for the win!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This series will serve less as a step-by-step process to get OCR serverless infrastructure up and running and more of a highlight reel of our "Aha!" moments. In part one, we talked about creating an AWS Lambda Layer with Docker. In part two, we chatted about architecture. Here in part three, we'll go through some strategies surrounding deployment. Check out the other posts in the series:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-layer-dependencies-1ooe"&gt;Layer Dependencies&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-planning-architecting-3foi"&gt;Planning and Architecting&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-lambda-integrating-with-activestorage-5b11"&gt;Integrating with Active Storage&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deploying to Lambda gets more complicated as the application grows. There is a big difference between deploying a single &lt;code&gt;hello world&lt;/code&gt; function with zero dependencies, and deploying multiple functions in multiple languages along with dependencies in a Lambda Layer. We discovered some useful things along the way while making that transition. Let's dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Function naming conventions and environment variables
&lt;/h2&gt;

&lt;p&gt;We followed a straightforward convention for differentiating between function names and their environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{function_name}_{environment}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Given our three Rails environments (&lt;code&gt;development&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, and &lt;code&gt;production&lt;/code&gt;) and a function name of &lt;code&gt;pdf_processor&lt;/code&gt;, we'd have three separate functions: &lt;code&gt;pdf_processor_development&lt;/code&gt;, &lt;code&gt;pdf_processor_staging&lt;/code&gt;, and &lt;code&gt;pdf_processor_production&lt;/code&gt;. Adding more functions may seem a bit overwhelming in terms of function count, but the clear distinctions between functions and their environments allow for isolated development, testing, and debugging.&lt;/p&gt;

&lt;p&gt;Along with other normal environment variables that the functions use, we added a &lt;code&gt;RUBY_ENV&lt;/code&gt; environment variable that was either &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, or &lt;code&gt;production&lt;/code&gt;. This is primarily used for name-spacing S3 buckets, DynamoDB tables, and calling other lambda functions. This decision played out well because we now have isolation in how we use other systems and services. This also makes reading logs and tracking performance metrics a lot easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instrumentation
&lt;/h2&gt;

&lt;p&gt;Instrumentation refers to the measure of a product's performance, to diagnose errors, and to write trace information. Early on in development, we added instrumentation to gain insight into how the bulk of the processing work performs.&lt;/p&gt;

&lt;p&gt;Here’s our implementation:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Instrument&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clock_gettime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLOCK_MONOTONIC&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="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"--&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; took &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clock_gettime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLOCK_MONOTONIC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then it is used like:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'doing some work'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;do_the_work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;i&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;The log would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; doing some work took 300ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;start&lt;/code&gt; variable gets caught in a closure and is available for the Proc that is returned. The semi-cryptic looking &lt;code&gt;Process.clock_gettime&lt;/code&gt; is used to read from the OS clock, and is more accurate than Ruby's &lt;code&gt;Time&lt;/code&gt; class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless
&lt;/h2&gt;

&lt;p&gt;When we first started building on Lambda, we &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/ruby-package.html"&gt;deployed the AWS way&lt;/a&gt;: zip the code up and ship it via the AWS CLI. This was fine in the beginning, but it left a lot to be desired. It puts the responsibility on the developer to know about the deployment process, something that should be handled by configuration. That added responsibility also detracted from a deployment being deterministic. If you missed a small detail, a deployment, and thus the function itself, could be botched. We don't zip and deploy our Rails apps up manually, and we shouldn't have to do that with our serverless functions either!&lt;/p&gt;

&lt;p&gt;In comes &lt;a href="https://www.serverless.com/"&gt;Serverless Framework&lt;/a&gt;. Everything we love about configuration and deterministic deployments are handled by Serverless. All configuration is written in YAML and is concise, intuitive, and incredibly powerful.. Environment variables, runtime specification, memory configuration, AWS roles, and layer versioning can all be done in a single YAML file. These features became even more valuable when we rewrote our vent function in JavaScript. We  now have a separate serverless config for each lambda function that allows us to achieve true language isolation. If you'd like to learn more about using Serverless, take a look at &lt;a href="https://www.serverless.com/framework/docs/providers/aws/guide/quick-start/"&gt;their AWS quickstart guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Shipping a Lambda application doesn’t have to be difficult. I hope what we’ve learned has helped you!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>aws</category>
      <category>serverless</category>
      <category>devops</category>
    </item>
    <item>
      <title>Ruby on AWS Lambda: Layer Dependencies</title>
      <dc:creator>Joel Hayhurst</dc:creator>
      <pubDate>Tue, 11 Aug 2020 20:00:41 +0000</pubDate>
      <link>https://dev.to/hint/ruby-on-aws-lambda-layer-dependencies-1ooe</link>
      <guid>https://dev.to/hint/ruby-on-aws-lambda-layer-dependencies-1ooe</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is part of our Ruby on AWS Lambda blog series. A recent project had us migrating an existing PDF document processing system from Rails Sidekiq to AWS Lambda. The processing includes OCR, creating preview images, splicing the PDF, and more. Moving to Lambda reduced processing time by 300 times in some cases.&lt;/em&gt;&lt;br&gt;
​&lt;br&gt;
&lt;em&gt;This series of articles will serve less as a step-by-step process to get OCR serverless infrastructure up and running and more of a highlight reel of our "Aha!" moments. In part one, we talk about creating a AWS Lambda Layer with Docker. Check out the other posts in the series:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-planning-architecting-3foi"&gt;Planning &amp;amp; Architecture&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-package-ship-it-7ol"&gt;Package &amp;amp; Ship It!&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-lambda-integrating-with-activestorage-5b11"&gt;Integrating with Active Storage&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building dependencies for Lambda can be confusing. They need to be compiled, zipped up, and then made into a layer. We also have to keep the file size for the dependencies under Lambda's limit. Here is how we did that using a Dockerfile.&lt;br&gt;
​&lt;br&gt;
You can &lt;a href="https://gist.github.com/joelmichael/efe48be65d789c555459067b5a54e42d"&gt;check out the full Dockerfile here&lt;/a&gt;.&lt;br&gt;
​&lt;/p&gt;
&lt;h2&gt;
  
  
  Dockerfile details
&lt;/h2&gt;

&lt;p&gt;​&lt;br&gt;
One question when building the dependencies was which Docker image to use. We first tried using the &lt;code&gt;amazonlinux&lt;/code&gt; image, but this actually resulted in some build problems with one of our dependencies. We later found the LambCI images and ended up using &lt;code&gt;lambci/lambda:build-ruby2.7&lt;/code&gt;, because we are using Ruby. This worked perfectly for us, and it has the benefit of already having build tools installed, making for faster Docker builds.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use AWS Lambda ruby2.7 build environment&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; lambci/lambda:build-ruby2.7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
AWS Lambda currently has a limit of 250 MB for its dependencies. If you are building significantly larger dependencies, as we were, then you are likely to reach this limit. It is therefore very important to compile your dependencies in a way that reduces file size. By using the &lt;code&gt;-Os&lt;/code&gt; compile time option, we were able to reduce the size of our binaries by over 90%.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Optimize compilation for size to try and stay below Lambda's 250 MB limit&lt;/span&gt;
&lt;span class="c"&gt;# This reduces filesize by over 90% (!) compared to the default -O2&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CFLAGS "-Os"&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CXXFLAGS $CFLAGS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
While slightly less performant than the default &lt;code&gt;-O2&lt;/code&gt;, the massively reduced file size is worth it in this situation.&lt;br&gt;
​&lt;br&gt;
Next up is building Leptonica.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
# Leptonica image-reading dependencies
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libjpeg-devel libpng-devel libtiff-devel
​
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; http://www.leptonica.org/source/leptonica-1.79.0.tar.gz
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf leptonica-1.79.0.tar.gz
​
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; leptonica-1.79.0&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="k"&gt;RUN &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
You will need to install the &lt;code&gt;-devel&lt;/code&gt; packages when compiling, but won't need the &lt;code&gt;-devel&lt;/code&gt; variant when providing the dependencies for runtime.&lt;br&gt;
​&lt;br&gt;
Then Tesseract:&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
# Optional Tesseract foreign language training dependencies
&lt;span class="c"&gt;# libicu-devel on Yum is of insufficient version (50, 52 is required)&lt;/span&gt;
&lt;span class="c"&gt;# These are also not really necessary for our usage.&lt;/span&gt;
&lt;span class="c"&gt;#RUN yum install -y libicu-devel pango-devel cairo-devel&lt;/span&gt;
​
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-Lo&lt;/span&gt; tesseract-4.1.1.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;  https://github.com/tesseract-ocr/tesseract/archive/4.1.1.tar.gz
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf tesseract-4.1.1.tar.gz
​
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; tesseract-4.1.1&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./autogen.sh &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="c"&gt;# These ENV vars have to be set or it will not build&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LEPTONICA_CFLAGS -I/opt/include/leptonica&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LEPTONICA_LIBS -L/opt/lib -lleptonica&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="k"&gt;RUN &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
​
# English training data
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /opt/share/tessdata&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt; https://github.com/tesseract-ocr/tessdata_best/raw/master/eng.traineddata
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
GhostScript was technically installable via RPM, but the amount of dependencies was too great. More on that later. We decided to just compile it with a minimal dependency set.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs952/ghostscript-9.52.tar.gz
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf ghostscript-9.52.tar.gz
​
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; ghostscript-9.52&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="k"&gt;RUN &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Ironically, we end up installing &lt;code&gt;ghostscript-devel&lt;/code&gt; so that ImageMagick can be built. It might be possible to use the prior GhostScript installation here, but this was simple enough for build purposes.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ghostscript-devel
​
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-Lo&lt;/span&gt; ImageMagick-7.0.10-6.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;  https://github.com/ImageMagick/ImageMagick/archive/7.0.10-6.tar.gz
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf ImageMagick-7.0.10-6.tar.gz
​
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; ImageMagick-7.0.10-6&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="k"&gt;RUN &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
We considered using libvips instead of ImageMagick, but it would have added scope to the project. Nonetheless I have some commented out code in the Dockerfile for building libvips in case we decide to switch to it in the future.&lt;br&gt;
​&lt;br&gt;
For Ruby gems, a Gemfile in the same directory that contains all of the gems is all we need.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s1"&gt;'https://rubygems.org'&lt;/span&gt;
&lt;span class="err"&gt;​&lt;/span&gt;
&lt;span class="c1"&gt;# PDF processing gems&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'simhash2'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'phashion'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rtesseract'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'mini_magick'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'pdf-reader'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'hexapdf'&lt;/span&gt;
&lt;span class="err"&gt;​&lt;/span&gt;
&lt;span class="c1"&gt;# Other gems used in these files&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'procto'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'adamantium'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'concord'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'activesupport'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 6.0.2'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'chronic'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'activestorage'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Note that a little bit of trickery is necessary to modify the gem paths for loading in Lambda.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
# Phashion dependencies
&lt;span class="c"&gt;# Can skip this step because they are already installed above for Leptonica&lt;/span&gt;
&lt;span class="c"&gt;#RUN yum install -y libjpeg-devel libpng-devel&lt;/span&gt;
​
# Copy Gemfile from host into container's current directory
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Gemfile .&lt;/span&gt;
​
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle config &lt;span class="nb"&gt;set &lt;/span&gt;path vendor/bundle
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle
​
# Modify directory structure for Lambda load path
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; vendor/bundle&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;ruby/gems
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;ruby/2.&lt;span class="k"&gt;*&lt;/span&gt; ruby/gems
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;ruby /opt
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Now the RPM packages. I left in the installation for GhostScript and its dependencies from RPM as a comment, mainly so you can see how many packages I had to specify manually.&lt;br&gt;
​&lt;br&gt;
Note that you will have to specify the &lt;code&gt;x86_64&lt;/code&gt; variants of these packages when using these tools.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
# Install yumdownloader and rpmdev-extract
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; yum-utils rpmdevtools
​
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;rpms
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; rpms&lt;/span&gt;
​
# Download dependency RPMs
&lt;span class="k"&gt;RUN &lt;/span&gt;yumdownloader libjpeg-turbo.x86_64 libpng.x86_64 libtiff.x86_64 &lt;span class="se"&gt;\
&lt;/span&gt;  libgomp.x86_64 libwebp.x86_64 jbigkit-libs.x86_64
&lt;span class="c"&gt;# GhostScript and dependencies&lt;/span&gt;
&lt;span class="c"&gt;# To reduce dependencies, we are compiling GhostScript from source instead&lt;/span&gt;
&lt;span class="c"&gt;# RUN yumdownloader ghostscript.x86_64 cups-libs.x86_64 fontconfig.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   fontpackages-filesystem freetype.x86_64 ghostscript-fonts jasper-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   lcms2.x86_64 libICE.x86_64 libSM.x86_64 libX11.x86_64 libX11-common \&lt;/span&gt;
&lt;span class="c"&gt;#   libXau.x86_64 libXext.x86_64 libXt.x86_64 libfontenc.x86_64 libxcb.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   poppler-data stix-fonts urw-fonts xorg-x11-font-utils.x86_64 avahi-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   acl.x86_64 audit-libs.x86_64 cracklib.x86_64 cracklib-dicts.x86_64 cryptsetup-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   dbus.x86_64 dbus-libs.x86_64 device-mapper.x86_64 device-mapper-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   elfutils-default-yama-scope elfutils-libs.x86_64 gzip.x86_64 kmod.x86_64 kmod-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   libcap-ng.x86_64 libfdisk.x86_64 libpwquality.x86_64 libsemanage.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   libsmartcols.x86_64 libutempter.x86_64 lz4.x86_64 pam.x86_64 qrencode-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   shadow-utils.x86_64 systemd.x86_64 systemd-libs.x86_64 ustr.x86_64 util-linux.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   expat.x86_64 xz-libs.x86_64 libgcrypt.x86_64 libgpg-error.x86_64 elfutils-libelf.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   bzip2-libs.x86_64&lt;/span&gt;
​
# Extract RPMs
&lt;span class="k"&gt;RUN &lt;/span&gt;rpmdev-extract &lt;span class="k"&gt;*&lt;/span&gt;.rpm
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.rpm
​
# Copy all package files into /opt/rpms
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-vR&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;/usr/&lt;span class="k"&gt;*&lt;/span&gt; /opt
​
# The x86_64 packages extract as lib64, we need to move these files to lib
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; rsync
&lt;span class="k"&gt;RUN &lt;/span&gt;rsync &lt;span class="nt"&gt;-av&lt;/span&gt; /opt/lib64/ /opt/lib/
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /opt/lib64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Notice some more path management. We used &lt;code&gt;rsync&lt;/code&gt; for copying because &lt;code&gt;cp&lt;/code&gt; gave us some problems.&lt;br&gt;
​&lt;br&gt;
Now we just need to zip up the dependencies.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /opt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;zip &lt;span class="nt"&gt;-r&lt;/span&gt; /root/ProcessDocumentLayer.zip &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
And lastly, the entrypoint for the Dockerfile, which copies the zip file to an output directory.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/bin/cp", "/root/ProcessDocumentLayer.zip", "/output"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Now we just need the Docker commands to build this. I put them at the very top of the file under a "Usage" section.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Usage:&lt;/span&gt;
&lt;span class="c"&gt;# docker build -t lambda .&lt;/span&gt;
&lt;span class="c"&gt;# docker run -v $(pwd):/output lambda&lt;/span&gt;
&lt;span class="c"&gt;# ./publish_layer.sh.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
The &lt;code&gt;publish_layer.sh&lt;/code&gt; script is a small one we wrote that uses &lt;code&gt;awscli&lt;/code&gt; to upload and publish the layer. You'll have to authenticate with AWS for it to work. I used &lt;code&gt;aws configure&lt;/code&gt; for this purpose but you can &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html"&gt;check out this article&lt;/a&gt; for more info.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;ProcessDocumentLayer.zip s3://process-document-layers
aws lambda publish-layer-version &lt;span class="nt"&gt;--layer-name&lt;/span&gt; ProcessDocumentLayer &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Process Document dependencies"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--content&lt;/span&gt; &lt;span class="nv"&gt;S3Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;process-document-layers,S3Key&lt;span class="o"&gt;=&lt;/span&gt;ProcessDocumentLayer.zip &lt;span class="nt"&gt;--compatible-runtimes&lt;/span&gt; ruby2.7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
And that's it. With this Dockerfile, we are able to easily build and publish a dependency layer for our OCR system on Lambda. We hope this was useful for you!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>aws</category>
      <category>serverless</category>
      <category>docker</category>
    </item>
    <item>
      <title>Ruby on AWS Lambda: Planning &amp; Architecting</title>
      <dc:creator>Nate Shoemaker</dc:creator>
      <pubDate>Tue, 11 Aug 2020 17:58:39 +0000</pubDate>
      <link>https://dev.to/hint/ruby-on-aws-lambda-planning-architecting-3foi</link>
      <guid>https://dev.to/hint/ruby-on-aws-lambda-planning-architecting-3foi</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is part of our Ruby on AWS Lambda blog series. A recent project had us migrating an existing pdf document processing system from Rails Sidekiq to AWS Lambda. The processing includes OCR, creating preview images, splicing the pdf, and more. Moving to Lambda reduced processing time by 300% in some cases; parallelization for the win!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This series will serve less as a step-by-step process to get OCR serverless infrastructure up and running and more of a highlight reel of our "Aha!" moments. In part one, we talked about creating an AWS Lambda Layer with Docker. In part two, we'll chat about architecting a serverless app. Check out the other posts in the series:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-layer-dependencies-1ooe"&gt;Layer Dependencies&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-planning-architecting-3foi"&gt;Package &amp;amp; Ship It!&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-lambda-integrating-with-activestorage-5b11"&gt;Integrating with Active Storage&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Rails devs, tell me if you've been here before: you have small units of work that should be processed concurrently, so you reach for Sidekiq. Solved! Well, usually solved. Sidekiq is an amazing tool that we use at Hint, and it solves most concurrency problems.&lt;/p&gt;

&lt;p&gt;However, a bottleneck is always a possibility. In our case, the document processor we engineered could process a PDF with one page, or 500. But since each page has to be processed and there is no page limit when uploading, it was common to process hundreds of pages. This work took much too time with Sidekiq, even with tens of workers. We wanted to see the same performance whether the PDF a user uploads has 50 pages, or 500.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;AWS Lambda was a good solution for our performance problem. It allows us to run our workers in parallel independent of the number of pages we are processing. However, Lambda doesn't give you any guidance on &lt;em&gt;how&lt;/em&gt; to design a serverless application. You are provided the basic building blocks: a function that calls other functions, and the ability to use any other AWS service. Quite open-ended! Luckily, there are some standard architecture practices that have emerged in the serverless community. We'll be focusing on the most popular strategy: fan-out, fan-in.&lt;/p&gt;

&lt;h3&gt;
  
  
  "What do you mean, software architecture?"
&lt;/h3&gt;

&lt;p&gt;Glad you asked! Martin Fowler said it best:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When people in the software industry talk about “architecture”, they refer to a hazily defined notion of the most important aspects of the internal design of a software system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we talk about architecture in the context of Lambda, we'll be touching on function composition, utilizing other AWS services, and how the application communicates with the real world. If you haven't, check out &lt;a href="https://martinfowler.com/architecture/"&gt;Martin Fowler's thoughts on software architecture&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Fan-out, Fan-in
&lt;/h2&gt;

&lt;p&gt;The fan-out, fan-in architecture is simple: one function starts &lt;code&gt;n&lt;/code&gt; number of functions, and when those finish, another function does something with the results. The first function is referred to as the ventilator (or vent). The vent function calls worker functions. Once the worker functions finish, the sink function is called. So, vent -&amp;gt; workers -&amp;gt; sink.&lt;/p&gt;

&lt;p&gt;For example, let's use a hypothetical 100-page pdf.. The vent function is triggered from an outside application and 100 worker functions are called. Each worker process a single page. The sink function is then invoked when all 100 are done, and we do things with the result. In theory, 1,000,000 pages should take as long as 100, since all the processing is happening in parallel. Let's go into a little bit more detail about the whole process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User uploads PDF from Rails app&lt;/li&gt;
&lt;li&gt;PDF is uploaded to S3 via Active Storage&lt;/li&gt;
&lt;li&gt;Vent function is triggered with the file key as an argument&lt;/li&gt;
&lt;li&gt;Vent function calls &lt;code&gt;n&lt;/code&gt; number of worker functions, passing file key as an argument so workers can find the file on S3&lt;/li&gt;
&lt;li&gt;Worker function processes page, stores preview image, collects information about page and stores that in DynamoDB, and a record in DynamoDB that holds the keeps track of the pages left is decreased by one&lt;/li&gt;
&lt;li&gt;If the worker is the final worker in the queue, the sink function is called&lt;/li&gt;
&lt;li&gt;Sink function collects all page information from DynamoDB and sends results as JSON back to the Rails app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/dynamodb/"&gt;DynamoDB&lt;/a&gt; is a NoSQL database offered by AWS. When using the fan-out, fan-in pattern you must keep track of progress so the sink function can be called when all workers are completed. The nature of serverless means that no state can be held, thus an external, stateful service must be used. In our case, we are using it as a glorified counter. When a worker function finishes, we decrease the records count column (&lt;code&gt;pages_left&lt;/code&gt;) by one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;decrement_pages_left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Aws&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DynamoDB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;table_name:                  &lt;/span&gt;&lt;span class="s2"&gt;"documents-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'RUBY_ENV'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;key:                         &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;job_id: &lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="ss"&gt;update_expression:           &lt;/span&gt;&lt;span class="s1"&gt;'set pages_left = pages_left - :val'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;expression_attribute_values: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;':val'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="ss"&gt;return_values:               &lt;/span&gt;&lt;span class="s1"&gt;'UPDATED_NEW'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;invoke_sink_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:attributes&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'pages_left'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that the sink function is then called if there are no pages left to process. Easy!&lt;/p&gt;

&lt;p&gt;For a more in-depth look into the fan-out, fan-in pattern take a look at &lt;a href="https://theburningmonk.com/2018/04/how-to-do-fan-out-and-fan-in-with-aws-lambda/"&gt;this fantastic blog post by Yan Cui&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowing what language is better suited for a certain task
&lt;/h2&gt;

&lt;p&gt;We initially used Ruby for our vent, worker, and sink functions. However, we hit a bottleneck in the vent function. Invoking hundreds of concurrent network requests (which are IO bound) is not one of Ruby's strong suits. Using any kind of library that takes advantage of CPU cores doesn't gain any advantage because of Lambda's CPU limitations (more on that in the next section). So, what language does Lambda support that has asynchronous IO bound operations out of the box? JavaScript!&lt;/p&gt;

&lt;p&gt;Now, this isn't a bash on Ruby. All programming languages have their strengths and weaknesses, and a rewrite in a different language should be well researched and thought out beforehand. Luckily for us, we have lots of Ruby &lt;em&gt;and&lt;/em&gt; JS experience. Also, the vent function encapsulates very little business logic, so a rewrite would be a good fit if an initial spike proved fruitful. And it did! When processing a 186-page document, the vent function took 30 seconds in Ruby, and 2 seconds in JS. Nailed it!&lt;/p&gt;

&lt;p&gt;If you experience performance issues on Lambda, make sure to research the problem thoroughly. The cool thing about Lambda is that you can use different languages throughout the application process. If part of the process would perform much better with a different language, try it out!&lt;/p&gt;

&lt;h2&gt;
  
  
  CPU/RAM limitations
&lt;/h2&gt;

&lt;p&gt;When assigning resources to your Lambda functions, memory is the only configurable option. Why? Well, there is no obvious answer in the UI, and even worse, it's hidden &lt;a href="https://aws.amazon.com/lambda/faqs/"&gt;deep in a FAQ&lt;/a&gt;. When you choose the amount of memory you want to allocate for a function, you are given proportional CPU power and other resources. What the docs don't tell you, however, is that if you allocate &lt;em&gt;enough&lt;/em&gt; memory, you'll be given two cores instead of one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/a/47582392"&gt;This Stack Overflow comment&lt;/a&gt; has more info. TL;DR: if you are using 1.8GB or more of memory, you get to use two CPU cores, instead of one. Even two cores is not a lot, and that's why asynchronous operations in programming languages that utilize multiple cores don't perform well on Lambda. When architecting a serverless application, it's better to split larger tasks into smaller subtasks, when possible.&lt;/p&gt;

&lt;p&gt;I hope you learned something new today! Architecting serverless applications come with their own unique set of challenges, but the community has great solutions that have been production-tested for quite some time.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>aws</category>
      <category>serverless</category>
      <category>architecture</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://dev.to/hint/5-ways-to-splat-in-ruby-4pgm</link>
      <guid>https://dev.to/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>Rails System Tests In Docker</title>
      <dc:creator>Nate Vick</dc:creator>
      <pubDate>Tue, 28 Apr 2020 17:04:35 +0000</pubDate>
      <link>https://dev.to/hint/rails-system-tests-in-docker-4cj1</link>
      <guid>https://dev.to/hint/rails-system-tests-in-docker-4cj1</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://hint.io/blog/rails-system-test-docker"&gt;Hint's blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At Hint, we use Docker extensively. It is our development environment for all of our projects. On a recent greenfield project, we wanted to use Rails &lt;a href="https://weblog.rubyonrails.org/2017/4/27/Rails-5-1-final/"&gt;System Tests&lt;/a&gt; to validate the system from end to end. &lt;/p&gt;

&lt;p&gt;In order to comfortably use System Tests inside of Docker we had to ask ourselves a few questions: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do we use RSpec for System Tests?&lt;/li&gt;
&lt;li&gt;How do we run the tests in headless mode in a modern browser?&lt;/li&gt;
&lt;li&gt;Can we run the test in a non-headless browser for building and debugging efficiently?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;In the context of answering these questions, we are going to first need to profile the Rails project to which these answers apply. Our Rails app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses Docker and Docker Compose.&lt;/li&gt;
&lt;li&gt;Uses RSpec for general testing.&lt;/li&gt;
&lt;li&gt;Has an entrypoint or startup script.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;If some or none of the above apply to your app and you would like to learn more about our approach to Docker, take a look at this post: &lt;a href="https://hint.io/blog/rails-development-with-docker"&gt;Dockerizing a Rails Project&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prep Work
&lt;/h2&gt;

&lt;p&gt;If your project was started before Rails 5.1, some codebase preparation is necessary. Beginning in Rails 5.1, the Rails core team integrated Capybara meaning Rails now properly handles all the painful parts of full system tests, e.g. database rollback. This means tools like &lt;code&gt;database_cleaner&lt;/code&gt; are no longer needed. If it is present, remove &lt;code&gt;database_cleaner&lt;/code&gt; from your Gemfile and remove any related config (typically found in &lt;code&gt;spec/support/database_cleaner.rb&lt;/code&gt; , &lt;code&gt;spec/spec_helper.rb&lt;/code&gt; , or &lt;code&gt;spec/rails_helper.rb&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Installation
&lt;/h2&gt;

&lt;p&gt;After that codebase prep has been completed, verify that RSpec ≥ 3.7 and the required system tests helper gems are installed.&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;group&lt;/span&gt; &lt;span class="ss"&gt;:development&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rspec-rails'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;= 3.7'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'capybara'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;= 2.15'&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'selenium-webdriver'&lt;/span&gt;
  &lt;span class="c1"&gt;# gem 'chromedriver-helper' don't leave this cruft in your Gemfile.:D&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the default system test helper gems installed with &lt;code&gt;rails new&lt;/code&gt; after Rails 5.1. We will not need &lt;code&gt;chromedriver-helper&lt;/code&gt; since we will be using a separate container for headless Chrome with &lt;code&gt;chromedriver&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The configuration below has been tested on Mac and Linux, but not Windows.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;Speaking of containers, let's add that service to the &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# other services...&lt;/span&gt;
  &lt;span class="na"&gt;selenium&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selenium/standalone-chrome&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added the &lt;code&gt;selenium&lt;/code&gt; service, which pulls down the latest &lt;code&gt;selenium/standalone-chrome&lt;/code&gt; image. You may notice I have not mapped a port to the host. This service is for inter-container communication, so there is no reason to map a port. We will need to add an environment variable to the service (app in this case) that Rails/RSpec will be running on for setting a portion of the Selenium URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle exec rails server -p 3000 -b '0.0.0.0'&lt;/span&gt;
    &lt;span class="c1"&gt;# ... more config ...&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;43447:43447"&lt;/span&gt;
    &lt;span class="c1"&gt;# ... more config ...&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SELENIUM_REMOTE_HOST=selenium&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Capybara
&lt;/h2&gt;

&lt;p&gt;We added a port mapping for Capybara as well: &lt;code&gt;43447:43447&lt;/code&gt;. Now let's add the Capybara config at &lt;code&gt;spec/support/capybara.rb&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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;headless&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HEADLESS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'false'&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;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:rack_test&lt;/span&gt;
  &lt;span class="k"&gt;end&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;before&lt;/span&gt; &lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;js: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;headless&lt;/span&gt;
            &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SELENIUM_REMOTE_HOST'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:4444/wd/hub"&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="s1"&gt;'http://host.docker.internal:9515'&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: :chrome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;options: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;browser:              :remote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;url:                  &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desired_capabilities: :chrome&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Find Docker IP address&lt;/span&gt;
    &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;headless&lt;/span&gt;
                             &lt;span class="sb"&gt;`/sbin/ip route|awk '/scope/ { print $9 }'`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
                           &lt;span class="k"&gt;else&lt;/span&gt;
                             &lt;span class="s1"&gt;'0.0.0.0'&lt;/span&gt;
                           &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'43447'&lt;/span&gt;
    &lt;span class="n"&gt;session_server&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server&lt;/span&gt;
    &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app_host&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;session_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&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;session_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;after&lt;/span&gt; &lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;js: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:browser&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;/This page includes a password or credit card input in a non-secure context/&lt;/span&gt;
          &lt;span class="c1"&gt;# Ignore this warning in tests&lt;/span&gt;
          &lt;span class="k"&gt;next&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&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;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;level&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;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
          &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break it down section by section.&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;headless&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HEADLESS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'false'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are using the &lt;code&gt;headless&lt;/code&gt; variable to make some decisions later in the file. This variable allows us to run &lt;code&gt;bundle exec rspec&lt;/code&gt; normally and run system tests against headless Chrome in the &lt;code&gt;selenium&lt;/code&gt; container. Or we run &lt;code&gt;HEADLESS=false bundle exec rspec&lt;/code&gt; and when a system test will attempt to connect to &lt;code&gt;chromedriver&lt;/code&gt; running on the host machine.&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;before&lt;/span&gt; &lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:rack_test&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our default driver for system tests will be &lt;code&gt;rack_test&lt;/code&gt;. It is the fastest driver available because it does not involve starting up a browser. It also means we cannot test JavaScript while using it, which brings us to the next section.&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;before&lt;/span&gt; &lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;js: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;headless&lt;/span&gt;
          &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SELENIUM_REMOTE_HOST'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:4444/wd/hub"&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="s1"&gt;'http://host.docker.internal:9515'&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: :chrome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;options: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;browser:              :remote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;url:                  &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;desired_capabilities: :chrome&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# ...more config...       &lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any specs with &lt;code&gt;js: true&lt;/code&gt; set will use this config. We set the &lt;code&gt;url&lt;/code&gt; for Selenium to use depending on if we are running headless or not. Notice the special Docker domain we are setting the non-headless url to; it is a URL that points to the host machine. The special domain is currently only available on Mac and Windows, so we will need to handle that for Linux later.&lt;/p&gt;

&lt;p&gt;We set our driver to &lt;code&gt;:selenium&lt;/code&gt; with config options for &lt;code&gt;browser, url, desired_capabilities&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt; &lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;js: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="c1"&gt;# ...selenium config...&lt;/span&gt;

  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;headless&lt;/span&gt;
                           &lt;span class="sb"&gt;`/sbin/ip route|awk '/scope/ { print $9 }'`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
                         &lt;span class="k"&gt;else&lt;/span&gt;
                           &lt;span class="s1"&gt;'0.0.0.0'&lt;/span&gt;
                         &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'43447'&lt;/span&gt;
  &lt;span class="n"&gt;session_server&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server&lt;/span&gt;
  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app_host&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;session_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&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;session_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we set the &lt;code&gt;Capybara.server_host&lt;/code&gt; address to the &lt;code&gt;app&lt;/code&gt; container IP address if headless or &lt;code&gt;0.0.0.0&lt;/code&gt; if not.&lt;/p&gt;

&lt;p&gt;The last part of RSpec configuration is to require this config in &lt;code&gt;spec/rails_helper.rb&lt;/code&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="c1"&gt;# This file is copied to spec/ when you run 'rails generate rspec:install'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'spec_helper'&lt;/span&gt;
&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'RAILS_ENV'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'../../config/environment'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Prevent database truncation if the environment is production&lt;/span&gt;
&lt;span class="nb"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Rails environment is running in production mode!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;production?&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rspec/rails'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'support/capybara'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to install &lt;a href="https://sites.google.com/a/chromium.org/chromedriver/downloads"&gt;ChromeDriver&lt;/a&gt; on the host machine. You will need to place it in a location in your &lt;code&gt;$PATH&lt;/code&gt;. Once it is there, when you want to run non-headless system tests, you will need to start ChromeDriver &lt;code&gt;chromedriver --whitelisted-ips&lt;/code&gt; in a new terminal session.  Now on a Mac, you should be able to run headless or non-headless system tests. Those commands again are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#HEADLESS&lt;/span&gt;
bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rspec

&lt;span class="c"&gt;#NON-HEADLESS&lt;/span&gt;
&lt;span class="nv"&gt;HEADLESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rspec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Special Linux Config
&lt;/h2&gt;

&lt;p&gt;There is one last step for Linux users because of the special &lt;code&gt;host.docker.internal&lt;/code&gt; URL is not available. We need to add some config to the entrypoint or startup script to solve that issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOST_DOMAIN&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"host.docker.internal"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;check_host &lt;span class="o"&gt;{&lt;/span&gt; ping &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-c1&lt;/span&gt; &lt;span class="nv"&gt;$HOST_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# check if the docker host is running on mac or windows&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; check_host&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;HOST_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ip route | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR==1 {print $3}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_IP&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$HOST_DOMAIN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/hosts
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We set an environment variable to the special Docker URL. We then create a function to check if the host responds to that URL. If it responds, we move on assuming we are running on Mac or Windows. If it does not respond, we assign the container's IP to an environment variable, then append a record to &lt;code&gt;/etc/hosts&lt;/code&gt;. We are now all set to run system tests on Linux as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: CI Setup
&lt;/h2&gt;

&lt;p&gt;Let's wrap this up with config to run system tests on Circle CI. We need to add &lt;code&gt;SELENIUM_REMOTE_HOST&lt;/code&gt; and the Selenium Docker image to &lt;code&gt;.circleci/config.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;parallelism&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;circleci/ruby:2.6.0-node&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SELENIUM_REMOTE_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selenium/standalone-chrome&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect with me on Twitter(&lt;a href="https://twitter.com/natron99"&gt;@natron99&lt;/a&gt;) to continue the conversation about Rails and Docker!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>docker</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Macro See, Macro Do</title>
      <dc:creator>Nate Shoemaker</dc:creator>
      <pubDate>Fri, 17 Apr 2020 14:38:51 +0000</pubDate>
      <link>https://dev.to/hint/macro-see-macro-do-2985</link>
      <guid>https://dev.to/hint/macro-see-macro-do-2985</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://hint.io/blog/elixir-demystifying-quote"&gt;Hint's blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post assumes you have Erlang/Elixir installed and that you can spin up a Phoenix project.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've been diving into Elixir and Phoenix as of late. I have thoroughly enjoyed my time spent with both and could gush about them for far, far too long. Elixir has some features whose underlying implementation wasn't obvious at first glance, namely the macro &lt;code&gt;quote/2&lt;/code&gt; (that slash and number indicates the arity).&lt;/p&gt;

&lt;p&gt;I first came across &lt;code&gt;quote/2&lt;/code&gt; in a fresh Phoenix project. Let's create one now! Run &lt;code&gt;mix phx.new foo&lt;/code&gt;, open up &lt;code&gt;lib/foo_web/foo_web.ex&lt;/code&gt; and see &lt;code&gt;quote/2&lt;/code&gt; being used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/foo_web/foo_web.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;FooWeb&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;namespace:&lt;/span&gt; &lt;span class="no"&gt;FooWeb&lt;/span&gt;

      &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;
      &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;FooWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Gettext&lt;/span&gt;
      &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;FooWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as:&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;controller/0&lt;/code&gt; is then used in controllers like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/foo_web/controllers/page_controller.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;FooWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PageController&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;FooWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:controller&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Going through &lt;a href="https://pragprog.com/book/phoenix14/programming-phoenix-1-4"&gt;Programming Phoenix&lt;/a&gt; I saw this macro being used again and again. I understood what it &lt;em&gt;meant&lt;/em&gt;: the &lt;code&gt;use&lt;/code&gt;, &lt;code&gt;import&lt;/code&gt;, and &lt;code&gt;alias&lt;/code&gt; macros are being injected into &lt;code&gt;PageController&lt;/code&gt;, so dependencies can be shared across modules. But why not just include them in the function definition? What is going behind the scenes? Why &lt;code&gt;quote/2&lt;/code&gt;? Being a Rails developer accustomed to magic, I accepted it and moved on.&lt;/p&gt;

&lt;p&gt;One of Phoenix's (and Elixir) strengths is that nothing is hidden from the developer. Everything is gloriously defined, displayed, and explicitly composed right in front of you. There really &lt;em&gt;isn't&lt;/em&gt; any magic. Thus my acceptance of it bothered me, so let's dive in and learn about &lt;code&gt;quote&lt;/code&gt; together!&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy and (almost) paste
&lt;/h2&gt;

&lt;p&gt;The best way to learn is by doing, so why don't we create some modules and reproduce what we've seen. Here's a very simple example I came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# bar.exs&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Math&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Math&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Work&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:things&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;print_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"the sum of &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Work&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_sum&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;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Again, I don't &lt;em&gt;really&lt;/em&gt; know what &lt;code&gt;quote/2&lt;/code&gt; is doing and why, but we mimicked what we saw in Phoenix pretty close. I think we're ready to try this out, let's run &lt;code&gt;elixir bar.exs&lt;/code&gt; and see what happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; elixir bar.exs
** (UndefinedFunctionError) function Bar.AllTheThings.__using__/1 is undefined or private
    Bar.AllTheThings.__using__(:things)
    bar.exs:18: (module)
    bar.exs:17: (file)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;🤔&lt;/p&gt;

&lt;p&gt;It seems that we're missing a function that Elixir assumes we have implemented. I'll be honest - I've never written a module that was consumed by &lt;code&gt;use&lt;/code&gt;, so let's double back to &lt;code&gt;FooWeb&lt;/code&gt; in our Phoenix App to see if we missed anything. At the bottom of the file, you'll see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="k"&gt;defmacro&lt;/span&gt; &lt;span class="n"&gt;__using__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ah! Elixir was looking for that function, so let's slap that it in &lt;code&gt;Bar.AllTheThings&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Math&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defmacro&lt;/span&gt; &lt;span class="n"&gt;__using__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Diving into &lt;code&gt;defmacro&lt;/code&gt; is outside the scope of this post, but we can acknowledge it as a requirement of a module that's consumed by &lt;code&gt;use&lt;/code&gt;. The use of &lt;code&gt;apply/3&lt;/code&gt; is straightforward: take a module, an atom that represents the function name, and call it with some arguments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:things&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="c1"&gt;# is equivalent to&lt;/span&gt;
&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; elixir bar.exs
the sum of 2 and 2 is 4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great, our dependency injection works. Now that we understand the structure let's dig into what's happening under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  I heard you like Elixir, so let's represent some Elixir with Elixir
&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://hexdocs.pm/elixir/Kernel.SpecialForms.html#quote/2"&gt;docs&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; quote(opts, block)

Gets the representation of any expression.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's try it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; iex
iex(1)&amp;gt; quote do sum(2, 2) end
{:sum, [], [2, 2]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's right! We are representing Elixir with Elixir. Elixir's AST (&lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"&gt;abstract syntax tree&lt;/a&gt;) is... Elixir! Pretty cool, huh? Macros, such as &lt;code&gt;quote/2&lt;/code&gt;, are represented by a tuple of three elements. The first element is (usually) an atom, the second is for metadata, and the third is the argument list.&lt;/p&gt;

&lt;p&gt;I wonder what our &lt;code&gt;import Bar.Math&lt;/code&gt; looks like as an AST? Let's find out! Comment out everything in &lt;code&gt;bar.exs&lt;/code&gt; except for the &lt;code&gt;Bar.Math&lt;/code&gt; module. Rename the file to &lt;code&gt;bar.ex&lt;/code&gt; so Elixir can compile it, and run &lt;code&gt;iex&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; iex
iex(1)&amp;gt; c "bar.ex"
[Bar.Math]
iex(2)&amp;gt; quote do
...(2)&amp;gt;   import Bar.Math
...(2)&amp;gt; end
{:import, [context: Elixir], [{:__aliases__, [alias: false], [:Bar, :Math]}]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There it is! We can see the AST as a three element tuple. It holds all the information that Elixir needs to know to &lt;code&gt;import&lt;/code&gt; a module. &lt;code&gt;quote/2&lt;/code&gt; gives us some fantastic syntax sugar; could you imagine writing these tuples everywhere? Just for fun, let's see how deep into the rabbit hole we can go. Rename &lt;code&gt;bar.ex&lt;/code&gt; back to &lt;code&gt;bar.exs&lt;/code&gt;, uncomment all the code, and change the &lt;code&gt;import Bar.Math&lt;/code&gt; to the AST representation without &lt;code&gt;quote/2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# bar.exs&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:import&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="no"&gt;Elixir&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:__aliases__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;alias:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;],&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;:Math&lt;/span&gt;&lt;span class="p"&gt;]}]}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defmacro&lt;/span&gt; &lt;span class="n"&gt;__using__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; elixir bar.exs
the sum of 2 and 2 is 4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It works! Let's go another level in by removing &lt;code&gt;things/0&lt;/code&gt; and placing our AST directly in &lt;code&gt;__using/1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# bar.exs&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;defmacro&lt;/span&gt; &lt;span class="n"&gt;__using__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:import&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="no"&gt;Elixir&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:__aliases__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;alias:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;],&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;:Math&lt;/span&gt;&lt;span class="p"&gt;]}]}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You know the drill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; elixir bar.exs
the sum of 2 and 2 is 4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Nice! Is it possible to inline the AST we have &lt;em&gt;in&lt;/em&gt; our &lt;code&gt;Bar.Work&lt;/code&gt; module? Sadly, we can't. The &lt;code&gt;use/2&lt;/code&gt; macro changes this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:things&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AllTheThings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__using__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:things&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We've come to the end of this Elixir in Elixir train! There are no other stops on this line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;So, what did we learn? The &lt;code&gt;quote&lt;/code&gt; macro transforms Elixir code to an AST. We can then leverage that to achieve real dependency injection in a functional language. How cool is that?&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>beginners</category>
      <category>functional</category>
    </item>
    <item>
      <title>Strong Opinions About Strong Parameters</title>
      <dc:creator>Benjamin Wood</dc:creator>
      <pubDate>Fri, 10 Apr 2020 17:11:47 +0000</pubDate>
      <link>https://dev.to/hint/strong-opinions-about-strong-parameters-1p12</link>
      <guid>https://dev.to/hint/strong-opinions-about-strong-parameters-1p12</guid>
      <description>&lt;p&gt;Rails announced &lt;code&gt;strong_parameters&lt;/code&gt; as a replacement for &lt;code&gt;protected_attributes&lt;/code&gt; nearly eight years ago. In 2020 many Rails apps have not completed the migration. Others have made the migration, but are worse off than before. Was &lt;code&gt;strong_parameters&lt;/code&gt; a bad idea? I don't think so. But like many things, it depends on how you use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instances of &lt;code&gt;ActionController:Parameters&lt;/code&gt; leak outside of the controller
&lt;/h2&gt;

&lt;p&gt;This is undoubtedly the most common mistake I've seen. The reason that &lt;code&gt;strong_parameters&lt;/code&gt; is superior to &lt;code&gt;protected_attributes&lt;/code&gt; is that it narrows the concern of mass-assignment protection to the controller. With &lt;code&gt;protected_attributes&lt;/code&gt;, the entire app had to consider which attributes could mass-assigned, and which could not. This led to anti-patterns like the use of &lt;code&gt;without_protection&lt;/code&gt; which allowed developers to skip mass-assignment protection under certain circumstances.&lt;/p&gt;

&lt;p&gt;The benefit of &lt;code&gt;strong_parameters&lt;/code&gt; is lost when &lt;code&gt;ActionController::Parameters&lt;/code&gt; are passed outside of a controller and suddenly you have to be concerned about mass-assignment protection everywhere, again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# FILE: app/lib/payment_processor.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&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;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&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;process&lt;/span&gt;
      &lt;span class="c1"&gt;# ... payment processing logic, etc&lt;/span&gt;
      &lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;payment_params&lt;/span&gt;
      &lt;span class="vi"&gt;@params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:payment&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:payment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# FILE: app/controllers/payments_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
      &lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PaymentProcessor&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;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;p&gt;&lt;strong&gt;Strong Opinion&lt;/strong&gt;: Parameters should be permitted at the controller level. If you pass some params on to another object (like a service class), first permit the params that are allowed for mass-assignment, then call &lt;code&gt;to_h&lt;/code&gt; on it to convert the object to a good ol' &lt;code&gt;ActiveSupport::HashWithIndifferentAccess&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# FILE: app/lib/payment_processor.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&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;payment_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@payment_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payment_params&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;process&lt;/span&gt;
      &lt;span class="c1"&gt;# ... payment processing logic, etc&lt;/span&gt;
      &lt;span class="no"&gt;Payment&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="vi"&gt;@payment_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# FILE: app/controllers/payments_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
      &lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PaymentProcessor&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;payment_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;payment_params&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:payment&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:payment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;h2&gt;
  
  
  Use of strong parameters where mass assignment is not being performed
&lt;/h2&gt;

&lt;p&gt;The Rails community is pretty split on this one. Should you &lt;em&gt;always&lt;/em&gt; permit parameters? Or only permit parameters when mass-assignment is being performed? Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# FILE: app/controllers/users_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;special_update_action&lt;/span&gt;
      &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

      &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;special_attribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:special_attribute&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_params&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:special_attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:including&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;p&gt;&lt;strong&gt;Strong Opinion&lt;/strong&gt;: Don't complicate your controllers by permitting params when mass assignment is &lt;em&gt;not&lt;/em&gt; being performed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# FILE: app/controllers/users_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;special_update_action&lt;/span&gt;
      &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

      &lt;span class="c1"&gt;# Notice that special_attribute is pulled off of `params` directly&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;special_attribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:special_attribute&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;h2&gt;
  
  
  Order of operations when mutating a params object
&lt;/h2&gt;

&lt;p&gt;Remember, &lt;code&gt;strong_parameters&lt;/code&gt; is meant to sanitize &lt;em&gt;externally-provided&lt;/em&gt; parameters. If you add a key to the params object, then call &lt;code&gt;permit&lt;/code&gt;, you'll end up having to permit the parameter you just added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# FILE: app/controllers/credit_cards_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditCardsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
      &lt;span class="n"&gt;credit_card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CreditCard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:top_secret_token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TopSecretToken&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;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;credit_card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credit_card_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;credit_card_params&lt;/span&gt;
      &lt;span class="c1"&gt;# Notice the addition of top_secret_token is included in this&lt;/span&gt;
      &lt;span class="c1"&gt;# scenario so that it can be mass-assigned above&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:credit_card&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;:top_secret_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:credit_card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:attrs&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;p&gt;&lt;strong&gt;Strong Opinion&lt;/strong&gt;: Permit your params &lt;em&gt;before&lt;/em&gt; mutating the object. This allows you to permit only the externally-provided parameters, then modify the object as you see fit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# FILE: app/controllers/credit_cards_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditCardsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
      &lt;span class="n"&gt;credit_card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CreditCard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

      &lt;span class="n"&gt;assignable_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;credit_card_params&lt;/span&gt;
      &lt;span class="n"&gt;assignable_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:top_secret_token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TopSecretToken&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;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;credit_card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assignable_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;credit_card_params&lt;/span&gt;
      &lt;span class="c1"&gt;# Notice that top_secret_token does *not* need to be included&lt;/span&gt;
      &lt;span class="c1"&gt;# here because it is assigned to the (permitted) return value&lt;/span&gt;
      &lt;span class="c1"&gt;# of credit_card_params&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:credit_card&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:credit_card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;h2&gt;
  
  
  Defining permitted attributes multiple (sometimes many) times
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;protected_attributes&lt;/code&gt;, the model provided a central place to specify which attributes were permitted for mass-assignment. With &lt;code&gt;strong_parameters&lt;/code&gt;, permitted parameters are often defined multiple times for the same resource in different controllers. This is not DRY and it is not maintainable. Add in consideration for &lt;code&gt;accepts_nested_attributes_for&lt;/code&gt; and you're maintaining countless lists of the same parameters.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: In the previous examples parameters were permitted directly in the controller as seen below. This was done for simplicity's sake, and also on the basis that no other controllers duplicated the same set of permitted parameters.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# FILE: app/controllers/departments_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DepartmentsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
      &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Department&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;department_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;department_params&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:department&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;employees_attributes: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:employee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:attrs&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# FILE: app/controllers/special/departments_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Special&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DepartmentsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
        &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Department&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;department_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
          &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

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

      &lt;span class="c1"&gt;# Copy pasta&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;department_params&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:department&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;employees_attributes: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:employee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:attrs&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;p&gt;&lt;strong&gt;Strong Opinion&lt;/strong&gt;: Duplicated parameters should be permitted in a module that can be included wherever it is needed. Furthermore, nested attributes should not be redefined multiple times. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;
  &lt;span class="c1"&gt;# FILE: app/controllers/concerns/strong_parameters/employee.rb&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Concerns&lt;/span&gt;
    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;StrongParameters&lt;/span&gt;
      &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Employee&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;employee_params&lt;/span&gt;
          &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:employee&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permitted_attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permitted_attrs&lt;/span&gt;
          &lt;span class="sx"&gt;%i(some employee attrs)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# FILE: app/controllers/concerns/strong_parameters/department.rb&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Concerns&lt;/span&gt;
    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;StrongParameters&lt;/span&gt;
      &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Department&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;department_params&lt;/span&gt;
          &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:department&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permitted_attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permitted_attrs&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="ss"&gt;:some&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;employees_attributes: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_destroy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;Concerns&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StrongParameters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permitted_attrs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# FILE: app/controllers/departments_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DepartmentsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Concerns&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StrongParameters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Department&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
      &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Department&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;department_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# FILE: app/controllers/special/departments_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Special&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DepartmentsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
      &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Concerns&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StrongParameters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Department&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
        &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Department&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;department_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
          &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# FILE: app/controllers/employees_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmployeesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Concerns&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StrongParameters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Employee&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
      &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employee&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;employee_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



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

&lt;p&gt;We follow these practices at Hint, and it's helped us (and our clients) a lot. Does your organization need help wrangling strong parameters? We can help. Ping me on twitter: &lt;a class="comment-mentioned-user" href="https://dev.to/benjaminwood"&gt;@benjaminwood&lt;/a&gt;
.&lt;/p&gt;

&lt;p&gt;Originally published on the hint.io blog here: &lt;a href="https://hint.io/blog/strong-parameters-strong-opinions"&gt;https://hint.io/blog/strong-parameters-strong-opinions&lt;/a&gt;&lt;/p&gt;

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