<?xml version="1.0" encoding="UTF-8"?>
  <feed xmlns="http://www.w3.org/2005/Atom">
    <title>Ruby Magic by AppSignal</title>
    <subtitle>Dive deep into all things Ruby. If you are interested in learning more about concurrency, webservers, memory allocations and garbage collection, you'll love this.</subtitle>
    <id>https://blog.appsignal.com</id>
    <link href="https://blog.appsignal.com"/>
    <link href="https://blog.appsignal.com/ruby-magic-feed.xml" rel="self"/>
    <updated>2026-05-20T10:00:00+00:00</updated>
    <author>
      <name>Roy Tomeij</name>
    </author>
    
  <entry>
    <title>Data Sovereignty: How to Keep All of Your Services in Europe (AppSignal + Hatchbox)</title>
    <link rel="alternate" href="https://blog.appsignal.com/2026/05/07/data-sovereignty-how-to-keep-all-of-your-services-in-europe-appsignal-hatchbox.html"/>
    <id>https://blog.appsignal.com/2026/05/07/data-sovereignty-how-to-keep-all-of-your-services-in-europe-appsignal-hatchbox.html</id>
    <published>2026-05-07T00:00:00+00:00</published>
    <updated>2026-05-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Here&#039;s how you can have a PaaS-like experience while keeping all of your services in Europe with AppSignal and Hatchbox.</summary>
    <content type="html">&lt;p&gt;Over the last decade, a great deal of data privacy regulations have been passed in the European Union. Like it or not, measures like &lt;a href=&quot;https://gdpr-info.eu/&quot;&gt;GDPR&lt;/a&gt;, the &lt;a href=&quot;https://digital-strategy.ec.europa.eu/en/policies/digital-services-act&quot;&gt;Digital Services Act&lt;/a&gt;, and the upcoming &lt;a href=&quot;https://artificialintelligenceact.eu/&quot;&gt;Artificial Intelligence Act&lt;/a&gt; are exerting increasing influence across industries over how and especially where the data of European customers is stored.&lt;/p&gt;
&lt;p&gt;In this article, we will explore the ways to keep the simplicity of a Platform as a Service (PaaS) while utilizing only European providers.&lt;/p&gt;
&lt;h2&gt;Why Data Sovereignty Matters More Than Ever&lt;/h2&gt;
&lt;p&gt;The General Data Protection Regulation (GDPR), which was ratified in 2016, granted fundamental rights to the &amp;quot;data subject&amp;quot;. These include the right of access, the right of rectification, the right of erasure, and similar. One of the implications of this regulation was that personal data of EU citizens can only be stored inside the Union or countries with &lt;strong&gt;adequate&lt;/strong&gt; protection levels.&lt;/p&gt;
&lt;p&gt;In the 2020 Schrems II ruling, the EU Court of Justice invalidated &lt;a href=&quot;https://www.maastrichtuniversity.nl/blog/2020/07/schrems-ii-verdict-privacy-shield-gone&quot;&gt;the EU-US Privacy Shield&lt;/a&gt; over concerns of data privacy adequacy. The ripple effect caused by this decision (and others) has led to greater vigilance about data privacy among European citizens, and customers have started to wonder where their personal information is stored.&lt;/p&gt;
&lt;p&gt;All this has put European companies under increased pressure to both comply with the GDPR requirements and make those efforts visible to target groups.&lt;/p&gt;
&lt;h2&gt;Traditional Trade-offs (and Why They Suck)&lt;/h2&gt;
&lt;p&gt;Before it has switched to “maintenance mode” with no clear successor emerging, &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt; was undoubtedly the go-to provider of the last decade. As a traditional PaaS, it defined deployment ergonomics: &lt;code&gt;git push heroku master&lt;/code&gt; was the gold standard of developer experience. But even though it set the bar, it was never without drawbacks (costs, limited European options, and infrastructure lock-in, to name a few). Developers were always limited to databases, key-value stores, and logging services it provided. Then, there were limited scaling options and the inability to fine-tune resources; compromises worth making, but far from ideal.&lt;/p&gt;
&lt;p&gt;Another option is to build your own operations pipeline: implement a DIY solution on bare metal, create the necessary tooling, and deploy every change manually. Even with modern container orchestration tools like &lt;a href=&quot;https://kamal-deploy.org/&quot;&gt;Kamal&lt;/a&gt;, this approach introduces a massive DevOps overhead. Permissions have to be managed, passwords need to be stored securely and shared among the team, you know the drill. Basically, it boils down to hiring manpower to synchronize the inner and outer loops (development and operations).&lt;/p&gt;
&lt;p&gt;The youngest &amp;quot;traditional&amp;quot; way to approach deployment and operations is managed Kubernetes. However, it’s important to point out that it has a steep learning curve and is probably overkill for most Rails apps. It does incorporate some of the developer experience improvements that Heroku pioneered, but you&amp;#39;ll still want to have a person on the team to manage the configuration. And if you&amp;#39;re just starting out, that&amp;#39;s not an ideal situation.&lt;/p&gt;
&lt;h2&gt;The Third Way: Own Your Cloud Without the Complexity&lt;/h2&gt;
&lt;p&gt;But what if I told you you can combine developer ergonomics with the freedom to choose your cloud provider? To deploy with a single &lt;code&gt;git push&lt;/code&gt; while keeping your data in Europe? To connect to an application and performance monitoring service (APM) that also serves as log storage and uptime watchdog? There is a way to achieve this, and while it&amp;#39;s not a silver bullet, it comes close.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hatchbox.io/&quot;&gt;Hatchbox&lt;/a&gt; is a DevOps service provider designed with one goal in mind: to simplify the deployment pipeline for you. You pick a (European) cloud provider like &lt;a href=&quot;https://www.scaleway.com/&quot;&gt;Scaleway&lt;/a&gt; or &lt;a href=&quot;https://hetzner.com/&quot;&gt;Hetzner&lt;/a&gt; (or any other Ubuntu-based server with root access), create a server, and Hatchbox takes care of the rest. You can deploy your Rails application with one click, or even &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.appsignal.com/&quot;&gt;AppSignal&lt;/a&gt; is a EU-hosted monitoring service that boasts a full suite of observation tools: error and performance monitoring, uptime watchdog, log management, server metrics dashboards, etc.&lt;/p&gt;
&lt;p&gt;Below, we will analyze this powerful combination using a real-world example.&lt;/p&gt;
&lt;p&gt;&lt;Banner
  title=&quot;Keep your monitoring data in Europe with AppSignal&quot;
  buttonText=&quot;Start your free trial&quot;
  buttonHref=&quot;https://appsignal.com/users/sign_up?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=data_sovereignty_how_to_keep_all_of_your_services_in_europe_appsignal_hatchbox_2026_04_25&amp;utm_content=banner_signup&quot;
  buttonGoal=&quot;data_sovereignty_how_to_keep_all_of_your_services_in_europe_appsignal_hatchbox_banner_signup&quot;
/&gt;&lt;/p&gt;
&lt;h2&gt;Practical Setup: A European-First Rails Stack&lt;/h2&gt;
&lt;p&gt;We’ll begin by setting up our Hatchbox account, provisioning a server, and deploying a Rails application. First, sign up at &lt;a href=&quot;https://app.hatchbox.io/users/sign_up&quot;&gt;Hatchbox&lt;/a&gt; and enter a payment method to start adding servers.&lt;/p&gt;
&lt;p&gt;Next, we have to create a new &lt;em&gt;cluster&lt;/em&gt;. In Hatchbox, a cluster is simply a group of servers where each one has a different role. That can be a web server, load balancer, database server, background processors, and so on.&lt;/p&gt;
&lt;p&gt;For the sake of this example, I will use a European option, Hetzner. However, like I’ve said, you can connect any provider via the &amp;quot;custom&amp;quot; option, as long as it can provision a server running an Ubuntu image for you. My second go-to European cloud provider is Scaleway, which also allows you to prepare a managed database and Redis services.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/new_cluster.png&quot; alt=&quot;Creating a new cluster on Hatchbox&quot; title=&quot;Creating a new cluster on Hatchbox&quot;/&gt;&lt;/p&gt;
&lt;p&gt;After setting up the cluster, we’re asked to provide an API token for our Hetzner account. To create one, navigate to a Hetzner project and click on Security. From there, create an API token with a meaningful description. Remember to give it read &lt;strong&gt;and&lt;/strong&gt; write access:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/api_token.png&quot; alt=&quot;Generating an API token from the Hetzner console&quot; title=&quot;Generating an API token from the Hetzner console&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now use it to connect your Hatchbox account to Hetzner:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/hatchbox_token.png&quot; alt=&quot;Connecting a Hetzner account to Hatchbox&quot; title=&quot;Connecting a Hetzner account to Hatchbox&quot;/&gt;&lt;/p&gt;
&lt;p&gt;After choosing a region, you&amp;#39;re ready to add a first server:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/hatchbox_cluster.png&quot; alt=&quot;Hatchbox cluster dashboard&quot; title=&quot;Hatchbox cluster dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;For this demo, we are going with a small CX 23 compute instance, which you can choose right inside this form. In other words, you don&amp;#39;t need to leave Hatchbox to manage resources in Hetzner.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/new_server.png&quot; alt=&quot;Creating a new server on Hetzner via Hatchbox’s interface&quot; title=&quot;Creating a new server on Hetzner via Hatchbox’s interface&quot;/&gt;&lt;/p&gt;
&lt;p&gt;As noted above, a server can have a couple of distinct roles. We&amp;#39;re going to activate four of them here, for demonstration purposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web server (this one is required)&lt;/li&gt;
&lt;li&gt;Cron server (also required for migrations during deployments)&lt;/li&gt;
&lt;li&gt;Background worker&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After we click &amp;quot;Create server&amp;quot;, Hatchbox will &lt;a href=&quot;https://hatchbox.relationkit.help/articles/21-how-are-hatchbox&quot;&gt;harden our server&lt;/a&gt; and install the necessary services and dependencies:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/hatchbox_log.png&quot; alt=&quot;Provisioning a server via Hatchbox&quot; title=&quot;Provisioning a server via Hatchbox&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The next step is to actually deploy an application. To do this, we need to create it within our Hatchbox cluster and give it a name:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/hatchbox_newapp.png&quot; alt=&quot;Creating a new app in Hatchbox&quot; title=&quot;Creating a new app in Hatchbox&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Then we&amp;#39;re asked to provide the Git repository the app should deploy from. I&amp;#39;ve already connected my Github account, so I can now simply specify the path.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/hb_repo.png&quot; alt=&quot;Connecting a Hatchbox app to a GitHub repository&quot; title=&quot;Connecting a Hatchbox app to a GitHub repository&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Once that is saved, we&amp;#39;ll have to provision the required PostgreSQL and Redis databases. We can do this by adding new unmanaged instances from the &amp;quot;Databases&amp;quot; tab. (For production, it&amp;#39;s advised to use a managed database service, but we&amp;#39;ll get to that in a second).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/hb_db.png&quot; alt=&quot;Creating an unmanaged PostgreSQL database on Hatchbox&quot; title=&quot;Creating an unmanaged PostgreSQL database on Hatchbox&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/hb_create_db.png&quot; alt=&quot;Creating an unmanaged Redis database on Hatchbox&quot; title=&quot;Creating an unmanaged Redis database on Hatchbox&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now I can conveniently click the &amp;quot;Deploy&amp;quot; button in the upper right corner, and Hatchbox will work its magic; that is, prepare the correct environment variables (&lt;code&gt;RAILS_ENV&lt;/code&gt;, &lt;code&gt;DATABASE_URL&lt;/code&gt; etc.), run the deploy script, and so on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aside:&lt;/strong&gt; You can also mimic Heroku-like behavior by enabling automatic deploys on &lt;code&gt;git push&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/hb_deploy.png&quot; alt=&quot;Enabling automatic deploys via git push&quot; title=&quot;Enabling automatic deploys via git push&quot;/&gt;&lt;/p&gt;
&lt;p&gt;What I am leaving out of this tutorial is the fact that you may want to connect your application to a domain or set up SSL. The former requires you to purchase a domain, add it to your Hatchbox app via &amp;quot;Domains &amp;amp; SSL,&amp;quot; and point the DNS records to the server. The latter, on the other hand, is more straightforward because Hatchbox uses &lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt;, which manages Let’s Encrypt certificates automatically.&lt;/p&gt;
&lt;p&gt;At the end of this process, you&amp;#39;ve got a Rails application running on European infrastructure. But your operation’s requirements likely don&amp;#39;t end here. You&amp;#39;ll want access to server and business metrics, error and log management, and the like. Luckily, &lt;a href=&quot;https://appsignal.com/&quot;&gt;AppSignal&lt;/a&gt; is a cost-effective Dutch provider that ticks all these boxes and is very easy to install.&lt;/p&gt;
&lt;p&gt;In your application, you only have to add this to your Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;quot;appsignal&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;bundle install&lt;/code&gt; it, and after &lt;a href=&quot;https://docs.appsignal.com/organization.html&quot;&gt;creating an organization in AppSignal&lt;/a&gt;, add a Ruby on Rails application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/appsignal_lang.png&quot; alt=&quot;Setting up the application on AppSignal&quot; title=&quot;Setting up the application on AppSignal&quot;/&gt;&lt;/p&gt;
&lt;p&gt;AppSignal will then provide you with the necessary installation steps:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/appsignal_ruby_gem.png&quot; alt=&quot;Instructions for installing AppSignal in your Ruby on Rails application&quot; title=&quot;Instructions for installing AppSignal in your Ruby on Rails application&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The CLI will guide you through the installation, and AppSignal will verify the success:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/appsignal_install.png&quot; alt=&quot;Verifying the AppSignal installation&quot; title=&quot;Verifying the AppSignal installation&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now that everything’s been set up, you can enjoy some peace of mind, courtesy of AppSignal. I recommend reading the &lt;a href=&quot;https://docs.appsignal.com/ruby.html&quot;&gt;docs&lt;/a&gt; for further instructions and configuration options.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/appsignal_start.png&quot; alt=&quot;App monitoring dashboard on AppSignal&quot; title=&quot;App monitoring dashboard on AppSignal&quot;/&gt;&lt;/p&gt;
&lt;p&gt;To complete your migration to European data services, consider extending your toolbox. Cloud providers like &lt;a href=&quot;https://www.scaleway.com/en/&quot;&gt;Scaleway&lt;/a&gt; or &lt;a href=&quot;https://www.ionos.de/&quot;&gt;IONOS&lt;/a&gt; offer a full suite of services, from managed databases and container registries to CDNs and object storage. For a more comprehensive list of alternatives, consult directories such as &lt;a href=&quot;https://european-alternatives.eu/&quot;&gt;European Alternatives&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Benefits and Drawbacks&lt;/h2&gt;
&lt;p&gt;The approach we’ve outlined in this article comes with several benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First of all, you’ll worry less about whether your application is GDPR-compliant. While there are other challenges to tackle in this domain, keeping your data in European territory makes them all easier to handle.&lt;/li&gt;
&lt;li&gt;Hatchbox gives you the flexibility of choosing your own cloud provider and instance configurations, while remaining your single point of contact for management.&lt;/li&gt;
&lt;li&gt;Deployment to Hatchbox is just as simple as pushing new releases to Heroku used to be.&lt;/li&gt;
&lt;li&gt;Enlisting AppSignal as your APM keeps the bulk of operational concerns manageable in a cost-effective and comprehensive way. Gone are the days when you needed separate services for uptime monitoring, log management, performance tracking, and error monitoring. It’s effectively an operations management one-stop shop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep in mind that there are some drawbacks, as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is a bit of DevOps overhead attached to Hatchbox if you ever need to customize your deploys or builds. Tasks like custom TLS certificates, installing OS packages, and similar can all be pulled off, but they will require a bit of manual work.&lt;/li&gt;
&lt;li&gt;You’ll be working with more than one company, so you’ll have to manage multiple billing/subscription plans and release cycles.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clearly, the benefits outweigh the downsides in this approach. All things considered, what’s not to love?&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You don&amp;#39;t need a DevOps team or complex PaaS to keep your data in Europe. This article has shown a simple and cost-effective way of deploying your Ruby on Rails application to a European data center. By using AppSignal, you can go a step further and ensure your application metrics and other monitoring data remain within domestic borders. And with agentic AI tools improving over time, achieving data sovereignty is bound to become even easier.&lt;/p&gt;
&lt;p&gt;Go ahead and try &lt;a href=&quot;https://appsignal.com/&quot;&gt;AppSignal&lt;/a&gt; and &lt;a href=&quot;https://hatchbox.io/&quot;&gt;Hatchbox&lt;/a&gt; for a fully European stack.&lt;/p&gt;
&lt;p&gt;Happy prompting!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Setting Up Server Monitoring for a Rails App on Hatchbox</title>
    <link rel="alternate" href="https://blog.appsignal.com/2026/04/30/setting-up-server-monitoring-for-a-rails-app-on-hatchbox.html"/>
    <id>https://blog.appsignal.com/2026/04/30/setting-up-server-monitoring-for-a-rails-app-on-hatchbox.html</id>
    <published>2026-04-30T00:00:00+00:00</published>
    <updated>2026-04-30T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Hatchbox makes deployment easy. Here&#039;s how to use AppSignal to monitor memory, CPU, disk, and load on your Rails app.</summary>
    <content type="html">&lt;p&gt;Owning your server stack shouldn&amp;#39;t be a source of anxiety. Unfortunately, it often is, especially if you only pay attention to the problems you can feel in your gut: Is the app running? Is it throwing exceptions? Does it seem fast enough? These are great intuitive measurements, but just as a doctor uses diagnostics to catch high blood pressure before it becomes a crisis, you need deeper visibility to detect memory leaks, CPU spikes, and disk consumption before they bring your project to a halt.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hatchbox.io/&quot;&gt;Hatchbox&lt;/a&gt; and &lt;a href=&quot;https://appsignal.com/&quot;&gt;AppSignal&lt;/a&gt; give you that &amp;quot;deeper visibility.&amp;quot; They simplify infrastructure management by replacing manual monitoring with automated, real-time feedback. Together, they transform complex data into actionable insights and make server management accessible to any developer. This gives you the diagnostic depth you’d expect from a much larger operation, without the overhead cost.&lt;/p&gt;
&lt;h2&gt;What AppSignal Captures at the Host Level&lt;/h2&gt;
&lt;p&gt;Hatchbox is built to coordinate and manage your &lt;a href=&quot;https://rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt; servers, providing high-level observability for your cluster. To offer greater insights, it has teamed up with AppSignal through a seamless integration. This partnership takes you from manual dashboard monitoring to automated instrumentation, historical trend analysis, alerting, and in-depth metrics all within the context of your application.&lt;/p&gt;
&lt;p&gt;When you add the &lt;a href=&quot;https://rubygems.org/gems/appsignal&quot;&gt;AppSignal gem&lt;/a&gt; to your app, you get access to both APM (Application Performance Monitoring) measurements and server-level instrumentation. This includes load average, CPU/memory usage, network traffic, and disk I/O. The table below breaks down how these two layers of visibility work together:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;Server-level&lt;/th&gt;
&lt;th&gt;App-level (APM)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Provider&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hatchbox/Host, AppSignal&lt;/td&gt;
&lt;td&gt;AppSignal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure &amp;amp; hardware&lt;/td&gt;
&lt;td&gt;Code &amp;amp; business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;quot;Is the server up?&amp;quot;&lt;/td&gt;
&lt;td&gt;&amp;quot;Is the &lt;code&gt;/checkout&lt;/code&gt; route slow?&amp;quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Load avg, memory %, disk I/O&lt;/td&gt;
&lt;td&gt;Throughput, error rates, traces&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Unlike other services, AppSignal doesn&amp;#39;t require a separate tool or daemon; everything is built into the AppSignal gem. The Hatchbox integration enables you to connect your stack in just two clicks, after which you can view real-time insights within Hatchbox or jump directly into AppSignal to debug.&lt;/p&gt;
&lt;h2&gt;Reading the Host Dashboard&lt;/h2&gt;
&lt;p&gt;To see the host-level measurements captured by AppSignal, you&amp;#39;ll need to navigate to the &lt;strong&gt;Host metrics&lt;/strong&gt; page first. You can find that navigation item under &lt;strong&gt;Host monitoring&lt;/strong&gt;. Once there, click the specific host you&amp;#39;re interested in. Alternatively, you can click the specific host listed under the &lt;strong&gt;Worst 10 hosts by&lt;/strong&gt; section within the &lt;strong&gt;Overview&lt;/strong&gt; dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/dashboard.png&quot; alt=&quot;Host metrics via the Dashboard&quot; title=&quot;Host metrics via the Dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Host metrics&lt;/strong&gt; dashboard displays several charts. These can be broken
down into groups: &lt;strong&gt;Memory&lt;/strong&gt;, &lt;strong&gt;CPU and load&lt;/strong&gt;, &lt;strong&gt;Disk usage&lt;/strong&gt;, and &lt;strong&gt;Network traffic&lt;/strong&gt;. Let&amp;#39;s take a closer look at each.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;a href=&quot;https://docs.appsignal.com/metrics/host-metrics.html&quot;&gt;Host metrics&lt;/a&gt; are only available for apps deployed to a server.&lt;/p&gt;
&lt;h3&gt;Memory&lt;/h3&gt;
&lt;p&gt;Ruby is a memory intensive language, and Rails is not the leanest framework.&lt;/p&gt;
&lt;p&gt;Even before it handles its first request, a base Rails application with standard boilerplate can easily consume 100 MB to 150 MB of RAM at boot. This makes memory utilization one of the primary measurements to keep an eye on.&lt;/p&gt;
&lt;p&gt;In general, a healthy server runs between 40% and 70% memory utilization (used / total * 100.0). When you’re looking for a &amp;quot;normal&amp;quot; memory picture for Hatchbox managed servers, don’t expect to see a flat line. It’s more of a waveform. You&amp;#39;ll see peaks during times of high traffic and valleys during low-usage periods. You&amp;#39;ll also notice regular drops in memory as Ruby&amp;#39;s garbage collector frees up unused memory.&lt;/p&gt;
&lt;p&gt;As you continue developing your app (and as you attract more users), you&amp;#39;ll notice &amp;quot;used memory&amp;quot; slowly start to increase. This is referred to as &amp;quot;drift,&amp;quot; and you won&amp;#39;t be able to see it unless you expand your time-frame to the 30-day view.&lt;/p&gt;
&lt;p&gt;This drift shouldn&amp;#39;t be confused with a memory leak. It’s caused by organic data growth or adding dependencies. Leaks, on the other hand, tend to look like a gradual increase in memory over a shorter time period. While memory usage may drop with traffic usage, the &amp;quot;floor&amp;quot; will continue to grow every day.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/memory-leak.png&quot; alt=&quot;Memory overview with imagined leak&quot; title=&quot;Memory overview with imagined leak&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Another thing to note: If you see memory climb steadily and then suddenly &amp;quot;crash&amp;quot; to a low point before climbing again, this may signal a process (like a &lt;a href=&quot;https://sidekiq.org/&quot;&gt;Sidekiq&lt;/a&gt; or &lt;a href=&quot;https://github.com/resque/resque&quot;&gt;Resque&lt;/a&gt; worker) crashing and getting restarted by the OS or Hatchbox after hitting a limit.&lt;/p&gt;
&lt;h3&gt;CPU and Load&lt;/h3&gt;
&lt;p&gt;It&amp;#39;s not uncommon to view CPU and load averages as the same thing, but they are actually not. A great way to look at these differences is with a doctor&amp;#39;s office analogy. CPU usage would be the speed at which a doctor is able to see patients. Similarly, load average could be imagined as the number of people sitting in the waiting room.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A load of 1.0 on a 1-core machine means the cashier is busy, but there is no line.&lt;/li&gt;
&lt;li&gt;A load of 2.0 on a 1-core machine means the cashier is busy &lt;em&gt;and&lt;/em&gt; there is another person waiting their turn.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Spikes in CPU and load don&amp;#39;t always indicate a problem. Often, they show a process kicking off. For example, it&amp;#39;s not uncommon for your CPU to max out during the &lt;code&gt;assets:precompile&lt;/code&gt; step of a deploy. Likewise, worker restarts may cause a jump in CPU usage and load as it retrieves and starts working on a new batch of jobs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/cpu-load.png&quot; alt=&quot;CPU and Load averages&quot; title=&quot;CPU and Load averages&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Disk Usage&lt;/h3&gt;
&lt;p&gt;When memory, CPU, or load averages spike, your users will experience degraded performance and slower response times. However, if disk usage spikes, they experience a cessation of all activities. Disk usage of 100% on your server means databases can&amp;#39;t write recovery logs, temp files can&amp;#39;t be created for uploads, and the operating system itself often locks up.&lt;/p&gt;
&lt;p&gt;When you&amp;#39;re alerted in the middle of the night because of a server crash, these are the three things to look for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Logs (the usual suspects):&lt;/strong&gt; Even when you&amp;#39;re rotating logs, a &amp;quot;noisy&amp;quot; app can generate gigabytes of text faster than the rotation script can clean it up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Temp files (&lt;code&gt;/tmp&lt;/code&gt;):&lt;/strong&gt; Image processing and file generation usually happen in the background. Processes such as Resque and Sidekiq can quickly fill up the &lt;code&gt;/tmp&lt;/code&gt; and leave no trace of a problem after the server restarts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database WAL segments:&lt;/strong&gt; Both Postgres and MySQL write changes to a WAL file before updating data. There are some instances where a replica loses connection and these segments pile up until the disk is full.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a rule of thumb, treat 80% disk usage as your warning signal to start investigating. Once you cross 95%, the operating system loses the overhead it needs for routine tasks, potentially leading to a silent yet total failure.&lt;/p&gt;
&lt;p&gt;&lt;Banner
  title=&quot;Is your Rails app slow?&quot;
  buttonText=&quot;Find out with AppSignal&quot;
  buttonHref=&quot;https://appsignal.com/users/sign_up?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=setting_up_server_monitoring_for_a_rails_app_on_hatchbox_2026_04_25&amp;utm_content=banner_signup&quot;
  buttonGoal=&quot;setting_up_server_monitoring_for_a_rails_app_on_hatchbox_banner_signup&quot;
/&gt;&lt;/p&gt;
&lt;h2&gt;Connecting Host Metrics to App Problems&lt;/h2&gt;
&lt;p&gt;A measurement by itself is just a number. For it to provide value and help you make good decisions, it needs to be correlated and compared to other data. When taken alone, measurements such as CPU %, memory usage, and disk usage are known as “vanity metrics.”&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vanity metrics might make you feel good, but they don’t change how you act. Actionable metrics change your behavior by helping you pick a course of action.&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://www.goodreads.com/book/show/16033602-lean-analytics&quot;&gt;Lean Analytics&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Although you can use the &lt;strong&gt;Host metrics&lt;/strong&gt; page to compare measurements, it&amp;#39;s worth your while to set up a &lt;a href=&quot;https://docs.appsignal.com/metrics/dashboards.html&quot;&gt;custom dashboard&lt;/a&gt; and display measurements either within the same chart or next to one another.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/dashboard-split.png&quot; alt=&quot;Host metrics - split view&quot; title=&quot;Host metrics - split view&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Below, you’ll find a metric correlation cheat sheet:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If you see...&lt;/th&gt;
&lt;th&gt;Check this Host Metric...&lt;/th&gt;
&lt;th&gt;Likely Culprit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;High response times / low CPU&lt;/td&gt;
&lt;td&gt;Disk I/O wait&lt;/td&gt;
&lt;td&gt;Database bottleneck or slow logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unpredictable &amp;quot;jittery&amp;quot; latency&lt;/td&gt;
&lt;td&gt;Swap usage&lt;/td&gt;
&lt;td&gt;Memory exhaustion / leaks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High request queueing&lt;/td&gt;
&lt;td&gt;Load average&lt;/td&gt;
&lt;td&gt;Undersized server (need more cores)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High CPU / low throughput&lt;/td&gt;
&lt;td&gt;CPU usage (per process)&lt;/td&gt;
&lt;td&gt;Inefficient code or background job &amp;quot;storms&amp;quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High response times and/or DB errors&lt;/td&gt;
&lt;td&gt;Disk usage&lt;/td&gt;
&lt;td&gt;Insufficient space for swapping or Write-Ahead Logs (WAL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU spikes&lt;/td&gt;
&lt;td&gt;Deployment marker&lt;/td&gt;
&lt;td&gt;Investigate if no deployment marker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Alerts Worth Setting Up&lt;/h2&gt;
&lt;p&gt;A dashboard is only useful while you&amp;#39;re looking at it. To stay ahead of issues, you need automated notifications. AppSignal’s &lt;a href=&quot;https://docs.appsignal.com/anomaly-detection&quot;&gt;Anomaly Detection&lt;/a&gt; handles this by alerting you the moment metrics exceed your set thresholds. Setting up these alerts is covered in &lt;a href=&quot;https://blog.appsignal.com/2020/02/19/how-to-monitor-your-host-metrics-automatically.html&quot;&gt;How to Monitor Your Host Metrics Automatically&lt;/a&gt;. For now, these are the triggers you&amp;#39;ll want defined for any Hatchbox setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disk usage:&lt;/strong&gt; You should set up a warning for 80% and a critical alert for 95%.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sustained memory usage:&lt;/strong&gt; Set this for 80% usage sustained for 5-10 minutes. If your app stays above that threshold, it&amp;#39;s likely &amp;quot;swapping.&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Load average anomaly:&lt;/strong&gt; Define this as a 15-minute average that exceeds your server&amp;#39;s core count + 1 (for example, set it for 3.0 on a 2-core server)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Monitoring your application&amp;#39;s performance without looking at the host is like checking a patient&amp;#39;s temperature but ignoring their pulse and breathing. APM tells you what is slow, but host metrics tell you why. Is it the disk at capacity? Is there a memory leak? Is the CPU undersized? Observability is about more than just keeping the lights on. It&amp;#39;s about understanding the environment your code lives in.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hatchbox.io/&quot;&gt;Hatchbox&lt;/a&gt; makes deployment easy, but the responsibility for server health still rests with you. Leveraging AppSignal’s host-level instrumentation lets you keep your finger on the pulse of both your application and the infrastructure hosting it. However, visibility is only the first half of the equation; you cannot spend your entire day watching graphs. By setting up alerts, you move from reactively “firefighting” to proactively managing your stack.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Monitoring Sidekiq Job Performance with AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2026/04/28/monitoring-sidekiq-job-performance-with-appsignal.html"/>
    <id>https://blog.appsignal.com/2026/04/28/monitoring-sidekiq-job-performance-with-appsignal.html</id>
    <published>2026-04-28T00:00:00+00:00</published>
    <updated>2026-04-28T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Sidekiq jobs fail quietly. Here&#039;s how to catch slow jobs, retry storms, and queue backlogs with AppSignal before your users notice.</summary>
    <content type="html">&lt;p&gt;When my &lt;a href=&quot;https://sidekiq.org/&quot;&gt;Sidekiq&lt;/a&gt; job starts failing or slowing down, I often feel frustrated, especially if I don’t know how to fix it.&lt;/p&gt;
&lt;p&gt;If you’re using Sidekiq to run your background jobs, you know what I’m talking about. It’s a vital element of your stack, handling everything from data exports to password reset requests. It runs silently in the background, and most of the time, you’re not even giving it a second thought.&lt;/p&gt;
&lt;p&gt;However, when things go wrong, like pages loading slowly, emails never being sent, or exports failing, the impact can be huge. Getting to the bottom of the issue can be like finding a needle in a haystack.&lt;/p&gt;
&lt;p&gt;That’s where &lt;a href=&quot;https://appsignal.com/&quot;&gt;AppSignal&lt;/a&gt; comes in. It can monitor your Sidekiq jobs, alert you when things go wrong, and give you all the context you need to fix and rerun failed jobs without having to spend hours debugging or dealing with customer complaints.&lt;/p&gt;
&lt;p&gt;In this tutorial, you will learn how to monitor your Sidekiq jobs’ performance with AppSignal. To follow along, you need to already have a Ruby application with some Sidekiq jobs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Even though we’ll be focusing on Sidekiq jobs here, it’s worth noting that AppSignal can monitor all aspects of your &lt;a href=&quot;https://rubyonrails.org/&quot;&gt;Rails&lt;/a&gt; application.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Sidekiq jobs live in a different part of the web application, which can make issues hard to spot.&lt;/p&gt;
&lt;p&gt;When a controller action fails, you get an alert within seconds. But what happens when a background job silently retries four times and gives up? You usually find out only when a user emails support asking where their CSV export went. These jobs slowly start piling up in the queue and degrading in performance without any obvious signals.&lt;/p&gt;
&lt;p&gt;When Sidekiq jobs fail, you might see things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Queues backing up and throughput collapsing&lt;/li&gt;
&lt;li&gt;Vital jobs silently exhausting their retries and dying&lt;/li&gt;
&lt;li&gt;Exceptions getting swallowed with no trace in your logs&lt;/li&gt;
&lt;li&gt;Users feeling the impact long before you realize something is happening&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What You Get Out of the Box&lt;/h2&gt;
&lt;p&gt;A great thing about AppSignal is how easy it is to get started with. You don’t need a bunch of customization. Just add the &lt;code&gt;appsignal&lt;/code&gt; gem, and you’re good to go.&lt;/p&gt;
&lt;p&gt;From that point on, every Sidekiq job execution is tracked automatically. You get insights like job duration, queue wait time, and many other instrumented events that fire within the job itself. In practice, this means you get a structured performance data and error visibility of your jobs immediately after AppSignal has been activated.&lt;/p&gt;
&lt;p&gt;If you’d like a full breakdown of what is captured and how the integration works under the hood, the &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/sidekiq.html#automated-dashboard&quot;&gt;AppSignal Sidekiq documentation&lt;/a&gt; is worth a look. However, here’s a categorized list of what you get out of the box:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Job duration and throughput per worker&lt;/strong&gt;, or how long each job takes and how many are being completed;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Queue length and latency&lt;/strong&gt;, or how many jobs are waiting and how long they&amp;#39;ve been sitting there;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Job status per queue&lt;/strong&gt;, which is a breakdown of succeeded, failed, and retried jobs across every queue;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Job count&lt;/strong&gt;, which is the number of workers and processes that are active at any given time;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection count&lt;/strong&gt;, or the number of open Sidekiq connections;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redis memory usage&lt;/strong&gt;, which refers to both allocated and RSS memory consumed by your Redis instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Three Signals That Matter&lt;/h2&gt;
&lt;p&gt;Instead of just going through different random job performance metrics, we will focus on the three most important ones:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Job duration&lt;/strong&gt; lets you spot your slowest jobs before they become outages. A job that averaged 2 seconds and now averages 12 is a problem you want to catch early.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Queue latency&lt;/strong&gt; has to do with the time a job spends sitting in the queue waiting to be picked up by a worker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error rate and retry count&lt;/strong&gt; provides a comparison between the error rate and the retry count.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You will learn how to find those metrics in the AppSignal dashboard and why they actually matter. But, before we go ahead with this section, it’s worth mentioning that Sidekiq provides its own dashboard, as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/sidekiq-dashboard.png&quot; alt=&quot;The Sidekiq dashboard&quot; title=&quot;The Sidekiq dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Still, as you can see from the image below, this dashboard doesn’t give you the full picture. It misses some important metrics and additional insights that are available on the AppSignal dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/metrics-db.png&quot; alt=&quot;Metrics on the AppSignal dashboard&quot; title=&quot;Metrics on the AppSignal dashboard&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Adding AppSignal to the Ruby/Sidekiq Project&lt;/h3&gt;
&lt;p&gt;Assuming you already have a project set up (as stated in the introduction), these steps will guide you through how you can add AppSignal to your Ruby project. And since AppSignal automatically tracks the Sidekiq metrics, there isn’t much configuration necessary on your end.&lt;/p&gt;
&lt;p&gt;Start by adding &lt;code&gt;&amp;#39;appsignal&amp;#39;&lt;/code&gt; to your Gemfile, then:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, sign in to AppSignal and follow the steps below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new application using the “&lt;strong&gt;Add app&lt;/strong&gt;” button.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Ruby &amp;amp; Rails&lt;/strong&gt; as the language.&lt;/li&gt;
&lt;li&gt;Fill in the app name.&lt;/li&gt;
&lt;li&gt;Install &lt;strong&gt;Rails&lt;/strong&gt; for your framework.&lt;/li&gt;
&lt;li&gt;Run the CLI installer provided in the root of your project and follow the instructions. The installer should look like this &lt;code&gt;bundle exec appsignal install YOUR_PUSH_API_KEY&lt;/code&gt; .&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This creates &lt;code&gt;config/appsignal.yml&lt;/code&gt; automatically. Here’s what mine looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# AppSignal Ruby gem configuration
# Visit our documentation for a list of all available configuration options.
# https://docs.appsignal.com/ruby/configuration/options.html
Appsignal.configure do |config|
  config.activate_if_environment(&amp;quot;development&amp;quot;, &amp;quot;production&amp;quot;, &amp;quot;staging&amp;quot;)
  config.name = &amp;quot;Sidekiq&amp;quot;
  # The application&amp;#39;s Push API key
  # We recommend removing this line and setting this option with the
  # APPSIGNAL_PUSH_API_KEY environment variable instead.
  # https://docs.appsignal.com/ruby/configuration/options.html#option-push_api_key
  config.push_api_key = &amp;quot;APPSIGNAL_PUSH_API_KEY&amp;quot;

  # Configure actions that should not be monitored by AppSignal.
  # For more information see our docs:
  # https://docs.appsignal.com/ruby/configuration/ignore-actions.html
  # config.ignore_actions &amp;lt;&amp;lt; &amp;quot;ApplicationController#isup&amp;quot;

  # Configure errors that should not be recorded by AppSignal.
  # For more information see our docs:
  # https://docs.appsignal.com/ruby/configuration/ignore-errors.html
  # config.ignore_errors &amp;lt;&amp;lt; &amp;quot;MyCustomError&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;config/boot.rb&lt;/code&gt; (the file that contains the setup for processing jobs) to load AppSignal before Sidekiq:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;appsignal&amp;#39;
require &amp;#39;appsignal/integrations/sidekiq&amp;#39;

Appsignal.start

require &amp;#39;sidekiq&amp;#39;
require &amp;#39;app/jobs/welcome_email_job&amp;#39;
require &amp;#39;app/jobs/order_processing_job&amp;#39;
require &amp;#39;app/jobs/log_cleanup_job&amp;#39;

Sidekiq.configure_server do |config|
  config.redis = { url: ENV.fetch(&amp;#39;REDIS_URL&amp;#39;, &amp;#39;redis://localhost:6379/0&amp;#39;) }
end

Sidekiq.configure_client do |config|
  config.redis = { url: ENV.fetch(&amp;#39;REDIS_URL&amp;#39;, &amp;#39;redis://localhost:6379/0&amp;#39;) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can run Sidekiq as normal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bundle exec sidekiq -r ./config/boot.rb -C ./config/sidekiq.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AppSignal will automatically track overall job status, job status per queue, duration per worker, and &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/sidekiq.html#sample-breakdown:~:text=into%20the%20details.-,Automated%20dashboard,-When%20AppSignal%20receives&quot;&gt;much more&lt;/a&gt;, which is enough for what we want to accomplish in this article.&lt;/p&gt;
&lt;p&gt;&lt;Banner
  title=&quot;Monitor your Sidekiq jobs with AppSignal&quot;
  buttonText=&quot;Start monitoring for free&quot;
  buttonHref=&quot;https://appsignal.com/users/sign_up?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=monitoring_sidekiq_job_performance_appsignal_2026_04_18&amp;utm_content=banner_signup&quot;
  buttonGoal=&quot;monitoring_sidekiq_job_performance_appsignal_banner_signup&quot;
/&gt;&lt;/p&gt;
&lt;h3&gt;Job Duration&lt;/h3&gt;
&lt;p&gt;A job that used to take 2 seconds and now averages 12 is your next outage waiting to happen. Any time a job starts taking longer than expected to execute, it’s a sign that something’s off.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A database query inside the job is hitting an unindexed table as data grows;&lt;/li&gt;
&lt;li&gt;An external API the job depends on has slowed down or started timing out;&lt;/li&gt;
&lt;li&gt;A job that once processed 10 records is now processing 10,000 with no change to the underlying code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You want to catch this early because that way, you’ll be fixing a performance issue instead of fighting a full-blown incident. You can easily spot this metric on &lt;strong&gt;Duration per worker&lt;/strong&gt; graph on the dashboard, which shows the amount of time that it took jobs to execute, grouped by namespace and action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/duration-perweek.png&quot; alt=&quot;Duration per worker metric on AppSignal dashboard &quot; title=&quot;Duration per worker metric on AppSignal dashboard&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Queue Latency&lt;/h3&gt;
&lt;p&gt;Latency is the time between when a job is enqueued and when a worker actually picks it up. It shows you how long a job has been sitting around before it starts.&lt;/p&gt;
&lt;p&gt;This makes queue latency your earliest warning for backlog problems. It starts climbing &lt;strong&gt;before&lt;/strong&gt; jobs begin failing or erroring, which gives you a chance to catch issues early. If you spot a spike on the AppSignal Sidekiq dashboard, it means your workers can&amp;#39;t keep up with the current load, even if every job that runs is technically succeeding.&lt;/p&gt;
&lt;p&gt;In the image below, you can see that there is no latency. However, if you have an application used by multiple users at a time, you’ll probably see some readings in the &lt;strong&gt;Queue latency&lt;/strong&gt; chart.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/queue-lat.png&quot; alt=&quot;Queue latency chart on AppSignal dashboard &quot; title=&quot;Queue latency chart on AppSignal dashboard&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Error Rate vs. Retry Count&lt;/h3&gt;
&lt;p&gt;Sidekiq retries can easily mask failures. A job that errors five times before actually succeeding looks fine from the outside. That’s where AppSignal provides a clearer picture by showing the actual error count and backtraces.&lt;/p&gt;
&lt;p&gt;Without that visibility, you can end up missing real issues, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a payment processing job that retries silently after hitting a rate limit and fails repeatedly, which can cause the charge to never go through&lt;/li&gt;
&lt;li&gt;a CSV export job that fails three times due to a sudden memory spike before it finally succeeds, adding delays that the user has to deal with&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;SendWebhookJob&lt;/code&gt; to a partner API that fails three times with a 503 error before it gets a 200 response. Sidekiq marks it as successful; meanwhile, your partner is experiencing delays in data, and their support team is filing tickets with yours. Luckily, AppSignal shows the 503 errors and identifies which partner endpoint is having issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sidekiq&amp;#39;s built-in retry mechanism is great for resilience, but it can be a hindrance when monitoring your application because it can lead to you missing a potential problem. The job we’ve mentioned above that errors 5 times before eventually succeeding still shows up as zero failures in the Sidekiq web UI, and it just disappears from the queue as &amp;quot;done.&amp;quot; AppSignal captures every failed attempt, its backtrace, and the exact job arguments that triggered it. Without this, you can&amp;#39;t really know for sure how reliable your jobs are.&lt;/p&gt;
&lt;p&gt;You can find these errors in the Dashboard overview.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/error-rate.png&quot; alt=&quot;Error rate on AppSignal dashboard&quot; title=&quot;Error rate on AppSignal dashboard&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Get Alerted of Failures Before Users Do&lt;/h2&gt;
&lt;p&gt;You don’t want to be sitting there, watching the metrics and searching for issues manually. That simply wouldn’t be productive. This is exactly where the Anomaly detection feature comes in.&lt;/p&gt;
&lt;p&gt;It lets you set triggers that send notifications when a metric crosses a defined threshold. In the context of Sidekiq jobs, it means getting an alert the moment queue latency spikes or a job class starts returning errors at an unusual rate, ideally before any user opens a support ticket.&lt;/p&gt;
&lt;p&gt;The detection runs on a minute-by-minute basis, so the gap between a problem starting and you knowing about it is kept to a minimum.&lt;/p&gt;
&lt;p&gt;You can configure Triggers with a variety of metrics, including error count, throughput, error rate increases, slow actions, and queue time.&lt;/p&gt;
&lt;p&gt;For the sake of this tutorial, we will set up triggers on the last three.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/trigger-det.png&quot; alt=&quot;Steps to add triggers for anomaly detection&quot; title=&quot;Steps to add triggers for anomaly detection&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Before you do that, though, you need to create triggers, the functionality that executes these alerts.&lt;/p&gt;
&lt;p&gt;Go to your app navigation and click on the &lt;strong&gt;Anomaly detection&lt;/strong&gt; tab. Choose &lt;strong&gt;Triggers&lt;/strong&gt;, then click the button to add a new trigger. Use the image above as a guide.&lt;/p&gt;
&lt;h3&gt;Slow Actions&lt;/h3&gt;
&lt;p&gt;The slow action trigger fires when the mean duration of a specific job class exceeds a time limit you’ve defined. With it, you can catch performance regressions, like when a job that used to run in 2 seconds suddenly starts running in 12 for one reason or another.&lt;/p&gt;
&lt;p&gt;To set this up, go to &lt;strong&gt;Slow actions&lt;/strong&gt; tab under &lt;strong&gt;Actions&lt;/strong&gt;. Choose the following options and save the trigger:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Namespace:&lt;/strong&gt; background&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alert me when the:&lt;/strong&gt; Mean&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is above (ms): 2000&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Tick &lt;strong&gt;Default email notifier&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the mean duration of that job crosses 3 seconds, AppSignal sends an alert with the job class name, the current mean, and a direct link to its performance page.&lt;/p&gt;
&lt;h3&gt;Error Rate Increase&lt;/h3&gt;
&lt;p&gt;The error rate trigger is activated when the percentage of job executions that raise exceptions goes above your defined limit. Without this kind of metric, a job could start failing 20% of the time and still go unnoticed for hours because Sidekiq retries keep the queue visually clean.&lt;/p&gt;
&lt;p&gt;To set this up, click &lt;strong&gt;Error rate&lt;/strong&gt; under &lt;strong&gt;Actions&lt;/strong&gt;. Then select the following options, and save the trigger:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Namespace:&lt;/strong&gt; Background&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alert me when error rate is above:&lt;/strong&gt; more than, 5%&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Default email notifier&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the 5% error rate threshold set here, you get alerted after roughly 1 in 20 job executions fail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You will need to set a lower threshold for payment processing or sensitive jobs where even a 1% failure rate is unacceptable.&lt;/p&gt;
&lt;h3&gt;Queue Time&lt;/h3&gt;
&lt;p&gt;The queue time trigger fires when the latency between enqueuing a job and it being executed exceeds your threshold. This is the earliest signal that there is a backlog issue because latency rises long before jobs begin to fail or workers start to crash.&lt;/p&gt;
&lt;p&gt;To set this up, click &lt;strong&gt;Queue time&lt;/strong&gt; under &lt;strong&gt;Actions&lt;/strong&gt;. Then select the following options and save the trigger:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Namespace:&lt;/strong&gt; Background&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alert me when the:&lt;/strong&gt; Mean&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is above:&lt;/strong&gt; more than, &lt;code&gt;30000ms&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Default email notifier&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this configuration, you will receive an email whenever the queue time on any monitored queue crosses its threshold. It will include all the information you need in order to identify the defective job.&lt;/p&gt;
&lt;p&gt;Here’s an example of what this email looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2026-04/alerts.png&quot; alt=&quot;Getting alerted with anomaly detection&quot; title=&quot;Getting alerted with anomaly detection&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Wrap-Up&lt;/h2&gt;
&lt;p&gt;In this article, we started by explaining how AppSignal can help you monitor your Sidekiq job performance. Then, we demonstrated how you can set it up in a Ruby project and explored the three key signals that matter: job duration, queue latency, and error rate versus retry count. Finally, you learned how to set triggers and alerts for slow actions, error rate increases, and queue time thresholds.&lt;/p&gt;
&lt;p&gt;This is just one of many ways AppSignal helps you build stable, scalable applications. If you want to create a more rounded observability setup for your background jobs, you can build on the knowledge gained here by exploring &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;AppSignal&amp;#39;s other monitoring features&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Ruby Parsing with Prism</title>
    <link rel="alternate" href="https://blog.appsignal.com/2026/01/07/an-introduction-to-ruby-parsing-with-prism.html"/>
    <id>https://blog.appsignal.com/2026/01/07/an-introduction-to-ruby-parsing-with-prism.html</id>
    <published>2026-01-07T00:00:00+00:00</published>
    <updated>2026-01-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Prism is here to change our lives as Ruby developers: find out how.</summary>
    <content type="html">&lt;p&gt;You might have heard about Prism, the new Ruby parser. Perhaps you&amp;#39;ve heard it&amp;#39;s faster, more reliable, and more powerful than what we had before. Or maybe you never took a compilers class and aren&amp;#39;t sure about what this &lt;em&gt;actually&lt;/em&gt; means.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m here to tell you all about it, and how it&amp;#39;s changing our lives as Ruby developers. Today, I want to take you from square one to writing your first transpiler.&lt;/p&gt;
&lt;h2&gt;Interpreters 101&lt;/h2&gt;
&lt;p&gt;Before we begin our journey, let&amp;#39;s start with the basics of how an interpreter works, so we&amp;#39;re all on the same page. Interpreting a programming language usually involves three main steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Tokenizing input (a.k.a. lexing)&lt;/strong&gt;: Breaking the input text into a list of meaningful tokens. That&amp;#39;s like converting your code into something like this:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;tokens = [
  { type: :integer,    literal: &amp;quot;0&amp;quot;,      value: 0,   line: 1 },
  { type: :operator,   literal: &amp;quot;+&amp;quot;,      value: nil, line: 1 },
  { type: :integer,    literal: &amp;quot;1&amp;quot;,      value: 1,   line: 1 },
  { type: :keyword,    literal: &amp;quot;if&amp;quot;,     value: 1,   line: 1 },
  { type: :identifier, literal: &amp;quot;admin?&amp;quot;, value: nil, line: 1 }
  # ...
]
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Parsing&lt;/strong&gt;: Analyzing the tokens to understand the program structure (what to do and in which order) and building a data representation that holds that information (known as an Abstract Syntax Tree). For example:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt; ast = {
   node_type: :binary,
   operation: &amp;quot;+&amp;quot;,
   left: {
     node_type: :number,
     value: 0
   },
   right: {
     node_type: :number,
     value: 1
   },
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Evaluating&lt;/strong&gt;: Executing the parsed input and producing an output. This is where your code &lt;em&gt;actually&lt;/em&gt; runs.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For a deeper dive into this topic, I recommend the &lt;a href=&quot;https://craftinginterpreters.com/&quot;&gt;Crafting Interpreters book&lt;/a&gt; or &lt;a href=&quot;https://www.youtube.com/watch?v=ozayFBNKO2M&quot;&gt;my RailsConf talk&lt;/a&gt; on the subject.&lt;/p&gt;
&lt;p&gt;Now let&amp;#39;s dive into what Prism can do and how it helps with parsing.&lt;/p&gt;
&lt;h2&gt;Why Is Prism Useful for Ruby Parsing?&lt;/h2&gt;
&lt;p&gt;Ruby historically used a parser called &lt;a href=&quot;https://github.com/ruby/ruby/blob/v3_5_0_preview1/parse.y&quot;&gt;parse.y&lt;/a&gt;, built with &lt;a href=&quot;https://www.gnu.org/software/bison/manual/html_node/Yacc.html&quot;&gt;Yacc&lt;/a&gt;. The catch? It was made specifically for CRuby, forcing other Ruby implementations (like JRuby and TruffleRuby) to create their own parsers from scratch.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s why tools like RuboCop, code editors, and even other Ruby implementations often lagged behind or had incompatibilities with newer Ruby syntax. Developers building Ruby analysis tools had to write their own parsers too, spawning projects like &lt;a href=&quot;https://github.com/whitequark/parser&quot;&gt;&lt;code&gt;whitequark/parser&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/seattlerb/ruby_parser&quot;&gt;&lt;code&gt;ruby_parser&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Prism solves this by becoming the &lt;em&gt;de facto&lt;/em&gt; parser for all Ruby tools and implementations. And &lt;a href=&quot;https://github.com/ruby/prism?tab=readme-ov-file#examples&quot;&gt;it&amp;#39;s working&lt;/a&gt;: it is now used in CRuby, JRuby, TruffleRuby, Rails, RuboCop, and more.&lt;/p&gt;
&lt;p&gt;Okay, enough talk. Prism can lex and parse Ruby, which allows us to build fun things. How about we build a ✨ &lt;em&gt;transpiler&lt;/em&gt; ✨ with it? Wait! Don&amp;#39;t go away. This will be simple. &lt;em&gt;I promise&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Your First Transpiler&lt;/h2&gt;
&lt;p&gt;The full code for the examples in this post is available &lt;a href=&quot;https://github.com/MatheusRich/introduction-to-prism&quot;&gt;in this repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, we&amp;#39;ll build a tool that converts our Ruby code into &lt;a href=&quot;https://github.com/searls/emoruby&quot;&gt;Emoruby&lt;/a&gt;. If you have never seen Emoruby, this is what it looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;📋 ❤️
  🔜 👋
    👀 💬😃 🌏💬
  🔚
🔚

❤️▪️🐣▪️👋
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Equivalent to this in Ruby:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Heart
  def wave
    puts &amp;quot;smiley earth_asia&amp;quot;
  end
end

Heart.new.wave
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ready? Ok, here we go. We&amp;#39;ll need a Gemfile to install &lt;code&gt;emoruby&lt;/code&gt; and &lt;code&gt;prism&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;source &amp;#39;https://rubygems.org&amp;#39;

gem &amp;quot;emoruby&amp;quot;, git: &amp;quot;https://github.com/searls/emoruby&amp;quot;, branch: &amp;quot;master&amp;quot;
gem &amp;quot;prism&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After &lt;code&gt;bundle install&lt;/code&gt;ing, let&amp;#39;s create the entry point for our transpiler — the &lt;code&gt;Rubyemo.ruby_to_emoji&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;require &amp;#39;emoruby&amp;#39;
require &amp;#39;prism&amp;#39;

module Rubyemo
  extend self

  def ruby_to_emoji(src)
    tokenize(src)
  end

  private

  def tokenize(src)
    result = Prism.lex(src)
    raise &amp;quot;Invalid Ruby code&amp;quot; if result.errors.any?

    result.value.map(&amp;amp;:first)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For now, it only tokenizes the input with Prism. The &lt;a href=&quot;https://ruby.github.io/prism/rb/Prism.html#method-c-lex&quot;&gt;&lt;code&gt;lex&lt;/code&gt; method&lt;/a&gt; returns a result object that contains either the tokens or errors. If the source code contains invalid Ruby, we&amp;#39;ll just raise an exception.&lt;/p&gt;
&lt;p&gt;Then we get the value attribute, which contains a list of &lt;a href=&quot;https://ruby.github.io/prism/rb/Prism/Token.html&quot;&gt;tokens&lt;/a&gt; and some other stuff. We only care about the tokens, so we grab them with &lt;code&gt;map(&amp;amp;:first)&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Emojify the Ruby Tokens&lt;/h3&gt;
&lt;p&gt;Now onto the fun part. How do we emojify our Ruby tokens? Emoruby has a very simple design, so we can basically replace tokens one by one with an emoji alternative:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module Rubyemo
  extend self

  def ruby_to_emoji(src)
    tokenize(src)
      .then { emojify it }
      .join
  end

  private

  def emojify(tokens)
    tokens.filter_map do |token|
      next if token.type == :EOF

      token_to_emoji(token)
    end
  end

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To do that mapping, we&amp;#39;ll use &lt;a href=&quot;https://github.com/searls/emoruby/blob/8869e595d0cb7a7d3b06589f6e0bc19d4fd7b6ee/config/translations.yml&quot;&gt;Emoruby&amp;#39;s translation file&lt;/a&gt; and &lt;a href=&quot;https://rubygems.org/gems/emoji_data&quot;&gt;EmojiData&lt;/a&gt; to translate token values to emojis by name.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module Rubyemo
  # ...

  TRANSLATIONS = Emoruby::ConvertsRubyToEmoji::TRANSLATIONS

  def token_to_emoji(token)
    case token
    in {type: :COMMENT, value:}
      &amp;quot;💭#{value[1..]}&amp;quot;
    in {type: :IGNORED_NEWLINE | :NEWLINE}
      &amp;quot;\n&amp;quot;
    else
      token.value.to_s.split.map do |part|
        TRANSLATIONS[part] || EmojiData.from_short_name(part)&amp;amp;.to_s || part
      end.join(&amp;quot; &amp;quot;)
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a token has no mapping, we leave it as-is so the code still runs. &lt;a href=&quot;https://docs.ruby-lang.org/en/master/syntax/pattern_matching_rdoc.html&quot;&gt;Pattern matching&lt;/a&gt; is pretty handy here to match a particular type and grab its value at the same time.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;And... that&amp;#39;s it! Let&amp;#39;s see our little transpiler in action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;emo = Rubyemo.ruby_to_emoji(&amp;#39;puts &amp;quot;Hello, world!&amp;quot;&amp;#39;)
puts emo # 👀💬Hello, world!💬

Emoruby.eval(emo) # Hello, world!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Fix Indentation and Spacing&lt;/h3&gt;
&lt;p&gt;You might not have noticed, but our code doesn&amp;#39;t handle indentation or spacing. See how the space between &lt;code&gt;puts&lt;/code&gt; and the opening quote got lost? We can do better than this, so let&amp;#39;s handle that. Luckily, the &lt;code&gt;Prism::Token&lt;/code&gt; instances have &lt;a href=&quot;https://ruby.github.io/prism/rb/Prism/Location.html&quot;&gt;information about their location&lt;/a&gt;, including the start/end line and column.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s change &lt;code&gt;emojify&lt;/code&gt; to this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def emojify(tokens)
  previous_line, previous_column = 1, 0

  tokens.filter_map do |token|
    next if token.type == :EOF

    emoji = token_to_emoji(token)
    indentation, previous_line, previous_column = indentation_for(
      token,
      previous_line,
      previous_column
    )

    indentation + emoji
  end

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And implement &lt;code&gt;indentation_for&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def indentation_for(token, previous_line, previous_column)
  if token.location.start_line != previous_line
    previous_line = token.location.start_line
    previous_column = 0
  end
  indentation = &amp;quot; &amp;quot; * (token.location.start_column - previous_column)
  previous_column = token.location.end_column

  [indentation, previous_line, previous_column]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Try emojifying this now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;ruby = &amp;lt;&amp;lt;~RUBY
  class Heart
    public def jeans
      puts &amp;quot;purse&amp;quot;
    end

    protected def shirt
      puts &amp;quot;yellow_heart&amp;quot;
    end

    private def wave
      puts &amp;quot;smiley earth_asia&amp;quot;
    end
  end

  Heart.new.wave
RUBY

puts Rubyemo.ruby_to_emoji(ruby)
# 📋 ❤️
#   🔓 🔜 👖
#     👀 💬👛💬
#   🔚
#
#   🔒 🔜 👕
#     👀 💬💛💬
#   🔚
#
#   ⛔️ 🔜 👋
#     👀 💬😃 🌏💬
#   🔚
# 🔚
#
# ❤️▪️🐣▪️👋
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done — for real.&lt;/p&gt;
&lt;h2&gt;Onto Parsing with Prism for Ruby&lt;/h2&gt;
&lt;p&gt;Rubyemo helped us to learn Prism lexing, but we didn&amp;#39;t need any parsing. Let&amp;#39;s try another example. Let&amp;#39;s say you learned about &lt;a href=&quot;https://docs.ruby-lang.org/en/3.2/Data.html&quot;&gt;Ruby 3.2&amp;#39;s &lt;code&gt;Data&lt;/code&gt; class&lt;/a&gt; and you want to rewrite your old structs with it. Why spend 10 minutes doing it manually when you can write a script in one hour that does it?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If you think this sounds like a RuboCop cop, you&amp;#39;re 100% correct! You could turn this into &lt;a href=&quot;https://thoughtbot.com/blog/rubocop-custom-cops-for-custom-needs&quot;&gt;a custom cop&lt;/a&gt; if you wanted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While Prism &lt;a href=&quot;https://ruby.github.io/prism/rb/Prism.html#method-c-parse&quot;&gt;has a method to parse code&lt;/a&gt;, for this, we&amp;#39;ll use its &lt;code&gt;Visitor&lt;/code&gt; class instead. The visitor design pattern allows us to add new operations to objects (in this case, the Prism AST nodes) without changing their classes. In other words, for each node type it finds, the &lt;code&gt;Visitor&lt;/code&gt; class will call a method, and we decide what to do in that situation.&lt;/p&gt;
&lt;p&gt;We need to act when we find &lt;code&gt;Struct.new&lt;/code&gt;, so that&amp;#39;s a method call, which in Prism is identified by the &lt;code&gt;CallNode&lt;/code&gt; type. Let&amp;#39;s filter those:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;require &amp;quot;prism&amp;quot;

class StructToData &amp;lt; Prism::Visitor
  Fix = Data.define(:location, :replacement)

  attr_reader :fixes

  def initialize(src)
    super()
    @src = src
    @fixes = []
  end

  def visit_call_node(node)
    if struct_new?(node)
      # todo
    end

    super
  end

  private

  def struct_new?(node)
    node.name == :new &amp;amp;&amp;amp;
      node.receiver.is_a?(Prism::ConstantReadNode) &amp;amp;&amp;amp;
      node.receiver.name == :Struct
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that we need to call &lt;code&gt;super&lt;/code&gt; on the visit methods, which makes Prism keep walking inner nodes (like the body of a class definition).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now we need to collect the struct arguments and build our fix object with the replacement code. We&amp;#39;ll also skip named structs, as those don&amp;#39;t map 1:1 to &lt;code&gt;Data&lt;/code&gt; classes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# ...
def visit_call_node(node)
  if struct_new?(node) &amp;amp;&amp;amp; !named_struct?(node)
    members = struct_members(node)
    replacement = build_replacement(members, node.block)
    @fixes &amp;lt;&amp;lt; Fix.new(node.location, replacement)
  end

  super
end

private

# ...

# skips interpolated symbols for simplicity
def struct_members(node)
  (node.arguments&amp;amp;.arguments || [])
    .take_while { it.is_a?(Prism::SymbolNode) }
    .map(&amp;amp;:slice)
end

def named_struct?(node)
  (node.arguments&amp;amp;.arguments || [])
    .first
    .is_a?(Prism::StringNode)
end

def build_replacement(node_members, node_block)
  call = &amp;quot;Data.define(#{node_members.join(&amp;quot;, &amp;quot;)})&amp;quot;
  if node_block
    call += &amp;quot; #{node_block.slice}&amp;quot;
  end
  call
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Make Fixes with a Method&lt;/h3&gt;
&lt;p&gt;Now let&amp;#39;s write a method to apply the fixes. We&amp;#39;ll process them in ascending order of start position, so offsets remain correct as we build the new source.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class StructToData &amp;lt; Prism::Visitor
  #...
  def self.rewrite(source)
    ast = Prism.parse(source)
    return [source, []] unless ast.success?

    v = new(source)
    v.visit(ast.value)
    [v.apply_fixes, v.fixes]
  end

  # ...

  private

  def apply_fixes
    return @src if @fixes.empty?

    pos = 0
    out = +&amp;quot;&amp;quot;
    @fixes.sort_by { it.location.start_offset }.each do |fix|
      out &amp;lt;&amp;lt; @src.byteslice(pos...fix.location.start_offset)
      out &amp;lt;&amp;lt; fix.replacement
      pos = fix.location.end_offset
    end
    out &amp;lt;&amp;lt; @src.byteslice(pos..-1)

    out
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We have to use &lt;code&gt;byteslice&lt;/code&gt; because Prism offsets are in bytes, while replacing using something like &lt;code&gt;String#[]=&lt;/code&gt; would fail on multibyte characters.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is enough for us to test our code. Let&amp;#39;s see it in action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;source = &amp;quot;Point = Struct.new(:x, :y, keyword_init: true)&amp;quot;

rewritten, _fixes = StructToData.rewrite(source)

rewritten ==&amp;quot;Point = Data.define(:x, :y)&amp;quot; # =&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It works!&lt;/p&gt;
&lt;h2&gt;Mutation: The Source of All Evil&lt;/h2&gt;
&lt;p&gt;There&amp;#39;s one big problem with our current approach: &lt;code&gt;Data&lt;/code&gt; objects are immutable, while structs aren&amp;#39;t, so we can&amp;#39;t always convert them. We have to also skip structs that mutate internal state.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll use an inner visitor to check if a struct body contains mutations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class MutationScanner &amp;lt; Prism::Visitor
  def initialize
    super
    @mutates = false
  end

  def mutates? = @mutates

  def visit_call_node(n)
    # self.x = ..., self[:k] = ...
    if n.receiver.is_a?(Prism::SelfNode) &amp;amp;&amp;amp; n.name.to_s.end_with?(&amp;quot;=&amp;quot;)
      @mutates = true
    end

    # adding writers via macros
    if n.name == :attr_writer || n.name == :attr_accessor
      @mutates = true
    end

    # define_method(:x=) { ... }
    if n.name == :define_method
      arg = n.arguments&amp;amp;.arguments&amp;amp;.first
      if (arg.is_a?(Prism::SymbolNode) || arg.is_a?(Prism::StringNode)) &amp;amp;&amp;amp; arg.unescaped.end_with?(&amp;quot;=&amp;quot;)
        @mutates = true
      end
    end

    super
  end

  # def x=(...) / def []=(...)
  def visit_def_node(n)
    if n.name.to_s.end_with?(&amp;quot;=&amp;quot;)
      @mutates = true
    end
    super
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This catches many cases, but there are many more ways to mutate a value in Ruby (i.e., by writing to an instance variable): writing to &lt;code&gt;ivars&lt;/code&gt; directly, using attribute writers, or memoization, to name a few. It would be a chore to define methods for each one manually.&lt;/p&gt;
&lt;p&gt;Luckily, Prism consistently names the nodes for these mutation methods, so we&amp;#39;ll just flag any nodes that perform a write:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class MutationScanner &amp;lt; Prism::Visitor
  # ... after def visit_def_node

  Prism
    .constants
    .filter_map do |const_name|
      next if const_name !~ /Write/ || const_name =~ /GlobalVariable|LocalVariable|Constant/

      Prism.const_get(const_name)
    end
    .each do |node_class|
      define_method(&amp;quot;visit_#{node_class.type}&amp;quot;) do |n|
        @mutates = true
        super(n)
      end
    end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we get all &amp;quot;write&amp;quot; constants and define methods, but ignore writing to constants, local variables, and global variables (as those don&amp;#39;t change a struct&amp;#39;s internal state).&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s wire up this scanner now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class StructToData &amp;lt; Prism::Visitor
  #...
  def visit_call_node(node)
    if struct_new?(node) &amp;amp;&amp;amp; !named_struct?(node) &amp;amp;&amp;amp; !mutates_instance_state?(node.block)
      # build fix
    end
  end

  # ...

  def mutates_instance_state?(block_node)
    return false if block_node.nil?

    scanner = MutationScanner.new
    scanner.visit(block_node)
    scanner.mutates?
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s it! Now our rewriter is ready to dance. Try it out with some of your structs.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Prism has already reshaped the Ruby landscape by making our tools faster, more portable, and more consistent. But its real impact will come from what &lt;em&gt;you&lt;/em&gt; build with it.&lt;/p&gt;
&lt;p&gt;Think bigger than just parsing: a Ruby-to-JS transpiler, a test runner that knows exactly which test to run from a file and line number, or even &lt;a href=&quot;https://github.com/MatheusRich/code_picture&quot;&gt;something that turns your code into pixel art&lt;/a&gt;. The parser is no longer the bottleneck — your imagination is.&lt;/p&gt;
&lt;p&gt;Go make something amazing!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>AppSignal’s Top 5 Ruby Posts in 2025</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/12/17/appsignals-top-5-ruby-posts-in-2025.html"/>
    <id>https://blog.appsignal.com/2025/12/17/appsignals-top-5-ruby-posts-in-2025.html</id>
    <published>2025-12-17T00:00:00+00:00</published>
    <updated>2025-12-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s look back at our top 5 Ruby posts this year.</summary>
    <content type="html">&lt;p&gt;As the year comes to a close, we’re thrilled to highlight our top five most-read Ruby articles of 2025:&lt;/p&gt;
&lt;h2&gt;Top 5 Ruby Blog Posts in 2025 💎&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2025/05/07/an-introduction-to-solid-queue-for-ruby-on-rails.html&quot;&gt;An Introduction to Solid Queue for Ruby on Rails&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In part one of our two-part series, we explored Solid Queue&amp;#39;s internals, discovered what makes it unique, and learned more about why it was created in the first place.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2025/06/18/a-deep-dive-into-solid-queue-for-ruby-on-rails.html&quot;&gt;A Deep Dive into Solid Queue for Ruby on Rails&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the second part of our series, we dove deeper into some of Solid Queue&amp;#39;s more advanced features.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2025/02/26/advanced-queries-in-activerecord-for-ruby-on-rails.html&quot;&gt;Advanced Queries in ActiveRecord for Ruby on Rails&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We got to grips with advanced ActiveRecord queries, shining a spotlight on more complex joins, custom SQL, and strategic employment of database-specific features.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2025/11/05/an-introduction-to-game-development-with-dragonruby.html&quot;&gt;An Introduction to Game Development with DragonRuby&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We covered the basics of game development with DragonRuby in the first part of this two-part series.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2025/10/01/how-to-read-code-from-the-showcase-ruby-on-rails-engine.html&quot;&gt;How to Read Code from the Showcase Ruby on Rails Engine&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this first part of a three-part series, we set up Showcase in a Rails application and ran it locally.&lt;/p&gt;
&lt;h2&gt;Season&amp;#39;s Greetings ❆ ⛄&lt;/h2&gt;
&lt;p&gt;Have a joyful festive season, and we’ll see you in the new year!&lt;/p&gt;
&lt;p&gt;If you haven’t already, don’t forget to &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter&lt;/a&gt;, so you never miss an upcoming post.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Create a Markdown Editor in Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/12/10/create-a-markdown-editor-in-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2025/12/10/create-a-markdown-editor-in-ruby-on-rails.html</id>
    <published>2025-12-10T00:00:00+00:00</published>
    <updated>2025-12-10T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this post, we&#039;ll build a Markdown editor using Rails.</summary>
    <content type="html">&lt;p&gt;In recent years, Markdown has become the lingua franca of plain-text files on the web. If you&amp;#39;re a developer, you have
read — and maybe even written — hundreds of Markdown documents over the course of your career.&lt;/p&gt;
&lt;p&gt;GitHub repositories use README files written in Markdown. Stack Overflow and Reddit use it to format posts. Technical
documentation, blog posts, and entire books are written in Markdown. And it&amp;#39;s not just for humans either! AI tools such
as Claude Code and Cursor use Markdown documents to
improve the effectiveness of AI agents. This article you are reading is — you guessed it — written in Markdown!&lt;/p&gt;
&lt;p&gt;Ruby on Rails, of course, has its own tooling around Markdown, and in this post, we&amp;#39;ll build a Markdown editor using Rails.&lt;/p&gt;
&lt;h2&gt;Markdown in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Rails 8.1 brings &lt;a href=&quot;https://github.com/rails/rails/pull/55511&quot;&gt;Markdown as content
type&lt;/a&gt;, as well as a &lt;a href=&quot;https://dev.37signals.com/announcing-lexxy-a-new-rich-text-editor-for-rails/&quot;&gt;new rich text
editor&lt;/a&gt; with Markdown support. And if you
can&amp;#39;t or don&amp;#39;t want to use Rails defaults, there are always gems, such as
&lt;a href=&quot;https://github.com/avo-hq/marksmith&quot;&gt;Marksmith&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But what does it actually take to build a GitHub-like Markdown editor? Something simple that supports editing Markdown
text and previewing the result. Something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-12/01_editor.png&quot; alt=&quot;Editor&quot;/&gt;
&lt;img src=&quot;/images/blog/2025-12/02_preview.png&quot; alt=&quot;Preview&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s find out!&lt;/p&gt;
&lt;h2&gt;Markdown and Markdown Flavors&lt;/h2&gt;
&lt;p&gt;Before we start with the implementation, let&amp;#39;s talk about the Markdown language itself. First, it&amp;#39;s essential to
understand that there is no single, definitive Markdown language. When Markdown was conceived in 2004, &lt;a href=&quot;https://daringfireball.net/projects/markdown/syntax&quot;&gt;its original
description&lt;/a&gt; left quite some room for interpretation. As a result,
a plethora of Markdown dialects — or flavors — sprang into existence, each of them supporting &lt;a href=&quot;https://gist.github.com/vimtaai/99f8c89e7d3d02a362117284684baa0f&quot;&gt;different features and
slightly different syntaxes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The flavor you might be most familiar with is GitHub Flavored Markdown, or GFM in short. It is based on
&lt;a href=&quot;https://commonmark.org/&quot;&gt;CommonMark&lt;/a&gt;, an attempt to create a formal, unified specification for Markdown, which was
created in 2014. The CommonMark specification has been widely adopted, but even so, several different Markdown flavors
remain common today.&lt;/p&gt;
&lt;p&gt;Now, what makes Markdown so interesting, and why was it so widely adopted? The beautiful thing about Markdown is that it
strikes a perfect balance between being structured and unstructured. It is not as verbose as other, more structured
languages (such as XML, HTML, or JSON), and that makes it easy to read and write. But it provides just enough structure
to be processed by computers. And that allows us to do interesting things with it.&lt;/p&gt;
&lt;p&gt;One of those interesting things is converting Markdown to other formats, such as HTML. As a matter of fact, that was
exactly what its designers had in mind!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Markdown allows you to write using an easy-to-read, easy-to-write plain text format, then convert it to structurally
valid HTML.
&lt;em&gt;Source: &lt;a href=&quot;https://daringfireball.net/projects/markdown/&quot;&gt;Markdown, John Gruber&amp;#39;s website&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Using Ruby, we have several fine options for converting Markdown into HTML: &lt;a href=&quot;https://github.com/vmg/redcarpet&quot;&gt;redcarpet&lt;/a&gt;, &lt;a href=&quot;https://github.com/gjtorikian/commonmarker&quot;&gt;commonmarker&lt;/a&gt;,
and &lt;a href=&quot;https://github.com/gettalong/kramdown&quot;&gt;Kramdown&lt;/a&gt;, to name a few. What they all have in common is that they convert
Markdown to HTML. For example:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Markdown&lt;/strong&gt;: &lt;code&gt;Markdown supports *italic* and **bold** text.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTML&lt;/strong&gt;: &lt;code&gt;&amp;lt;p&amp;gt;Markdown supports &amp;lt;em&amp;gt;italic&amp;lt;/em&amp;gt; and &amp;lt;strong&amp;gt;bold&amp;lt;/strong&amp;gt; text.&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;As I mentioned earlier, different flavors of Markdown work differently. We&amp;#39;ll be using GitHub Flavored Markdown (GFM) for the
rest of this article. If you&amp;#39;re curious about how GFM differs from other Markdown flavors, take a look at &lt;a href=&quot;https://github.github.com/gfm/&quot;&gt;the
specification&lt;/a&gt;. Note the sections tagged as &lt;em&gt;extension&lt;/em&gt; — these are features that are
unique to GFM and not usually found in other Markdown dialects.&lt;/p&gt;
&lt;p&gt;Now that we have covered the theory, let&amp;#39;s get building.&lt;/p&gt;
&lt;h2&gt;Creating a Markdown Editor with Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Our Markdown editor will require three things. First, a way for users to write — and save — plain text. This is easily
accomplished using off-the-shelf Rails features. Second, we&amp;#39;ll utilize a third-party gem to render Markdown to HTML. And
finally, a tiny bit of JavaScript to tie these things together and create a nice user experience.&lt;/p&gt;
&lt;p&gt;While the quickest way to add a Markdown editor to your application is simply by using a gem — such as the excellent
&lt;a href=&quot;https://github.com/avo-hq/marksmith&quot;&gt;Marksmith&lt;/a&gt; — it&amp;#39;s always worthwhile to learn what goes on under the hood.
You can follow along with the code used in this post by &lt;a href=&quot;https://github.com/hschne/github-markdown-editor&quot;&gt;viewing or cloning this repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start a new Rails app with a simple post model that will hold our Markdown text. We use &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind
CSS&lt;/a&gt; for styling and also enable
&lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html&quot;&gt;ActiveStorage&lt;/a&gt;. Why do we need ActiveStorage, you ask?&lt;/p&gt;
&lt;p&gt;It&amp;#39;s a surprise: you&amp;#39;ll see later.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails new github-markdown-editor --css=tailwind &amp;amp;&amp;amp; cd github-markdown-editor
rails active_storage:install
# Add our Post model and migrate
rails g scaffold Post body:text
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The post scaffold creates a form with a text area, which is all we need for users to write and save Markdown documents.
Now we need to render those documents to HTML. We&amp;#39;ll use &lt;a href=&quot;https://github.com/gjtorikian/commonmarker&quot;&gt;Commonmarker&lt;/a&gt;. It
supports GitHub Flavored Markdown and is easily customizable, which makes it the logical choice.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bundle add commonmarker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we tackle the editor itself, let&amp;#39;s update our app so it displays rendered Markdown when showing a post. We only
need to add a handful of lines to both our posts controller and the post partial.&lt;/p&gt;
&lt;p&gt;Note our use of &lt;code&gt;html_safe&lt;/code&gt;. Otherwise, the HTML created by Commonmarker will be sanitized by Rails. We don&amp;#39;t want
sanitized HTML — we want our HTML to be shown to the user as rendered.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
class PostsController &amp;lt; ApplicationController

  def show
    require &amp;#39;commonmarker&amp;#39;
    @markdown = Commonmarker.to_html(@post.body).html_safe
  end

  #...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/posts/_post.html.erb --&amp;gt;
&amp;lt;div id=&amp;quot;&amp;lt;%= dom_id post %&amp;gt;&amp;quot; class=&amp;quot;w-full sm:w-auto my-5 space-y-5&amp;quot;&amp;gt;
  &amp;lt;!-- ... --&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;strong class=&amp;quot;block font-medium mb-1&amp;quot;&amp;gt;Markdown:&amp;lt;/strong&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;%= @markdown %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To try it out, let&amp;#39;s create a new post with the following Markdown content.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;# Markdown Rendering

Let&amp;#39;s add some Markdown rendering to a Rails application using the [Commonmarker](https://github.com/gjtorikian/commonmarker) gem.

Markdown supports **bold** and _italic_ text. GFM adds features like ~~strike through text~~.

Autolinking and emojis are supported by Commonmarker, check it out: https://www.appsignal.com/ :heart:

There&amp;#39;s also syntax highlighting:

```ruby
def greet(name)
  puts &amp;quot;Hello, #{name}&amp;quot;
end
```

As well as tables and lots of other features!

| Gem          | Main Feature                                |
| ------------ | ------------------------------------------- |
| CommonMarker | GitHub-flavored Markdown                    |
| AppSignal    | Performance monitoring for your application |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we view the post, things are clearly working. We can see bold and italic text, links, and even syntax highlighting.
But we are still far from a GitHub-like editor.&lt;/p&gt;
&lt;p&gt;We don&amp;#39;t want to create a post to see what the resulting HTML looks like. What we need is a live preview.&lt;/p&gt;
&lt;h2&gt;Preview with Turbo Streams&lt;/h2&gt;
&lt;p&gt;Rather than showing rendered HTML only after a post is created, we want to display it while in the
writing process. We&amp;#39;ll easily be able to do so using Turbo Streams and StimulusJS. Let&amp;#39;s add a Turbo stream action to
our posts controller and the corresponding route to our routes file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
class PostController
  def preview
    require &amp;quot;commonmarker&amp;quot;
    @markdown = Commonmarker.to_html(params[:body])
    render turbo_stream: turbo_stream.update(&amp;quot;preview&amp;quot;, @markdown)
  end
  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/routes.rb
Rails.application.routes.draw do
  resources :posts do
    post :preview, on: :collection
  end
  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our Turbo stream targets the &lt;code&gt;preview&lt;/code&gt; element. Accordingly, we create a &lt;code&gt;div&lt;/code&gt; with the &lt;code&gt;preview&lt;/code&gt; ID in the post form. We
also add a button that will trigger the Turbo stream action using a Stimulus controller — which we&amp;#39;ll create next.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/posts/_form.html.erb --&amp;gt;
&amp;lt;%= form_with(model: post, class: &amp;quot;contents&amp;quot;,
              data: {
                controller: &amp;quot;preview&amp;quot;,
                preview_url_value: preview_posts_path
              }) do |form| %&amp;gt;
  &amp;lt;!-- ... --&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;%= form.textarea :body, rows: 22,
                  data: {
                    preview_target: &amp;quot;editorContent&amp;quot;,
                  },
                  class: &amp;quot;...&amp;quot;
                  %&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;!-- Add a button to trigger the preview and div to display it --&amp;gt;
  &amp;lt;div class=&amp;quot;my-5&amp;quot;&amp;gt;
    &amp;lt;button type=&amp;quot;button&amp;quot; data-action=&amp;quot;click-&amp;gt;preview#show&amp;quot; class=&amp;quot;...&amp;quot;&amp;gt;
      Preview
    &amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div id=&amp;quot;preview&amp;quot; class=&amp;quot;my-5 p-4 border rounded-md bg-white&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Our Stimulus controller is nothing special. It takes the content of our editor, which we refer to as the
&lt;code&gt;editorContentTarget&lt;/code&gt;. It then sends a request to the post preview action. The rest is done automagically by
Turbo. On a side note, we are using &lt;a href=&quot;https://github.com/rails/request.js&quot;&gt;rails/request.js&lt;/a&gt; here, which makes it a lot
simpler to send Turbo Stream requests — or any other requests for that matter — to your Rails application from
JavaScript.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/preview_controller.js
import { Controller } from &amp;quot;@hotwired/stimulus&amp;quot;;
import { post } from &amp;quot;@rails/request.js&amp;quot;;

export default class extends Controller {
  static targets = [&amp;quot;editorContent&amp;quot;];
  static values = { url: String };

  show() {
    post(this.urlValue, {
      body: {
        body: this.editorContentTarget.value,
      },
      responseKind: &amp;quot;turbo-stream&amp;quot;,
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our post form, we can now preview the rendered Markdown at the click of a button. Great success!&lt;/p&gt;
&lt;h2&gt;Improving the User Experience&lt;/h2&gt;
&lt;p&gt;Now, there are two more things to address. For one, if you&amp;#39;ve been playing along, you&amp;#39;ve probably noticed our HTML doesn&amp;#39;t
really look right. We are rendering tags such as &lt;code&gt;h1&lt;/code&gt; or &lt;code&gt;table&lt;/code&gt; correctly, but the styling is off. Since we use
Tailwind, there is an easy way to fix this — the &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-typography&quot;&gt;TailwindCSS typography
plugin&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;/* app/assets/tailwind/application.css */
@import &amp;quot;tailwindcss&amp;quot;;
/* Add the typography plugin */
@plugin &amp;quot;@tailwindcss/typography&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What does it do, you ask?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The official Tailwind CSS Typography plugin provides a set of prose classes you can use to add beautiful typographic
defaults to any vanilla HTML you don&amp;#39;t control, &lt;strong&gt;like HTML rendered from Markdown&lt;/strong&gt;, or pulled from a CMS.
&lt;em&gt;Source: &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-typography&quot;&gt;Tailwind CSS Typography GitHub README&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sounds perfect for our purpose. To make our HTML look pretty, we just need to add the &lt;code&gt;prose&lt;/code&gt; class to our preview
pane.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!-- app/views/posts/_form.html.erb --&amp;gt;
&amp;lt;div id=&amp;quot;preview&amp;quot; class=&amp;quot;my-5 p-4 ... prose&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-12/03_styling.png&quot; alt=&quot;Without prose&quot;/&gt;
&lt;img src=&quot;/images/blog/2025-12/04_styling_prose.png&quot; alt=&quot;With prose&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Much better. If you are not using Tailwind or the typography plugin, you can still make your HTML look nice, but you&amp;#39;ll
need to create the necessary CSS by hand.&lt;/p&gt;
&lt;p&gt;Now, finally, let&amp;#39;s touch up our editor. We want to switch between our editor (the text area where we write our
Markdown) and our preview at the click of a button. For that, we&amp;#39;ll update our Stimulus controller so that it toggles
between these two panes by hiding one and displaying the other. We&amp;#39;ll introduce the necessary targets, along with new
targets for the buttons (so we can change the style of the active one).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/preview_controller.js
export default class extends Controller {
  static targets = [
    &amp;quot;editor&amp;quot;,
    &amp;quot;editorContent&amp;quot;,
    &amp;quot;preview&amp;quot;,
    &amp;quot;editorButton&amp;quot;,
    &amp;quot;previewButton&amp;quot;,
  ];
  static values = { url: String };

  editor(event) {
    event.preventDefault();
    this.#toggleButton(this.editorButtonTarget, this.previewButtonTarget);
    this.#togglePane(this.editorTarget, this.previewTarget);
  }

  preview(event) {
    event.preventDefault();
    post(this.urlValue, {
      body: { body: this.editorContentTarget.value },
      responseKind: &amp;quot;turbo-stream&amp;quot;,
    });
    this.#toggleButton(this.previewButtonTarget, this.editorButtonTarget);
    this.#togglePane(this.previewTarget, this.editorTarget);
  }

  #toggleButton(activate, deactivate) {
    // Toggle button styles
    activate.classList.remove(&amp;quot;bg-gray-100&amp;quot;, &amp;quot;hover:bg-gray-50&amp;quot;);
    // ...
  }

  #togglePane(show, hide) {
    // Hide one pane and show the other
    show.classList.remove(&amp;quot;hidden&amp;quot;);
    hide.classList.add(&amp;quot;hidden&amp;quot;);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need to make some changes to our form. We can remove our previous preview button since we no longer need it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/posts/_form.html.erb --&amp;gt;
&amp;lt;div class=&amp;quot;...&amp;quot;&amp;gt;
  &amp;lt;button class=&amp;quot;...&amp;quot; data-action=&amp;quot;click-&amp;gt;preview#editor&amp;quot; data-preview-target=&amp;quot;editorButton&amp;quot; type=&amp;quot;button&amp;quot;&amp;gt;
    Write
  &amp;lt;/button&amp;gt;
  &amp;lt;button
    class=&amp;quot;...&amp;quot; data-action=&amp;quot;click-&amp;gt;preview#preview&amp;quot; data-preview-target=&amp;quot;previewButton&amp;quot; type=&amp;quot;button&amp;quot;&amp;gt;
    Preview
  &amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;my-5&amp;quot; data-preview-target=&amp;quot;editor&amp;quot;&amp;gt;
  &amp;lt;%= form.textarea :body,
                rows: 22,
                data: { preview_target: &amp;quot;editorContent&amp;quot; },
                class: &amp;quot;...&amp;quot;
                %&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;preview&amp;quot; data-preview-target=&amp;quot;preview&amp;quot; class=&amp;quot;... prose max-w-full&amp;quot; &amp;gt; &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&amp;#39;s all we need to create a magnificent Markdown editor just like the one GitHub has! Well, that&amp;#39;s not entirely
true. GitHub&amp;#39;s editor has a lot of subtle features that make our writing experience more pleasant — we won&amp;#39;t add all of
them here.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s one thing we will add, though. What I adore about the GitHub editor is that it lets you add images to your
Markdown simply by pasting them into the editor. Let&amp;#39;s wrap up this article by adding this advanced feature.&lt;/p&gt;
&lt;h2&gt;Adding Image Uploads to our Markdown Editor in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Remember how we enabled ActiveStorage when we set up our demo application? Well, this is what it&amp;#39;s for!&lt;/p&gt;
&lt;p&gt;To upload files directly from our editor, we&amp;#39;ll be using &lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html#direct-uploads&quot;&gt;Rails Direct
Uploads&lt;/a&gt;. Let&amp;#39;s add the necessary JavaScript
to our application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bin/importmap pin @rails/activestorage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we update our trusty preview controller one final time.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import { DirectUpload } from &amp;quot;@rails/activestorage&amp;quot;;

export default class extends Controller {
  // Add the upload URL value
  static values = { url: String, uploadUrl: String };

  upload(event) {
    if (!event.clipboardData.files.length) return;

    event.preventDefault();
    Array.from(event.clipboardData.files).forEach((file) =&amp;gt;
      this.#uploadFile(file),
    );
  }

  #uploadFile(file) {
    const upload = new DirectUpload(file, this.uploadUrlValue);
    upload.create((_error, blob) =&amp;gt; {
      const url = `/rails/active_storage/blobs/redirect/${
        blob.signed_id
      }/${encodeURIComponent(blob.filename)}`;
      const link = `![${blob.filename}](${url})\n`;
      // Update editor content
      const start = this.editorContentTarget.selectionStart;
      const end = this.editorContentTarget.selectionEnd;
      this.editorContentTarget.setRangeText(link, start, end);
    });
  }
  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;upload&lt;/code&gt; function will be called whenever a user pastes something in our editor. If the pasted data contains any
files, we use the &lt;code&gt;DirectUpload&lt;/code&gt; class supplied by ActiveStorage to upload them to our server. Once the upload has
finished, we take the resulting blob attributes and convert them into a markdown image link that Commonmarker can
convert to HTML. Finally, we use
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/selectionStart&quot;&gt;selectionStart&lt;/a&gt; and
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/selectionEnd&quot;&gt;selectionEnd&lt;/a&gt; to determine at which
position to insert this link.&lt;/p&gt;
&lt;p&gt;We now only need to update our HTML to provide the direct upload URL and wire up the paste event to our Stimulus
controller. Simple.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= form_with(model: post, class: &amp;quot;contents&amp;quot;, data:
              { controller: &amp;quot;preview&amp;quot;,
                preview_url_value: preview_posts_path,
                &amp;lt;!-- Add the direct uploads URL --&amp;gt;
                preview_upload_url_value: rails_direct_uploads_url
              }) do |form| %&amp;gt;
  &amp;lt;%= form.textarea :body,
                    data: {
                      preview_target: &amp;quot;editorContent&amp;quot;,
                      &amp;lt;!-- Wire up the paste event --&amp;gt;
                      action: &amp;quot;paste-&amp;gt;preview#upload&amp;quot;,
                    },
                    &amp;lt;!-- ... --&amp;gt;
  %&amp;gt;
%&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we did leave some things out. For one, there is no error handling — your image upload may fail, and you should
handle that. Also, for a true GitHub-like experience, you&amp;#39;d want to support uploading files via drag-and-drop, as well
as allowing users to select files using a file picker. These features work similarly to upload-on-paste — we just left
them out in this post for brevity.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;Markdown is arguably &lt;em&gt;the&lt;/em&gt; language of the web. Except it isn&amp;#39;t a single language, but is comprised of various dialects,
the most popular of which are CommonMark and GitHub Flavored Markdown.&lt;/p&gt;
&lt;p&gt;In the Ruby ecosystem, Commonmarker is an excellent choice for converting GFM to HTML.&lt;/p&gt;
&lt;p&gt;By leveraging the power of Turbo
Streams, StimulusJS, and TailwindCSS, we have created a nice-looking Markdown editor that supports live previews and image
uploads in no time.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Completing, Integrating, and Publishing Our Game with DragonRuby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/11/26/completing-integrating-and-publishing-our-game-with-dragonruby.html"/>
    <id>https://blog.appsignal.com/2025/11/26/completing-integrating-and-publishing-our-game-with-dragonruby.html</id>
    <published>2025-11-26T00:00:00+00:00</published>
    <updated>2025-11-26T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s continue developing our game, then integrate and publish it using DragonRuby in the second part of our two-part series.</summary>
    <content type="html">&lt;p&gt;In part one of this series, we started developing a simple Flappy Bird clone game using the DragonRuby game development toolkit. We didn&amp;#39;t come very far, though — we stopped after integrating player input to keep our plane afloat.&lt;/p&gt;
&lt;p&gt;In this second and concluding part, we&amp;#39;ll implement the remaining simple game mechanics. We&amp;#39;ll also take a brief look at interfacing with an HTTP server and publishing our game on itch.io.&lt;/p&gt;
&lt;h2&gt;Scene Management&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s continue developing the game by adding a condition for terminating it (&amp;quot;game over&amp;quot;). Whenever the plane &lt;em&gt;collides&lt;/em&gt; with an obstacle or drops out of the bottom of the screen, the game should abort and give feedback to the player.&lt;/p&gt;
&lt;p&gt;For simplicity, let&amp;#39;s start with the latter. To keep responsibilities clear in our game code, we&amp;#39;ll create a &lt;code&gt;game_over?&lt;/code&gt; method that returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; (we will later extend it to include arbitrary collisions):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def game_over? args
  args.state.plane_pos.y &amp;lt;= 0 - args.state.plane_pos.h
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we simply check the plane&amp;#39;s &lt;code&gt;y&lt;/code&gt; coordinate against the lower boundary of the screen. Additionally, we subtract the sprite&amp;#39;s height, so that this check only returns &lt;code&gt;true&lt;/code&gt; when the plane has fully disappeared.&lt;/p&gt;
&lt;p&gt;When the game is over, we want to display a special screen that just reads &amp;quot;Game Over&amp;quot; (later, we&amp;#39;ll add a score). To do this, we have to divide our game logic into two &lt;em&gt;scenes&lt;/em&gt;: a &lt;code&gt;:game&lt;/code&gt; scene and a &lt;code&gt;:game_over&lt;/code&gt; scene. As you might have guessed, we can just make this an additional key to our game state.&lt;/p&gt;
&lt;p&gt;In each tick, we will check whether the game is over. If it is, we&amp;#39;ll just switch the &lt;code&gt;args.state&lt;/code&gt; variable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def tick args
+   args.state.scene ||= :game
    args.state.dy ||= 0

    # etc.

+   if game_over?(args)
+     args.state.scene = :game_over
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ve now arrived at a point that warrants a closer look at code organization. Looking at our program right now, it is clear that we can draw a line between code that &lt;em&gt;renders&lt;/em&gt; something to the screen and code that &lt;em&gt;calculates&lt;/em&gt; changes to the game state. Let&amp;#39;s extract both into their own methods (for good measure, I&amp;#39;ve also added an &lt;code&gt;init&lt;/code&gt; method that encapsulates the state initialization code neatly):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;GRAVITY = 0.5
FLAP_POWER = 10

def tick args
  init args
  calc args
  render args
end

def init args
  args.state.scene ||= :game
  args.state.dy ||= 0
  args.state.plane_pos ||= { x: 640,
                             y: 360,
                             w: 88,
                             h: 73,
                             anchor_x: 0.5,
                             anchor_y: 0.5 }
end

def render args
  args.outputs.sprites &amp;lt;&amp;lt; { x: 0 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
  args.outputs.sprites &amp;lt;&amp;lt; { x: 1280 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }

  args.outputs.sprites &amp;lt;&amp;lt; { **args.state.plane_pos,
                            path: &amp;quot;sprites/Planes/planeRed1.png&amp;quot; }
end

def calc args
  args.state.dy -= GRAVITY

  if args.inputs.mouse.down || args.inputs.keyboard.key_down.space
    args.state.dy += FLAP_POWER
  end

  args.state.plane_pos.y += args.state.dy

  args.state.scene = :game_over if game_over?(args)
end

def game_over? args
  args.state.plane_pos.y &amp;lt;= 0 - args.state.plane_pos.h
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now complete our scene management code in the &lt;code&gt;render&lt;/code&gt; method. If the current scene is &lt;code&gt;:game&lt;/code&gt;, we&amp;#39;ll continue painting our plane sprite. If it&amp;#39;s &lt;code&gt;:game_over&lt;/code&gt;, we&amp;#39;ll print a message instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def render args
  # rolling background
  args.outputs.sprites &amp;lt;&amp;lt; { x: 0 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
  args.outputs.sprites &amp;lt;&amp;lt; { x: 1280 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }

  if args.state.scene == :game
    args.outputs.sprites &amp;lt;&amp;lt; { **args.state.plane_pos,
                              path: &amp;quot;sprites/Planes/planeRed1.png&amp;quot; }
  elsif args.state.scene == :game_over
    args.outputs.labels &amp;lt;&amp;lt; { x: 640, y: 360, text: &amp;quot;GAME OVER&amp;quot;, size_px: 128, anchor_x: 0.5, anchor_y: 0.5 }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This completes our minimal scene management implementation. It&amp;#39;s not fully complete yet, because the game should reset after a timeout when it&amp;#39;s over, but we will leave that out for now. If we run it from our terminal with &lt;code&gt;./dragonruby&lt;/code&gt;, this is what we get:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/dragonruby-game-over.gif&quot; alt=&quot;Game over screen&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Collision Detection&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s now piece together the final component of our game mechanic: obstacles and detecting collisions. We will add a key called &lt;code&gt;walls&lt;/code&gt; to our game state, which will hold an array of all currently visible walls. Each wall will have two sprites — one for a rock emerging from below, and one for a stalactite from above. Other than that, we only have to store an &lt;code&gt;x&lt;/code&gt; position and update it, as walls move from right to left.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start with preparing the state in &lt;code&gt;init&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def init args
    args.state.scene ||= :game
    args.state.dy ||= 0
+   args.state.walls ||= []

    # etc.
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we&amp;#39;ll add some code to &lt;code&gt;calc&lt;/code&gt;, to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;update the &lt;code&gt;x&lt;/code&gt; position of all visible walls so that they move to the left (in the code below, every wall&amp;#39;s &lt;code&gt;x&lt;/code&gt; coordinate is diminished by 8)&lt;/li&gt;
&lt;li&gt;destroy walls that have moved out to the left. We remove all walls whose &lt;code&gt;x&lt;/code&gt; position is smaller than -108 (the wall sprite&amp;#39;s width) from the array using &lt;code&gt;reject!&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;create new walls from the right. We do this at an interval of 120 frames (that&amp;#39;s every two seconds) by adding a new hash to the array with an &lt;code&gt;x&lt;/code&gt; position of 1280 (the screen&amp;#39;s width).&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def calc args
    args.state.dy -= GRAVITY

    if args.inputs.mouse.down || args.inputs.keyboard.key_down.space
      args.state.dy += FLAP_POWER
    end

    args.state.plane_pos.y += args.state.dy

+   # walls
+   args.state.walls.each { |w| w.x -= 8 }
+   args.state.walls.reject! { |w| w.x &amp;lt; -108 }
+   args.state.walls &amp;lt;&amp;lt; {
+     x: 1280
+   } if Kernel.tick_count % 120 == 0

    args.state.scene = :game_over if game_over?(args)
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we are using absolute numbers for positions here, just to avoid blowing up the scope of this article. For robust, relative positioning, take a look at the &lt;a href=&quot;https://docs.dragonruby.org/#/api/grid?id=grid&quot;&gt;Grid&lt;/a&gt; class.&lt;/p&gt;
&lt;p&gt;Next, we actually render the walls using more sprites from Kenney&amp;#39;s &amp;quot;Tappy Plane&amp;quot; kit:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def render args
    # ...

    if args.state.scene == :game
      args.outputs.sprites &amp;lt;&amp;lt; { **args.state.plane_pos,
                                path: &amp;quot;sprites/Planes/planeRed1.png&amp;quot; }
+     args.state.walls.each do |wall|
+       wall.sprites = [
+         {
+           x: wall.x,
+           y: 0,
+           w: 108,
+           h: 239,
+           path: &amp;quot;sprites/rock.png&amp;quot;
+         },
+         {
+           x: wall.x,
+           y: 720,
+           w: 108,
+           h: 239,
+           anchor_y: 1,
+           path: &amp;quot;sprites/rockDown.png&amp;quot;
+         }
+       ]

+       args.outputs.sprites &amp;lt;&amp;lt; args.state.walls.map(&amp;amp;:sprites)
      end
    elsif args.state.scene == :game_over
      # ...
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we actually &lt;em&gt;mutate&lt;/em&gt; the wall entities stored in the game state by setting a &lt;code&gt;sprites&lt;/code&gt; property on them that contains an array of sprite definitions. Thus, every object in &lt;code&gt;args.state.walls&lt;/code&gt; contains an array of &lt;code&gt;sprites&lt;/code&gt;, which we then use to actually draw the sprites to &lt;code&gt;args.outputs&lt;/code&gt;. The reason for this detour will become clear presently.&lt;/p&gt;
&lt;p&gt;We will handle the actual collision detection next. Since we have extracted the &lt;code&gt;game_over?&lt;/code&gt; method, which is called every tick, this is the logical place to put the code that checks if the plane intersects with an obstacle.&lt;/p&gt;
&lt;p&gt;As it turns out, this is pretty straightforward, since DragonRuby provides a couple of convenience methods for &lt;a href=&quot;https://docs.dragonruby.org/#/api/geometry?id=collision-functions&quot;&gt;collision detection&lt;/a&gt;, such as &lt;code&gt;intersect_rect?&lt;/code&gt; and &lt;code&gt;any_intersect_rect?&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can make use of the latter to test whether a collection of objects that respond to &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;w&lt;/code&gt;, and &lt;code&gt;h&lt;/code&gt; (such as our walls&amp;#39; &lt;code&gt;sprites&lt;/code&gt;) overlaps with another object that also responds to those attributes (such as our &lt;code&gt;plane_pos&lt;/code&gt;). All that&amp;#39;s needed is a bit of data massaging using &lt;code&gt;flat_map&lt;/code&gt;, and this expression returns &lt;code&gt;true&lt;/code&gt; whenever our plane intersects with a wall sprite.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def game_over? args
  return true if args.state.plane_pos.y &amp;lt;= 0 - args.state.plane_pos.h

  args.state.walls
    .flat_map { |wall| wall.sprites || [] }
    .any_intersect_rect? args.state.plane_pos
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these changes, our game finally behaves as you would expect it to:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/dragonruby-collision-detection.gif&quot; alt=&quot;A game screen containing a 2D plane that crashes into a mountain&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Storing and Managing a Score&lt;/h2&gt;
&lt;p&gt;The final piece of functionality we want to add to the game is tracking a score for our player and displaying it. Again, let&amp;#39;s start by adding it to our &lt;code&gt;init&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def init args
    args.state.scene ||= :game
+   args.state.score ||= 0
    args.state.dy ||= 0
    args.state.walls ||= []
    args.state.plane_pos ||= { x: 640,
                               y: 360,
                               w: 88,
                               h: 73,
                               anchor_x: 0.5,
                               anchor_y: 0.5 }
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next up, we are going to display it in the top left corner. Let&amp;#39;s add that to &lt;code&gt;render&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def render args
    args.outputs.sprites &amp;lt;&amp;lt; { x: 0 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
    args.outputs.sprites &amp;lt;&amp;lt; { x: 1280 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }

+   args.outputs.labels &amp;lt;&amp;lt; { x: 10, y: 710, text: &amp;quot;SCORE: #{args.state.score}&amp;quot;, size_px: 64 }

    # etc...
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To keep things as simple as possible, we are going to increase the score count by 1 every time a wall has moved out of the left screen boundary. The simplest way to do this is to calculate the difference of the wall count before and after their removal in &lt;code&gt;calc&lt;/code&gt;. If it has decreased (and the player is still playing, i.e., it&amp;#39;s not game over), we increase the score by 1:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def calc args
    # omitted

    # score
+   wall_count_before = args.state.walls.count
    args.state.walls.reject! { |w| w.x &amp;lt; -108 }
+   if wall_count_before &amp;gt; args.state.walls.count &amp;amp;&amp;amp; args.state.scene == :game
+     args.state.score += 1
+   end

    # omitted
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Now we can observe how the game score increases with every surpassed wall:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/dragonruby-score.gif&quot; alt=&quot;Increasing the game score&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;a href=&quot;https://docs.dragonruby.org/#/api/runtime?id=slowmo&quot;&gt;&lt;code&gt;args.gtk.slowmo!&lt;/code&gt;&lt;/a&gt; helper method can be really useful when debugging time-critical game mechanics!&lt;/p&gt;
&lt;h2&gt;Playing Sounds&lt;/h2&gt;
&lt;p&gt;Before moving on to the final steps (integration and deployment), let&amp;#39;s add an audible touch to the game. There are lots of different sounds you might want to add to your game, but in general, they fall into two categories: long-running &lt;em&gt;looping&lt;/em&gt; sounds and &lt;em&gt;one-shot&lt;/em&gt; sounds.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start with the first category and play a looping engine sound for the plane. In DragonRuby, sound is just another output device and handled with the &lt;code&gt;tick&lt;/code&gt; method. &lt;em&gt;However&lt;/em&gt;, as sound is time-based, you&amp;#39;ll want to only trigger it once, otherwise you&amp;#39;ll get hundreds of overlapping samples. That&amp;#39;s why we start the engine sound in the &lt;code&gt;init&lt;/code&gt; method, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def init args
  # omitted

  if args.state.tick_count == 1
    args.audio[:engine] = {
      input: &amp;quot;sounds/engine.ogg&amp;quot;,
      gain: 0.2,
      looping: true
    }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notably, we &lt;em&gt;only&lt;/em&gt; start playback at the first tick, and set &lt;code&gt;looping&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;. This will ensure that the engine sound keeps playing back, and not overlap.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s now turn to some sound effects. First, we are going to add a sound whenever the plane climbs. We already check for input in &lt;code&gt;calc&lt;/code&gt;, so we just have to add a one-shot sound:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def calc args
    # omitted

    if args.inputs.mouse.down || args.inputs.keyboard.key_down.space
      args.state.dy += FLAP_POWER
+     args.outputs.sounds &amp;lt;&amp;lt; &amp;quot;sounds/jump.ogg&amp;quot;
    end

    # omitted
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we just &lt;em&gt;append&lt;/em&gt; a sound file path to &lt;code&gt;args.outputs.sounds&lt;/code&gt;, which is arguably a very Rubyesque DSL. We can apply the same technique when increasing the score count:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def calc args
    # omitted

    if wall_count_before &amp;gt; args.state.walls.count &amp;amp;&amp;amp; args.state.scene == :game
      args.state.score += 1
+     args.outputs.sounds &amp;lt;&amp;lt; &amp;quot;sounds/score.ogg&amp;quot;
    end

    # omitted
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let&amp;#39;s add a sound when the plane crashes. For this, we&amp;#39;ll have to set a &lt;code&gt;game_over_played&lt;/code&gt; boolean flag, because otherwise the sound would start playing at each tick:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def calc args
    # omitted

    if game_over?(args)
      args.state.scene = :game_over

+     unless args.state.game_over_played
+       args.audio[:engine].paused = true
+       args.outputs.sounds &amp;lt;&amp;lt; &amp;quot;sounds/explosion.ogg&amp;quot;
+       args.state.game_over_played = true
+     end
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the game is over, the engine sound is paused and an explosion sound plays. However, to ensure that this happens only once, we set (and subsequently check at every tick) &lt;code&gt;args.state.game_over_played&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you&amp;#39;re wondering where to obtain sounds, it&amp;#39;s the same procedure as with sprites. There are bazillions of sound packs available online!&lt;/p&gt;
&lt;h2&gt;Integration with an HTTP Server to Store and Retrieve High Scores&lt;/h2&gt;
&lt;p&gt;What would a video game be without a leaderboard? Obviously, the easiest choice would be to keep high scores in a local file. However, DragonRuby also contains a minimal HTTP client that can interact with remote servers.&lt;/p&gt;
&lt;p&gt;We are going to use this functionality in the &amp;quot;game over&amp;quot; scene to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Persist the current score via a POST request.&lt;/li&gt;
&lt;li&gt;Retrieve the highest 3 scores via a GET request.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To achieve this, we first need a minimal local web application. We are going to use &lt;a href=&quot;https://roda.jeremyevans.net/&quot;&gt;Roda&lt;/a&gt; here to fit the whole app into one file. Install it with &lt;code&gt;gem install roda&lt;/code&gt; and then use this &lt;code&gt;config.ru&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;quot;roda&amp;quot;

$scores = []

class App &amp;lt; Roda
  plugin :json
  plugin :json_parser

  route do |r|
    r.post &amp;quot;score&amp;quot; do
      $scores &amp;lt;&amp;lt; r.params[&amp;quot;count&amp;quot;]
      $scores.sort.reverse[0..2]
    end
  end
end

run App.freeze.app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run this with &lt;code&gt;rackup&lt;/code&gt;, it should give you a server running on &lt;a href=&quot;http://localhost:9292&quot;&gt;http://localhost:9292&lt;/a&gt; that you can use to persist the scores. You can POST new scores to &lt;code&gt;/score&lt;/code&gt;, which returns the best 3 scores in the response. For demonstration purposes, we are just storing the leaderboard locally, in the global variable called &lt;code&gt;$scores&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, we need to again add a new variable to the game state, to keep the current state of the leaderboard locally:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def init args
    args.state.scene ||= :game
    args.state.score ||= 0
+   args.state.hi_scores ||= []
    args.state.dy ||= 0
    args.state.walls ||= []

    # etc.
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To send a POST request to the server, DragonRuby has a method called &lt;a href=&quot;https://docs.dragonruby.org/#/api/runtime?id=http_post_body&quot;&gt;http_post_body&lt;/a&gt;, one of the rare cases of asynchronous code in the framework. We have to wait for this &amp;quot;promise&amp;quot; to complete, therefore, we first store it in the game state. Add this to the &lt;code&gt;calc&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def calc
    # omitted

    if game_over?(args)
      # omitted

+     payload = &amp;quot;{\&amp;quot;count\&amp;quot;: #{args.state.score}}&amp;quot;
+     args.state.post_result ||= args.gtk.http_post_body &amp;quot;http://localhost:9292/score&amp;quot;,
+                                                        payload,
+                                                        [&amp;quot;Content-Type: application/json&amp;quot;, &amp;quot;Content-Length: #{payload.length}&amp;quot;]
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This sends (you have guessed it) the payload as a JSON object (&lt;code&gt;{ &amp;quot;count&amp;quot;: YOUR_CURRENT_SCORE }&lt;/code&gt;) to the &lt;code&gt;/score&lt;/code&gt; route.&lt;/p&gt;
&lt;p&gt;Now all that&amp;#39;s left is to retrieve the updated high score table once that &amp;quot;promise&amp;quot; has resolved. We do this in the &lt;code&gt;render&lt;/code&gt; method and render the leaderboard to the screen if we are in the &lt;code&gt;game_over&lt;/code&gt; scene:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def render
    # omitted

    elsif args.state.scene == :game_over
      args.outputs.labels &amp;lt;&amp;lt; { x: 640, y: 520, text: &amp;quot;GAME OVER&amp;quot;, size_px: 128, anchor_x: 0.5, anchor_y: 0.5 }

+     if args.state.post_result &amp;amp;&amp;amp; args.state.post_result[:complete]
+       if args.state.post_result[:http_response_code] == 200
+         args.state.hi_scores = args.gtk.parse_json args.state.post_result[:response_data]
+
+         args.state.hi_scores.each_with_index do |score, index|
+           args.outputs.labels &amp;lt;&amp;lt; { x: 560, y: 400 - index * 90, text: &amp;quot;#{index + 1}. #{score}&amp;quot;, size_px: 64 }
+         end
+       end
+     end
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, we can behold our high score table once the plane has crashed:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/dragonruby-leaderboard.png&quot; alt=&quot;Displaying a leaderboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;We are going to stop here. Just keep in mind that to restart the game, you&amp;#39;d now have to clear the &lt;code&gt;post_result&lt;/code&gt;, along with all other relevant game parameters.&lt;/p&gt;
&lt;h2&gt;Publishing&lt;/h2&gt;
&lt;p&gt;Now that we&amp;#39;ve built our first game, we want to tell the world about it! Luckily, DragonRuby comes with built-in support to publish games as HTML (i.e., WASM-based) exports on &lt;a href=&quot;https://itch.io/&quot;&gt;itch.io&lt;/a&gt;, an online marketplace for indie games. Let&amp;#39;s walk through the deployment process.&lt;/p&gt;
&lt;p&gt;First, create an itch.io account if you don&amp;#39;t have one already, and &lt;a href=&quot;https://itch.io/game/new&quot;&gt;create a new game&lt;/a&gt;. Give your game a title, and be sure to select &amp;quot;HTML&amp;quot; as the project type:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/dragonruby-itchio.png&quot; alt=&quot;Creating a game on itch.io&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now it&amp;#39;s time to fill out the game&amp;#39;s metadata for the actual bundled package. Open the file located at &lt;code&gt;mygame/metadata/game_metadata.txt&lt;/code&gt; and uncomment the first couple of lines. Then fill it out with your itch.io username, your game&amp;#39;s id and title, and add an icon if you want:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;devid=my_itch_io_username
devtitle=My Name
gameid=tappy-plane-demo
gametitle=Tappy Plane Demo
version=0.1
icon=metadata/icon.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that &lt;code&gt;devid&lt;/code&gt; must match your itch.io username, and &lt;code&gt;gameid&lt;/code&gt; must match the project&amp;#39;s URL path.&lt;/p&gt;
&lt;p&gt;After you&amp;#39;ve completed this step, it&amp;#39;s time to start the actual bundling. Run this command in the terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ ./dragonruby-publish --package mygame
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a &lt;code&gt;builds&lt;/code&gt; folder into which DragonRuby compiles binaries of all target platforms:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ ls builds
tappy-plane-demo-html5-0.1/             tappy-plane-demo-linux-raspberrypi-0.1/
tappy-plane-demo-html5.zip              tappy-plane-demo-linux-raspberrypi.bin*
tappy-plane-demo-linux-amd64-0.1/       tappy-plane-demo-linux-raspberrypi.zip
tappy-plane-demo-linux-amd64.bin*       tappy-plane-demo-mac-0.1/
tappy-plane-demo-linux-amd64.zip        tappy-plane-demo-macos.zip
tappy-plane-demo-linux-arm64-0.1/       tappy-plane-demo-windows-amd64-0.1/
tappy-plane-demo-linux-arm64.bin*       tappy-plane-demo-windows-amd64.exe*
tappy-plane-demo-linux-arm64.zip        tappy-plane-demo-windows-amd64.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on your operating system, you can now even go ahead and try the game binary locally. However, we are going to use the HTML export and upload it to itch.io. For this, just edit your game settings and select the &lt;code&gt;tappy-plane-demo-html5.zip&lt;/code&gt; file from the &lt;code&gt;builds&lt;/code&gt; folder. Make sure to tick the &amp;quot;This file will be played in the browser&amp;quot; checkbox:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/dragonruby-itchio-uploads.png&quot; alt=&quot;Uploading the HTML5 game to itch.io&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Finally, we have to make sure the viewport dimensions are configured properly, and &lt;code&gt;SharedArrayBuffer&lt;/code&gt; support is enabled (this is required for DragonRuby&amp;#39;s WASM engine to work).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/dragonruby-itchio-embed-options.png&quot; alt=&quot;Setting embed options on itch.io&quot;/&gt;&lt;/p&gt;
&lt;p&gt;After you have saved these changes, your game should be playable in the browser at the specified URL. Yay!&lt;/p&gt;
&lt;p&gt;Deploying to itch.io is only one option, though. DragonRuby helps you deploy to Steam, mobile platforms, and even to Raspberry Pi or virtual reality headsets. For more information, check out the &lt;a href=&quot;https://docs.dragonruby.org/&quot;&gt;DragonRuby official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this two-part series, we&amp;#39;ve taken DragonRuby from a fresh install to a complete, playable game — covering core game architecture, scene management, collisions, scoring, audio, and even online leaderboards and publishing. Along the way, we&amp;#39;ve seen how DragonRuby’s minimal, Ruby-flavored API makes it possible to build and ship games fast without sacrificing clarity or control.&lt;/p&gt;
&lt;p&gt;While our &amp;quot;Tappy Plane&amp;quot; clone is simple by design, it demonstrates the full workflow from idea to release. With these building blocks in place, you can now focus on creative touches — refining gameplay, adding polish, or inventing something entirely new.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Rendering Samples with Showcase for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/11/12/rendering-samples-with-showcase-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2025/11/12/rendering-samples-with-showcase-for-ruby-on-rails.html</id>
    <published>2025-11-12T00:00:00+00:00</published>
    <updated>2025-11-12T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the final part of our Showcase for Rails series, we&#039;ll look at its key feature: samples.</summary>
    <content type="html">&lt;p&gt;In parts one and two of this series, we familiarized ourselves with the ins and outs of Showcase.&lt;/p&gt;
&lt;p&gt;Now, we&amp;#39;ll dive into samples, Showcase&amp;#39;s main feature. Samples show how a component can be used in a real application.&lt;/p&gt;
&lt;h2&gt;Samples in our Ruby App&lt;/h2&gt;
&lt;p&gt;In our case, we have two samples, one for a small button and one for a large button:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/showcase_button_samples.png&quot; alt=&quot;Showcase button samples&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at how the samples are rendered. We&amp;#39;ll add one sample back to our preview file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# test/dummy/app/views/showcase/previews/components/_button.html.erb %&amp;gt;

&amp;lt;% showcase.badge :partial, :component %&amp;gt;
&amp;lt;% showcase.description &amp;quot;Button is our standard element for what to click on&amp;quot; %&amp;gt;

&amp;lt;% showcase.sample &amp;quot;Large&amp;quot;, description: &amp;quot;This is our larger button&amp;quot; do %&amp;gt;
  &amp;lt;%= render &amp;quot;components/button&amp;quot;, content: &amp;quot;Button content&amp;quot;, mode: :large %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we refresh the page in our browser, it should look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/showcase_large_button_sample.png&quot; alt=&quot;Showcase large button sample&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Sample Methods in Showcase for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;From our understanding of the gem&amp;#39;s architecture, the &lt;code&gt;showcase.sample&lt;/code&gt; method populates the &lt;code&gt;@samples&lt;/code&gt; instance variable of the &lt;code&gt;Showcase::Preview&lt;/code&gt; instance, which is then passed to the final view. Let&amp;#39;s take a look at the &lt;code&gt;Showcase::Preview#sample&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/preview.rb

class Showcase::Preview
  def sample(name, **options, &amp;amp;block)
    @samples &amp;lt;&amp;lt; Showcase::Sample.new(@view_context, name, **options).tap { _1.evaluate(&amp;amp;block) }
  end
end
# =&amp;gt; @samples = [Showcase::Sample instance]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the &lt;code&gt;Showcase::Sample&lt;/code&gt; initializer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb

class Showcase::Sample
  attr_reader :name, :id, :events, :details
  attr_reader :rendered, :source, :instrumented

  def initialize(view_context, name, description: nil, id: name.parameterize, syntax: :erb, events: nil, **details)
    @view_context = view_context
    @name, @id, @syntax, @details = name, id, syntax, details
    @events = Array(events)
    description description if description
  end

  def description(content = nil, &amp;amp;block)
    @description = content || @view_context.capture(&amp;amp;block) if content || block_given?
    @description
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is nothing very surprising in this initializer; most of it is just setting instance variables. Here is the current state of our &lt;code&gt;Showcase::Sample&lt;/code&gt; instance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;Showcase::Sample.new(view_context, &amp;quot;Large&amp;quot;, description: &amp;quot;This is our larger button&amp;quot;)
# =&amp;gt; @name         = &amp;quot;Large&amp;quot;
# =&amp;gt; @id           = &amp;quot;large&amp;quot;
# =&amp;gt; @description  = &amp;quot;This is our larger button&amp;quot;
# =&amp;gt; @syntax       = :erb
# =&amp;gt; @details      = {}
# =&amp;gt; @events       = []
# =&amp;gt; @view_context = The view context helper
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have our instance, we can call the &lt;code&gt;evaluate&lt;/code&gt; method on it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb

class Showcase::Sample
  def evaluate(&amp;amp;block)
    if block.arity.zero?
      consume(&amp;amp;block)
    else
      @view_context.capture(self, &amp;amp;block)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;About &lt;code&gt;block.arity&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If you are not familiar with &lt;code&gt;block.arity&lt;/code&gt;, it returns the number of arguments that the block expects. If the block does not expect any arguments, &lt;code&gt;block.arity&lt;/code&gt; will return &lt;code&gt;0&lt;/code&gt;. In our case, the block is the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;% showcase.sample &amp;quot;Large&amp;quot;, description: &amp;quot;This is our larger button&amp;quot; do %&amp;gt;
  &amp;lt;%= render &amp;quot;components/button&amp;quot;, content: &amp;quot;Button content&amp;quot;, mode: :large %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, our block does not expect any arguments, so &lt;code&gt;block.arity&lt;/code&gt; will return &lt;code&gt;0&lt;/code&gt;. This means that the &lt;code&gt;consume&lt;/code&gt; method will be called, with the block as an argument:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb

class Showcase::Sample
  def consume(&amp;amp;block)
    render(&amp;amp;block)
    extract_source(&amp;amp;block)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;About the &lt;code&gt;consume&lt;/code&gt; Method&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;consume&lt;/code&gt; method calls the &lt;code&gt;render&lt;/code&gt; and &lt;code&gt;extract_source&lt;/code&gt; methods, passing our block as an argument. Let&amp;#39;s have a look at the &lt;code&gt;render&lt;/code&gt; method first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb

class Showcase::Sample
  def render(&amp;amp;block)
    # TODO: Remove `is_a?` check when Rails 6.1 support is dropped.
    assigns = proc { @instrumented = _1 if _1.is_a?(ActiveSupport::Notifications::Event) }
    ActiveSupport::Notifications.subscribed(assigns, &amp;quot;render_partial.action_view&amp;quot;) do
      @rendered = @view_context.capture(&amp;amp;block)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;render&lt;/code&gt; method uses &lt;a href=&quot;https://edgeapi.rubyonrails.org/classes/ActiveSupport/Notifications.html&quot;&gt;&lt;code&gt;ActiveSupport::Notifications&lt;/code&gt;&lt;/a&gt; to subscribe to the &lt;code&gt;render_partial.action_view&lt;/code&gt; event. This event is triggered whenever a partial is rendered in Rails. In our case, the block passed to the &lt;code&gt;showcase.sample&lt;/code&gt; method will render the &lt;code&gt;components/button&lt;/code&gt; partial, so an event will be triggered and the &lt;code&gt;@instrumented&lt;/code&gt; instance variable will be set to the &lt;a href=&quot;https://edgeapi.rubyonrails.org/classes/ActiveSupport/Notifications/Event.html&quot;&gt;&lt;code&gt;ActiveSupport::Notifications::Event&lt;/code&gt;&lt;/a&gt; published by the rendering of the partial.&lt;/p&gt;
&lt;p&gt;This &lt;code&gt;ActiveSupport::Notifications::Event&lt;/code&gt; instance responds to the &lt;code&gt;duration&lt;/code&gt; and &lt;code&gt;allocation&lt;/code&gt; methods. It will be used later to display the duration and allocations of the sample in the view, as shown in the top right corner here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/showcase_sample_duration_allocations.png&quot; alt=&quot;Showcase button sample with duration and allocations&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Calling &lt;code&gt;extract_source&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Next, the &lt;code&gt;extract_source&lt;/code&gt; method is called, with the block as an argument:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb

class Showcase::Sample
 def extract_source(&amp;amp;block)
    source = extract_source_block_via_matched_indentation_from(*block.source_location)
    @source = @view_context.instance_exec(source, @syntax, &amp;amp;Showcase.sample_renderer)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;extract_source&lt;/code&gt; method first calls &lt;code&gt;extract_source_block_via_matched_indentation_from&lt;/code&gt; with &lt;code&gt;*block.source_location&lt;/code&gt; as arguments. If you&amp;#39;re not familiar with &lt;code&gt;block.source_location&lt;/code&gt;, it returns an array of two elements: the file path and line number where the block was defined. In our case, it will return something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;[
  &amp;quot;/path/to/test/dummy/app/views/showcase/previews/components/_button.html.erb&amp;quot;,
  4  # The line number where the block was defined
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The splat operator &lt;code&gt;*&lt;/code&gt; before &lt;code&gt;block.source_location&lt;/code&gt; is used to unpack the array into two separate arguments: the file path and the line number.&lt;/p&gt;
&lt;h3&gt;A More Complex Method&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s have a look at the &lt;code&gt;extract_source_block_via_matched_indentation_from&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb

class Showcase::Sample
  def extract_source_block_via_matched_indentation_from(file, source_location_index)
    # `Array`s are zero-indexed, but `source_location` indexes are not, hence `pred`.
    starting_line, *lines = File.readlines(file).slice(source_location_index.pred..)

    indentation = starting_line.match(/^\s+/).to_s
    matcher = /^#{indentation}\S/

    index = lines.index { _1.match?(matcher) }
    lines.slice!(index..) if index
    lines.join.strip_heredoc
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is quite a complex method to read, so let&amp;#39;s analyze it step by step. &lt;code&gt;File.readlines(file)&lt;/code&gt; reads all the lines of the file and returns them as an array. In our case, the result looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;File.readlines(file)
# [
#   &amp;quot;&amp;lt;% showcase.badge :partial, :component %&amp;gt;\n&amp;quot;,
#   &amp;quot;&amp;lt;% showcase.description \&amp;quot;Button is our standard element for what to click on\&amp;quot; %&amp;gt;\n&amp;quot;,
#   &amp;quot;\n&amp;quot;,
#   &amp;quot;&amp;lt;% showcase.sample \&amp;quot;Large\&amp;quot;, description: \&amp;quot;This is our larger button\&amp;quot; do %&amp;gt;\n&amp;quot;,
#   &amp;quot;  &amp;lt;%= render \&amp;quot;components/button\&amp;quot;, content: \&amp;quot;Button content\&amp;quot;, mode: :large %&amp;gt;\n&amp;quot;,
#   &amp;quot;&amp;lt;% end %&amp;gt;\n&amp;quot;
# ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;slice(source_location_index.pred..)&lt;/code&gt; method call gets lines starting from where the block was defined. The &lt;code&gt;pred&lt;/code&gt; method is used to get the previous index, as &lt;code&gt;source_location&lt;/code&gt; returns a 1-based index, while Ruby arrays are 0-based:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;File.readlines(file).slice(source_location_index.pred..)
# [
#   &amp;quot;&amp;lt;% showcase.sample \&amp;quot;Large\&amp;quot;, description: \&amp;quot;This is our larger button\&amp;quot; do %&amp;gt;\n&amp;quot;,
#     &amp;quot;  &amp;lt;%= render \&amp;quot;components/button\&amp;quot;, content: \&amp;quot;Button content\&amp;quot;, mode: :large %&amp;gt;\n&amp;quot;,
#   &amp;quot;&amp;lt;% end %&amp;gt;\n&amp;quot;
# ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we now view the whole line, the &lt;code&gt;starting_line&lt;/code&gt; variable contains the line where the block was defined, and the &lt;code&gt;lines&lt;/code&gt; variable contains all the lines after it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;starting_line, *lines = File.readlines(file).slice(source_location_index.pred..)

starting_line
# &amp;quot;&amp;lt;% showcase.sample \&amp;quot;Large\&amp;quot;, description: \&amp;quot;This is our larger button\&amp;quot; do %&amp;gt;\n&amp;quot;

lines
# [
#   &amp;quot;  &amp;lt;%= render \&amp;quot;components/button\&amp;quot;, content: \&amp;quot;Button content\&amp;quot;, mode: :large %&amp;gt;\n&amp;quot;,
#   &amp;quot;&amp;lt;% end %&amp;gt;\n&amp;quot;
# ]
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, the &lt;code&gt;indentation&lt;/code&gt; variable will store the leading whitespace of the &lt;code&gt;starting_line&lt;/code&gt;. The regex &lt;code&gt;/^\s+/&lt;/code&gt; matches one or more whitespace characters at the beginning of a string, and &lt;code&gt;to_s&lt;/code&gt; converts the match result to a string. In our case, the &lt;code&gt;starting_line&lt;/code&gt; doesn&amp;#39;t have any leading whitespace, so &lt;code&gt;indentation&lt;/code&gt; is an empty string.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;indentation = starting_line.match(/^\s+/).to_s
# &amp;quot;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then the &lt;code&gt;matcher&lt;/code&gt; regex will match lines that start with the same indentation, followed by a non-whitespace character. If our code is properly indented, the &lt;code&gt;index&lt;/code&gt; variable will return the index of the block&amp;#39;s &lt;code&gt;end&lt;/code&gt; keyword:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;matcher = /^#{indentation}\S/
index = lines.index { _1.match?(matcher) }
# 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, &lt;code&gt;lines.slice!(index..)&lt;/code&gt; removes all the lines from the &lt;code&gt;index&lt;/code&gt; to the end of the array. In our case, it will remove the last line of the block, the &lt;code&gt;end&lt;/code&gt; keyword:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;lines.slice!(index..) if index
# [
#   &amp;quot;  &amp;lt;%= render \&amp;quot;components/button\&amp;quot;, content: \&amp;quot;Button content\&amp;quot;, mode: :large %&amp;gt;\n&amp;quot;
# ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last step is simply to convert the array of lines back to a string and trim extra white space:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;lines.join.strip_heredoc
# &amp;quot;&amp;lt;%= render \&amp;quot;components/button\&amp;quot;, content: \&amp;quot;Button content\&amp;quot;, mode: :large %&amp;gt;\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Phew! That was quite a complicated method, but we managed to get through it by reading carefully, line by line.&lt;/p&gt;
&lt;h3&gt;Back To &lt;code&gt;extract_source&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;We can now go back to our &lt;code&gt;extract_source&lt;/code&gt; method and try to understand the last line of code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb

class Showcase::Sample
 def extract_source(&amp;amp;block)
    source = extract_source_block_via_matched_indentation_from(*block.source_location)
    @source = @view_context.instance_exec(source, @syntax, &amp;amp;Showcase.sample_renderer)
  end
end
# source  = &amp;quot;&amp;lt;%= render \&amp;quot;components/button\&amp;quot;, content: \&amp;quot;Button content\&amp;quot;, mode: :large %&amp;gt;\n&amp;quot;
# @syntax = :erb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Understanding this line requires a solid grasp of the Ruby language. First, we have to know what &lt;code&gt;Showcase.sample_renderer&lt;/code&gt; is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# lib/showcase.rb

module Showcase
  def self.sample_renderer
    @sample_renderer ||=
      begin
        gem &amp;quot;rouge&amp;quot;
        require &amp;quot;rouge&amp;quot;

        formatter = Rouge::Formatters::HTML.new
        @sample_renderer = -&amp;gt;(source, syntax) do
          lexed = Rouge::Lexer.find(syntax).lex(source)
          formatter.format(lexed).html_safe
        end
      rescue LoadError
        proc { _1 }
      end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Rouge Gem in Showcase for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;By default, &lt;code&gt;Showcase.sample_renderer&lt;/code&gt; uses the &lt;a href=&quot;https://github.com/rouge-ruby/rouge&quot;&gt;&lt;code&gt;rouge&lt;/code&gt; gem&lt;/a&gt; to highlight the syntax of the source code. However, developers using the &lt;code&gt;showcase&lt;/code&gt; engine could choose a different syntax highlighter, as the &lt;code&gt;sample_renderer&lt;/code&gt; is configurable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# lib/showcase.rb

module Showcase
  # This line means that the `sample_renderer` can be overridden
  singleton_class.attr_writer :sample_renderer
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we remember correctly, the &lt;code&gt;rouge&lt;/code&gt; gem is not listed in the &lt;code&gt;showcase.gemspec&lt;/code&gt; file, which means that it is required to use the &lt;code&gt;showcase&lt;/code&gt; engine:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# lib/showcase.rb

module Showcase
  def self.sample_renderer
    @sample_renderer ||=
      begin
        gem &amp;quot;rouge&amp;quot;
        require &amp;quot;rouge&amp;quot;
        # ...
      rescue LoadError
        proc { _1 }
      end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;rescue LoadError&lt;/code&gt; idiom means that if &lt;code&gt;rouge&lt;/code&gt; is not present in the &lt;code&gt;Gemfile&lt;/code&gt;, the &lt;code&gt;sample_renderer&lt;/code&gt; will return a simple &lt;code&gt;proc&lt;/code&gt; that returns the source code without any syntax highlighting. If it succeeds, it will return a lambda function that takes two arguments: &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;syntax&lt;/code&gt;, and a highlighted version of the source code.&lt;/p&gt;
&lt;p&gt;We can actually test this in the Rails console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;source = &amp;quot;&amp;lt;%= render \&amp;quot;components/button\&amp;quot;, content: \&amp;quot;Button content\&amp;quot;, mode: :large %&amp;gt;\n&amp;quot;
syntax = :erb
Showcase.sample_renderer.call(source, syntax)
# &amp;quot;&amp;lt;span class=\&amp;quot;cp\&amp;quot;&amp;gt;&amp;amp;lt;%=&amp;lt;/span&amp;gt; &amp;lt;span class=\&amp;quot;n\&amp;quot;&amp;gt;render&amp;lt;/span&amp;gt; &amp;lt;span class=\&amp;quot;s2\&amp;quot;&amp;gt;\&amp;quot;components/button\&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;span class=\&amp;quot;p\&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=\&amp;quot;ss\&amp;quot;&amp;gt;content: &amp;lt;/span&amp;gt;&amp;lt;span class=\&amp;quot;s2\&amp;quot;&amp;gt;\&amp;quot;Button content\&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;span class=\&amp;quot;p\&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=\&amp;quot;ss\&amp;quot;&amp;gt;mode: :large&amp;lt;/span&amp;gt; &amp;lt;span class=\&amp;quot;cp\&amp;quot;&amp;gt;%&amp;amp;gt;&amp;lt;/span&amp;gt;\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The funny &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;n&lt;/code&gt;, &lt;code&gt;s2&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;, and &lt;code&gt;ss&lt;/code&gt; classes are the CSS classes used by &lt;code&gt;Rouge&lt;/code&gt; to style the syntax highlighting, as shown in the following picture:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-11/showcase_syntax_highlighting.png&quot; alt=&quot;Showcase syntax highlighting&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now that we have a good understanding of the &lt;code&gt;Showcase.sample_renderer&lt;/code&gt;, we can go back to the &lt;code&gt;extract_source&lt;/code&gt; method and ask ourselves why the last line of code is so complicated:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb

class Showcase::Sample
 def extract_source(&amp;amp;block)
    source = extract_source_block_via_matched_indentation_from(*block.source_location)
    @source = @view_context.instance_exec(source, @syntax, &amp;amp;Showcase.sample_renderer)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In fact, if we make a small experiment and change it to use the &lt;code&gt;Showcase.sample_renderer&lt;/code&gt; directly, it will work just as well:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/sample.rb
class Showcase::Sample
  def extract_source(&amp;amp;block)
    source = extract_source_block_via_matched_indentation_from(*block.source_location)
    @source = Showcase.sample_renderer.call(source, @syntax)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So why does the original code use &lt;code&gt;instance_exec&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;As we are in a gem, we need to allow users of the gem to use it in a wide variety of contexts. The &lt;code&gt;instance_exec&lt;/code&gt; method allows us to execute the &lt;code&gt;Showcase.sample_renderer&lt;/code&gt; lambda in the context of the &lt;code&gt;@view_context&lt;/code&gt;. This makes all view helpers available in the lambda, which could be useful if the &lt;code&gt;sample_renderer&lt;/code&gt; needed to use any view helper methods, such as &lt;code&gt;sanitize&lt;/code&gt;, &lt;code&gt;link_to&lt;/code&gt;, or any other method that is available in the view context. However, this is a very niche use case and probably not useful in 99% of situations.&lt;/p&gt;
&lt;h2&gt;Sample Instance Variables Render the View&lt;/h2&gt;
&lt;p&gt;We are finally done with our &lt;code&gt;Showcase::Sample&lt;/code&gt; instance. Its instance variables now hold all the data necessary to render our view:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# =&amp;gt; @name         = &amp;quot;Large&amp;quot;
# =&amp;gt; @id           = &amp;quot;large&amp;quot;
# =&amp;gt; @description  = &amp;quot;This is our larger button&amp;quot;
# =&amp;gt; @source       = &amp;quot;&amp;lt;span class=\&amp;quot;cp\&amp;quot;&amp;gt;&amp;amp;lt;%=&amp;lt;/span&amp;gt;...&amp;lt;span class=\&amp;quot;cp\&amp;quot;&amp;gt;%&amp;amp;gt;&amp;lt;/span&amp;gt;\n&amp;quot;
# =&amp;gt; @rendered     = &amp;quot;&amp;lt;%= render \&amp;quot;components/button\&amp;quot;, content: \&amp;quot;Button content\&amp;quot;, mode: :large %&amp;gt;\n&amp;quot;
# =&amp;gt; @instrumented = ActiveSupport::Notifications::Event instance
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the view, we can render the &lt;code&gt;&amp;quot;showcase/engine/sample&amp;quot;&lt;/code&gt; partial for each of the preview&amp;#39;s samples:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/showcase/engine/_preview.html.erb %&amp;gt;

&amp;lt;% if preview.samples.any? %&amp;gt;
  &amp;lt;section&amp;gt;
    &amp;lt;h2 class=&amp;quot;...&amp;quot;&amp;gt;Samples&amp;lt;/h2&amp;gt;
    &amp;lt;%= render partial: &amp;quot;showcase/engine/sample&amp;quot;, collection: preview.samples %&amp;gt;
  &amp;lt;/section&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we have a look at the &lt;code&gt;&amp;quot;showcase/engine/sample&amp;quot;&lt;/code&gt; partial, it renders a link with the name of the sample:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/showcase/engine/_sample.html.erb %&amp;gt;

&amp;lt;%= link_to sample.name, &amp;quot;##{sample.id}&amp;quot;, class: &amp;quot;...&amp;quot; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It also renders figures to show the time needed to render the component and its number of allocations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/showcase/engine/_sample.html.erb %&amp;gt;

&amp;lt;% if event = sample.instrumented %&amp;gt;
  &amp;lt;div class=&amp;quot;...&amp;quot;&amp;gt;
    &amp;lt;span&amp;gt;&amp;lt;%= event.duration.round(1) %&amp;gt;ms&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;&amp;lt;%= event.allocations %&amp;gt; allocs&amp;lt;/span&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It renders the actual HTML of the component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/showcase/engine/_sample.html.erb %&amp;gt;

&amp;lt;% if sample.rendered %&amp;gt;
  &amp;lt;section class=&amp;quot;...&amp;quot;&amp;gt;
    &amp;lt;%= sample.rendered %&amp;gt;
  &amp;lt;/section&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, it renders the source code necessary to render the component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/showcase/engine/_sample.html.erb %&amp;gt;

&amp;lt;% if sample.source %&amp;gt;
  &amp;lt;details&amp;gt;
    &amp;lt;summary class=&amp;quot;...&amp;quot;&amp;gt;View Source&amp;lt;/summary&amp;gt;

    &amp;lt;section class=&amp;quot;...&amp;quot;&amp;gt;
      &amp;lt;pre&amp;gt;&amp;lt;%= sample.source %&amp;gt;&amp;lt;/pre&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/details&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now understand how all values are computed. There is no more magic left for us to uncover in the Showcase gem!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this series on Showcase, we learned how Rails engines work: their main files and how to run them locally. But we also learned much more than that. We broadened our knowledge of Ruby and Rails, learning about &lt;code&gt;block.arity&lt;/code&gt;, the &lt;code&gt;view_context&lt;/code&gt; helper, &lt;code&gt;ActiveSupport::Notifications&lt;/code&gt;, and how to use &lt;code&gt;rouge&lt;/code&gt; to highlight code syntax in our Rails applications.&lt;/p&gt;
&lt;p&gt;Instead of relying solely on documentation, we can gain a much deeper understanding of Ruby by reading source code. We can even end up adding new features to engines and contributing to open-source, improving our skills and giving back to the community.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Game Development with DragonRuby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/11/05/an-introduction-to-game-development-with-dragonruby.html"/>
    <id>https://blog.appsignal.com/2025/11/05/an-introduction-to-game-development-with-dragonruby.html</id>
    <published>2025-11-05T00:00:00+00:00</published>
    <updated>2025-11-05T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll cover the basics of game development with DragonRuby in the first part of this two-part series.</summary>
    <content type="html">&lt;p&gt;The DragonRuby Game Toolkit is a powerful, cross-platform 2D game engine that allows you to create fun game titles while staying in your favorite developer-friendly language. What&amp;#39;s not to love?&lt;/p&gt;
&lt;p&gt;In this post, we are going to cover the basics of game development with DragonRuby. We will use a &amp;quot;Flappy Bird&amp;quot; clone to explain the fundamental concepts.&lt;/p&gt;
&lt;p&gt;But before we get started, let&amp;#39;s address two initial concerns you might have about DragonRuby right off the bat.&lt;/p&gt;
&lt;h2&gt;Initial Concerns&lt;/h2&gt;
&lt;p&gt;First of all, DragonRuby is not free. Yes, that&amp;#39;s correct, it costs money — at the time of writing, the standard license is a one-time purchase of $48. Given that you get a state-of-the-art 2D graphics engine boxed with integrated publishing/exporting to &lt;a href=&quot;https://itch.io/&quot;&gt;itch.io&lt;/a&gt; and &lt;a href=&quot;https://store.steampowered.com/&quot;&gt;Steam&lt;/a&gt;, in my opinion this is more than justified. In addition, there are semi-regular discounts at special occasions like Ruby or gaming conferences and conventions, so watch out for these!&lt;/p&gt;
&lt;p&gt;Second, you might have heard that Ruby is &amp;quot;slow&amp;quot;. While we can probably agree that this is a relative term in general, it is even more fuzzy in this case. Typically when referring to &amp;quot;Ruby&amp;quot; we mean the CRuby interpreter, which is what you most likely run on the machines that serve your Rails apps. DragonRuby is a whole different &lt;em&gt;runtime&lt;/em&gt; though, based on &lt;a href=&quot;https://mruby.org/&quot;&gt;mruby&lt;/a&gt; (that is, with an intentionally smaller subset of functionality, but optimized for game development). We&amp;#39;ll take a closer look later in this article, but &lt;a href=&quot;https://docs.dragonruby.org/##/misc/faq?id=but-ruby-is-slow&quot;&gt;the DragonRuby docs have some videos comparing its performance to other game engines&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.dragonruby.org/##/misc/philosophy&quot;&gt;Check out DragonRuby&amp;#39;s docs page about their overall philosophy and goals&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;First Steps: Game Development with DragonRuby&lt;/h2&gt;
&lt;p&gt;To start out developing a game, download it from the &lt;a href=&quot;https://dragonruby.itch.io/dragonruby-gtk&quot;&gt;DragonRuby Game Toolkit&lt;/a&gt; after you&amp;#39;ve purchased a standard license. Make sure you select the correct download according to your platform:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/blog/2025-10/download-dr.png&quot;&gt;Download DragonRuby&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once you unpack the zip file, this is the resulting directory structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ ll
CHANGELOG-CURR.txt
CHANGELOG-PREV.txt
console-logo.png
ctags-emacs
ctags-vim
docs/
dragonruby*
dragonruby-controller.png
dragonruby-httpd*
dragonruby-publish*
dragonruby.png
eula.txt
font.ttf
mygame/
open-source-licenses.txt
README.txt
samples/
VERSION.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All you need to get started is the &lt;code&gt;dragonruby&lt;/code&gt; executable and the &lt;code&gt;mygame&lt;/code&gt; folder, which contains the game logic. Here&amp;#39;s a general best practice that might sound counterintuitive at first, but it actually makes great sense: Do not keep this folder in a shared location and refer to it when developing a new game, as this will set you up for trouble.&lt;/p&gt;
&lt;p&gt;Instead, &lt;em&gt;always download a fresh copy of the GTK and use the predefined structure&lt;/em&gt;. What might seem wasteful actually makes sense, because the moment you package and ship your game, the &lt;code&gt;dragonruby-publish&lt;/code&gt; executable expects files to be in certain paths and you&amp;#39;ll have to deal with a lot of confusing issues if you rename them. So, even though you might have thought up that very catchy title for your game, it&amp;#39;s fine to leave the folder name &lt;code&gt;mygame&lt;/code&gt; and just set the game name in the metadata. We&amp;#39;ll talk about that later.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re curious about the limits of DragonRuby and what functionality and interfacing it provides, check out the &lt;code&gt;samples&lt;/code&gt; folder. It contains an abundance of examples and even whole games of different genres.&lt;/p&gt;
&lt;p&gt;To round off this section, let&amp;#39;s review how DragonRuby interfaces with MRuby, graphics rendering, and hardware I/O. It employs a layered approach where it encapsulates MRuby at the lowest layer, then adds optimizations for its cross-platform targets and the &lt;a href=&quot;https://www.libsdl.org/&quot;&gt;Simple DirectMedia Layer&lt;/a&gt; library, with Ruby bindings to get consistent access to low-level resources. The &lt;a href=&quot;https://docs.dragonruby.org/##/misc/faq?id=what-does-multilevel-cross-platform-mean&quot;&gt;DragonRuby docs&lt;/a&gt; have more context on this matter.&lt;/p&gt;
&lt;p&gt;A further surprise for a seasoned Ruby developer is that there is no concept of &amp;quot;gems&amp;quot;. This is another trait it inherits from MRuby. In fact, there are many third-party libraries, but they are not included via a Gemfile and managed by Bundler. Instead, it&amp;#39;s a more manual process requiring you to either clone git repositories manually (e.g., under &lt;code&gt;mygame/lib&lt;/code&gt;), or use the built-in &lt;a href=&quot;https://docs.dragonruby.org/##/api/runtime?id=download_stb_rb_raw&quot;&gt;download_stb_rb&lt;/a&gt; function to download and require external code.&lt;/p&gt;
&lt;h2&gt;Getting Started with DragonRuby Development&lt;/h2&gt;
&lt;p&gt;Now let&amp;#39;s take our first look at the game. For this, just start the &lt;code&gt;dragonruby&lt;/code&gt; executable from a terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ ./dragonruby
* [Engine] Log started at 2025/8/29 10:26:32
* [Engine] DragonRuby Game Toolkit
* [Engine]   Version: 6.30
* [Engine]   Tier: Standard
* [Engine]   GIT Hash: 4fe7c8c45b2fef5c393a91b5f128c490aa5529ca
* [Engine]   Build Date: Aug 18 2025 19:55:47
* [Engine] Platform: Mac OS X
- [Engine] Process Working Directory: /Users/jrubisch/Documents/_CODE/demo/dragonruby-gtk-macos/dragonruby-macos/
- [Engine] Game Dir: /Users/jrubisch/Documents/_CODE/demo/dragonruby-gtk-macos/dragonruby-macos//mygame
- [Engine] Game ID: hello-SDL
- [Engine] Game Title: Update metadata/game_metadata.txt in your mygame directory to change this title.
- [Engine] Game Version: 1.0
- [Engine] Game Package ID: org.dragonruby.hello-SDL
- [Engine] Game Developer ID: dragonruby
- [Engine] Game Developer Title: DragonRuby LLC
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&amp;#39;ve shortened the log output a bit to highlight the most salient information: the GTK version and platform, information about our game directory, and metadata. More interestingly, though, a window appears with some educational copy and animation:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/blog/2025-10/dragonruby-start.gif&quot;&gt;DragonRuby start&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The Anatomy of a DragonRuby Game&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s examine this starter demo to get a grasp of the basic building blocks of a DragonRuby game. For this, let&amp;#39;s open &lt;code&gt;mygame/main.rb&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def tick args
  args.state.logo_rect ||= { x: 576,
                             y: 200,
                             w: 128,
                             h: 101 }

  args.outputs.labels  &amp;lt;&amp;lt; { x: 640,
                            y: 600,
                            text: &amp;#39;Hello World!&amp;#39;,
                            size_px: 30,
                            anchor_x: 0.5,
                            anchor_y: 0.5 }

  ## ... more args wrangling
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right off the bat, two things stand out that tell us we&amp;#39;re in a whole different Ruby world here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There&amp;#39;s no Ruby class defined. There&amp;#39;s not even a &lt;code&gt;main&lt;/code&gt; method. Instead, all we get is this &lt;code&gt;tick&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;args&lt;/code&gt; seems to be something like a god object.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On a very high level, you can imagine the DragonRuby framework as an infinite loop which calls this &lt;code&gt;tick&lt;/code&gt; method 60 times a second — we call this the &lt;strong&gt;game loop&lt;/strong&gt;. Everything you want your game to do has to happen here. The &lt;code&gt;args&lt;/code&gt; object is a structure that wraps a good part of the whole DragonRuby API, along with the game state (sprite positions, scores, etc.). Mainly, it covers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I/O (video, sound, controllers)&lt;/li&gt;
&lt;li&gt;Event handling&lt;/li&gt;
&lt;li&gt;Capturing and managing game state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can observe some of this happening here already: &lt;code&gt;args.state&lt;/code&gt; is something akin to an &lt;code&gt;OpenStruct&lt;/code&gt; where you can add, reference, and modify keys as you go. In this case, we initialize the &lt;code&gt;logo_rect&lt;/code&gt; key with screen coordinates and dimensions (&lt;code&gt;x, y, w, h&lt;/code&gt;). Note that this key is completely arbitrary. We &lt;strong&gt;have&lt;/strong&gt; to use the conditional assignment operator (&lt;code&gt;||=&lt;/code&gt;) here, because, remember, this method is called repeatedly, and we only want to set it in the first iteration.&lt;/p&gt;
&lt;p&gt;Next, we append (&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;) a hash to &lt;code&gt;args.outputs.labels&lt;/code&gt;. You can probably guess that this is what draws &amp;quot;Hello World&amp;quot; to the screen at the specified position.&lt;/p&gt;
&lt;h3&gt;Drawing and Animating Sprites&lt;/h3&gt;
&lt;p&gt;A bit further below we find this snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;args.outputs.sprites &amp;lt;&amp;lt; { x: args.state.logo_rect.x,
                          y: args.state.logo_rect.y,
                          w: args.state.logo_rect.w,
                          h: args.state.logo_rect.h,
                          path: &amp;#39;dragonruby.png&amp;#39;,
                          angle: Kernel.tick_count }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without exaggeration, this type of interaction is the heart of every 2D computer game. You see, drawing primitives such as lines, points, or rectangles programmatically is far too computationally expensive to do in these 16.67 milliseconds of a game tick. At least as the count of elements in your game starts to grow, you will quickly reach an upper limit of what&amp;#39;s possible. That&amp;#39;s why pre-drawn graphics known as &lt;strong&gt;sprites&lt;/strong&gt; have been used for this purpose since the invention of video games.&lt;/p&gt;
&lt;p&gt;Here, we encounter the simplest case: Just opening an image file (in this case &lt;code&gt;dragonruby.png&lt;/code&gt; in the root directory) and rendering it at a specific location with specific dimensions.&lt;/p&gt;
&lt;p&gt;This example even goes a bit further by animating it. Notice the &lt;code&gt;Kernel.tick_count&lt;/code&gt; method? It reports how many ticks have passed since you started the game, and this will increment by 1 — in every tick, of course. That&amp;#39;s why we can use it to rotate our sprite by assigning it to &lt;code&gt;angle&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you think that you are limited by this technique, you are mistaken: You can go a long way by cleverly organizing and animating your sprites, as we will see later on.&lt;/p&gt;
&lt;p&gt;The sprite is positioned at the location designated by &lt;code&gt;args.state.logo_rect&lt;/code&gt;. When you are developing a game, it can come in handy to inspect the contents of the &lt;code&gt;state&lt;/code&gt; structure. That&amp;#39;s where the &lt;strong&gt;DragonRuby Console&lt;/strong&gt; comes into play. While running your game, press &lt;code&gt;`&lt;/code&gt; (the backtick key), and key in &lt;code&gt;args.state.logo_rect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/blog/2025-10/dragonruby-console.png&quot;&gt;DragonRuby console&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here, you can inspect and even interact with your game very conveniently.&lt;/p&gt;
&lt;h3&gt;Checking Inputs and Updating Game State&lt;/h3&gt;
&lt;p&gt;While the demo game is open, press some arrow keys. You will notice that the rotating logo starts moving:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/blog/2025-10/dragonruby-input.gif&quot;&gt;DragonRuby input&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can find the reason for this in the bottom part of the game:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;if args.inputs.keyboard.left
  args.state.logo_rect.x -= 10
elsif args.inputs.keyboard.right
  args.state.logo_rect.x += 10
end

if args.inputs.keyboard.down
  args.state.logo_rect.y -= 10
elsif args.inputs.keyboard.up
  args.state.logo_rect.y += 10
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use &lt;code&gt;args.inputs.keyboard&lt;/code&gt; to check whether a certain key has been pressed in the current game tick. &lt;code&gt;left, right, up, down&lt;/code&gt; are convenience methods that return &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; depending on the state of the respective arrow keys.&lt;/p&gt;
&lt;p&gt;When they are pressed, we update the coordinates of &lt;code&gt;args.state.logo_rect&lt;/code&gt;. In the following game tick, this new position will be used to draw the logo sprite, and we&amp;#39;ve come full circle.&lt;/p&gt;
&lt;h2&gt;Example Game: A Flappy Bird Clone&lt;/h2&gt;
&lt;p&gt;To further deepen our understanding of game development, let&amp;#39;s build an exciting example game — a ... 🥁 ... Flappy Bird clone.&lt;/p&gt;
&lt;p&gt;The first and maybe all too obvious question is: where do we get our sprite images from? In the age of generative AI, the most straightforward answer would probably be to have your favorite chat agent create them. If you&amp;#39;re more classically inclined and want to support other indie creators, a safe place to grab some quality assets is &lt;a href=&quot;https://kenney.nl/&quot;&gt;Kenney&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As it turns out, they have a pack called &lt;a href=&quot;https://kenney.nl/assets/tappy-plane&quot;&gt;Tappy Plane&lt;/a&gt;, which contains precisely what we need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a character (a plane)&lt;/li&gt;
&lt;li&gt;obstacles (rocks)&lt;/li&gt;
&lt;li&gt;an unobtrusive background&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Copy all the sprites into &lt;code&gt;mygame/sprites&lt;/code&gt; and we&amp;#39;re ready to get going.&lt;/p&gt;
&lt;h3&gt;Draw and Animate the Background&lt;/h3&gt;
&lt;p&gt;First, we are going to draw the background, which is just a static sprite. Remove everything inside the &lt;code&gt;tick&lt;/code&gt; method and replace it with a simple array concatenation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def tick args
  args.outputs.sprites &amp;lt;&amp;lt; { x: 0, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;If you now run &lt;code&gt;dragonruby&lt;/code&gt; from the root directory, you&amp;#39;ll see the background painted on the canvas.&lt;/p&gt;
&lt;p&gt;Now it&amp;#39;d be nice to add some movement to the scene by animating and looping the background.&lt;/p&gt;
&lt;p&gt;To achieve this, we just have to adjust our rendering logic a little bit:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def tick args
  args.outputs.sprites &amp;lt;&amp;lt; { x: 0 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
  args.outputs.sprites &amp;lt;&amp;lt; { x: 1280 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the first change is that we are now rendering the same sprite twice, albeit with an &lt;code&gt;x&lt;/code&gt; offset (the game width — 1280). Then, we use &lt;code&gt;Kernel.tick_count&lt;/code&gt; to access how many ticks have passed since the game started, and use it to move the &lt;code&gt;x&lt;/code&gt; position to the left and subtract it. The result looks like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/blog/2025-10/dragonruby-background.gif&quot;&gt;DragonRuby background&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Rendering the Player Sprite&lt;/h3&gt;
&lt;p&gt;Next, we&amp;#39;ll draw the plane. Before we do that, though, we&amp;#39;ll instantiate its position in &lt;code&gt;args.state&lt;/code&gt; like in the example above. This way, it will &amp;quot;survive&amp;quot; ticks and we can modify the y coordinate by applying gravity and jumping. Once that is done, we can use this position and the sprite image&amp;#39;s path to actually display it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  def tick args
+   args.state.plane_pos ||= { x: 640,
+                              y: 360,
+                              w: 88,
+                              h: 73,
+                              anchor_x: 0.5,
+                              anchor_y: 0.5 }

    args.outputs.sprites &amp;lt;&amp;lt; { x: 0 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
    args.outputs.sprites &amp;lt;&amp;lt; { x: 1280 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }

+   args.outputs.sprites &amp;lt;&amp;lt; { **args.state.plane_pos,
+                             path: &amp;quot;sprites/Planes/planeRed1.png&amp;quot; }
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that order is important here: Had we drawn the plane &lt;strong&gt;before&lt;/strong&gt; the background, it would be covered by it and invisible! And voilà, here&amp;#39;s our plane hovering in front of the mountains:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/blog/2025-10/dragonruby-plane.gif&quot;&gt;DragonRuby plane&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Applying Forces&lt;/h2&gt;
&lt;p&gt;In the final section of this introduction to DragonRuby, we&amp;#39;ll touch on game physics. We&amp;#39;ll apply gravity to the plane, and listen for input on the keyboard to nudge it upwards. So, first of all, we define &lt;code&gt;GRAVITY&lt;/code&gt; as a constant. Then we have to dig up some high school kinetics. We know that gravity is a force that is directed downwards, and that force equals mass times acceleration. So we initialize a new key on &lt;code&gt;args.state&lt;/code&gt; called &lt;code&gt;dy&lt;/code&gt; (the difference of the y coordinate from one tick to the next) with 0.&lt;/p&gt;
&lt;p&gt;Because it&amp;#39;s pointing downward, we then subtract &lt;code&gt;GRAVITY&lt;/code&gt; from this variable. What this means is that the distance in y direction covered from frame to frame increases — it &lt;em&gt;accelerates&lt;/em&gt;. Finally, we add this distance to the plane&amp;#39;s current y position, and the result is an avatar that falls out of the game canvas at the bottom:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;+ GRAVITY = 0.5

  def tick args
+   args.state.dy ||= 0
    args.state.plane_pos ||= { x: 640,
                               y: 360,
                               w: 88,
                               h: 73,
                               anchor_x: 0.5,
                               anchor_y: 0.5 }

    args.outputs.sprites &amp;lt;&amp;lt; { x: 0 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
    args.outputs.sprites &amp;lt;&amp;lt; { x: 1280 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }

+   args.state.dy -= GRAVITY

+   args.state.plane_pos.y += args.state.dy

    args.outputs.sprites &amp;lt;&amp;lt; { **args.state.plane_pos,
                              path: &amp;quot;sprites/Planes/planeRed1.png&amp;quot; }
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;/images/blog/2025-10/dragonruby-gravity.gif&quot;&gt;DragonRuby gravity&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Checking Inputs&lt;/h3&gt;
&lt;p&gt;Before we close off this article, let&amp;#39;s make sure players have a chance to survive the first few seconds. To do this, we are going to listen for input on the space bar or left mouse button to &amp;quot;flap&amp;quot;, i.e., make the plane rise a couple of pixels. To simplify things, we are going to just add a constant value to &lt;code&gt;dy&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  GRAVITY = 0.5
+ FLAP_POWER = 10

  def tick args
    args.state.dy ||= 0
    args.state.plane_pos ||= { x: 640,
                               y: 360,
                               w: 88,
                               h: 73,
                               anchor_x: 0.5,
                               anchor_y: 0.5 }

    args.outputs.sprites &amp;lt;&amp;lt; { x: 0 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }
    args.outputs.sprites &amp;lt;&amp;lt; { x: 1280 - Kernel.tick_count % 1280, y: 0, w: 1280, h: 720, path: &amp;quot;sprites/background.png&amp;quot; }

    args.state.dy -= GRAVITY

+   if args.inputs.mouse.down || args.inputs.keyboard.key_down.space
+     args.state.dy += FLAP_POWER
+   end

    args.state.plane_pos.y += args.state.dy

    args.outputs.sprites &amp;lt;&amp;lt; { **args.state.plane_pos,
                              path: &amp;quot;sprites/Planes/planeRed1.png&amp;quot; }
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, whenever a player either clicks the mouse or hits the space bar, the value of &lt;code&gt;FLAP_POWER&lt;/code&gt; gets added to the y coordinate difference applied in that tick. The result is that with a little practice, you can let the plane hover:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/blog/2025-10/dragonruby-flap.gif&quot;&gt;DragonRuby flap&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this first part of our two-part series on DragonRuby, we&amp;#39;ve taken DragonRuby from a fresh download all the way through to building a basic but playable Flappy Bird clone, covering setup, the game loop, state management, rendering sprites, handling inputs, and applying simple physics.&lt;/p&gt;
&lt;p&gt;Along the way, we&amp;#39;ve seen how DragonRuby&amp;#39;s unique MRuby-based runtime and procedural architecture keep things both lightweight and powerful, letting you focus on gameplay rather than boilerplate. While our example is intentionally simple, the same principles scale to more complex projects.&lt;/p&gt;
&lt;p&gt;In part two, we&amp;#39;ll start layering on mechanics, collisions, and scoring to transform this prototype into a real game.&lt;/p&gt;
&lt;p&gt;See you next time!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Render a Component Preview In Showcase for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/10/15/render-a-component-preview-in-showcase-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2025/10/15/render-a-component-preview-in-showcase-for-ruby-on-rails.html</id>
    <published>2025-10-15T00:00:00+00:00</published>
    <updated>2025-10-15T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore how to render a preview of a component in Showcase for Rails.</summary>
    <content type="html">&lt;p&gt;In part one of this series, we walked through how to use Showcase in a Rails app.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s now time to read some Ruby code written by experienced Rails developers. To do this without getting lost, we&amp;#39;ll choose one feature of the &lt;code&gt;showcase&lt;/code&gt; engine and analyze how it works: rendering a preview of a component.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;About Showcase&amp;#39;s Button Component Preview&lt;/h2&gt;
&lt;p&gt;If we visit &lt;code&gt;http://localhost:3000/docs/showcase/previews/components/button&lt;/code&gt; in the browser, we should see the complete documentation for a button component:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-10/showcase-button-doc.png&quot; alt=&quot;Showcase button documentation&quot;/&gt;&lt;/p&gt;
&lt;p&gt;According to the &lt;code&gt;showcase&lt;/code&gt; documentation, this view is generated by the following code in the &lt;code&gt;test/dummy&lt;/code&gt; application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# test/dummy/app/views/showcase/previews/components/_button.html.erb %&amp;gt;

&amp;lt;% showcase.badge :partial, :component %&amp;gt;
&amp;lt;% showcase.description &amp;quot;Button is our standard element for what to click on&amp;quot; %&amp;gt;

&amp;lt;% showcase.sample &amp;quot;Basic&amp;quot; do %&amp;gt;
  &amp;lt;%= render &amp;quot;components/button&amp;quot;, content: &amp;quot;Button content&amp;quot;, mode: :small %&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;% showcase.sample &amp;quot;Large&amp;quot;, description: &amp;quot;This is our larger button&amp;quot; do %&amp;gt;
  &amp;lt;%= render &amp;quot;components/button&amp;quot;, content: &amp;quot;Button content&amp;quot;, mode: :large %&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;% showcase.options do |o| %&amp;gt;
  &amp;lt;% o.required :content, &amp;quot;The content to output as the button text&amp;quot; %&amp;gt;
  &amp;lt;% o.optional :mode, &amp;quot;We support three modes&amp;quot;, default: :small, options: %i[ small medium large ] %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But how does it work? That&amp;#39;s what we are going to find out!&lt;/p&gt;
&lt;h2&gt;Rendering the Title, Badges, and Description&lt;/h2&gt;
&lt;p&gt;First, we&amp;#39;ll modify our preview file to only render the title, badges, and description for now, making things easier to follow. Let&amp;#39;s remove everything else from the preview file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# test/dummy/app/views/showcase/previews/components/_button.html.erb %&amp;gt;

&amp;lt;% showcase.badge :partial, :component %&amp;gt;
&amp;lt;% showcase.description &amp;quot;Button is our standard element for what to click on&amp;quot; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we refresh the page, we should now see a much simpler page:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-10/showcase-simplified-button.png&quot; alt=&quot;Showcase simplified button preview&quot;/&gt;&lt;/p&gt;
&lt;p&gt;To understand how such a view is generated, we can examine our Rails server logs, just as we would in a regular Rails application. If we refresh the button preview page, we should see the following logs in our terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;Started GET &amp;quot;/docs/showcase/previews/components/button&amp;quot;
Processing by Showcase::PreviewsController#show as HTML
Parameters: {&amp;quot;id&amp;quot; =&amp;gt; &amp;quot;components/button&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, the request is routed to the &lt;code&gt;Showcase::PreviewsController#show&lt;/code&gt; action, with the parameter &lt;code&gt;id&lt;/code&gt; set to &lt;code&gt;&amp;quot;components/button&amp;quot;&lt;/code&gt;. If we take a look at the routes, we can see that any route starting with &lt;code&gt;previews/&lt;/code&gt; will be routed to the &lt;code&gt;Showcase::PreviewsController#show&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# config/routes.rb

Showcase::Engine.routes.draw do
  get &amp;quot;previews/*id&amp;quot;, to: &amp;quot;previews#show&amp;quot;, as: :preview
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Showcase::PreviewsController#show&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/controllers/showcase/previews_controller.rb

class Showcase::PreviewsController &amp;lt; Showcase::EngineController
  def show
    @preview = Showcase::Path.new(params[:id]).preview_for view_context
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, the &lt;code&gt;show&lt;/code&gt; action instantiates a new &lt;code&gt;Showcase::Path&lt;/code&gt; object with a &lt;code&gt;&amp;quot;components/button&amp;quot;&lt;/code&gt; extracted from the &lt;code&gt;params&lt;/code&gt; as an argument.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;It then calls the &lt;code&gt;preview_for&lt;/code&gt; method on this instance, passing the &lt;code&gt;view_context&lt;/code&gt; as an argument. The result of this method call is assigned to the &lt;code&gt;@preview&lt;/code&gt; instance variable, which will be used later when rendering the view.&lt;/p&gt;
&lt;h2&gt;About &lt;code&gt;view_context&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If you are not familiar with &lt;a href=&quot;https://edgeapi.rubyonrails.org/classes/ActionView/Rendering.html#method-i-view_context&quot;&gt;&lt;code&gt;view_context&lt;/code&gt;&lt;/a&gt;, it is a Rails method that provides access to view helpers and other view-related methods. We will use it later to render HTML from models by calling &lt;code&gt;view_context.render&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s break down this code. The &lt;code&gt;Showcase::Path&lt;/code&gt; initializer looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/path.rb

class Showcase::Path
  attr_reader :id, :segments, :basename

  def initialize(path)
    @id = path.split(&amp;quot;.&amp;quot;).first.delete_prefix(&amp;quot;_&amp;quot;).sub(/\/_/, &amp;quot;/&amp;quot;)
    @basename = File.basename(@id)
    @segments = File.dirname(@id).split(&amp;quot;/&amp;quot;)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that &lt;code&gt;Showcase::Path.new(&amp;quot;components/button&amp;quot;)&lt;/code&gt; will create a new &lt;code&gt;Showcase::Path&lt;/code&gt; object with the following attributes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;Showcase::Path.new(&amp;quot;components/button&amp;quot;)
# =&amp;gt; @basename = &amp;quot;button&amp;quot;
# =&amp;gt; @id       = &amp;quot;components/button&amp;quot;
# =&amp;gt; @segments = [&amp;quot;components&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The controllers then call the &lt;code&gt;Showcase::Path#preview_for&lt;/code&gt; method on this instance, with the &lt;code&gt;view_context&lt;/code&gt; as an argument:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/path.rb

class Showcase::Path
  def preview_for(view_context)
    Showcase::Preview.new(view_context, id: id, title: basename.titleize).tap(&amp;amp;:render_associated_partial)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method first instantiates a new &lt;code&gt;Showcase::Preview&lt;/code&gt; object with the &lt;code&gt;view_context&lt;/code&gt;, the &lt;code&gt;id&lt;/code&gt;, and the &lt;code&gt;basename&lt;/code&gt; as arguments. In our case, the &lt;code&gt;id&lt;/code&gt; is &lt;code&gt;&amp;quot;components/button&amp;quot;&lt;/code&gt; and the &lt;code&gt;basename&lt;/code&gt; is &lt;code&gt;&amp;quot;button&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;About the &lt;code&gt;Showcase::Preview&lt;/code&gt; Initializer&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s have a look at the &lt;code&gt;Showcase::Preview&lt;/code&gt; initializer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/preview.rb

class Showcase::Preview
  attr_reader :id, :badges, :samples

  def initialize(view_context, id:, title: nil)
    @view_context, @id = view_context, id
    @badges, @samples = [], []
    title title
  end

  def title(content = nil)
    @title = content if content
    @title
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, the initializer stores those arguments in instance variables. Here is the current state of our &lt;code&gt;Showcase::Preview&lt;/code&gt; instance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;Showcase::Preview.new(view_context, id: &amp;quot;components/button&amp;quot;, title: &amp;quot;Button&amp;quot;)
# =&amp;gt; @title        = &amp;quot;Button&amp;quot;
# =&amp;gt; @id           = &amp;quot;components/button&amp;quot;
# =&amp;gt; @samples      = []
# =&amp;gt; @badges       = []
# =&amp;gt; @view_context = The view context helper
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Showcase::Preview#render_associated_partial&lt;/code&gt; method is then called on the &lt;code&gt;Showcase::Preview&lt;/code&gt; instance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/preview.rb

class Showcase::Preview
  def render_associated_partial
    @view_context.render &amp;quot;showcase/previews/#{id}&amp;quot;, showcase: self
    nil
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method calls the &lt;code&gt;render&lt;/code&gt; method on the &lt;code&gt;@view_context&lt;/code&gt;. The &lt;code&gt;render&lt;/code&gt; method is the one we use every day in our Rails views to render a template or a partial and return some HTML.&lt;/p&gt;
&lt;p&gt;In our case, the partial that we want to render is the &lt;code&gt;showcase/previews/components/button&lt;/code&gt; partial. The &lt;code&gt;showcase&lt;/code&gt; local is passed to the partial, which allows us to access our &lt;code&gt;Showcase::Preview&lt;/code&gt; instance in the partial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# test/dummy/app/views/showcase/previews/components/_button.html.erb %&amp;gt;

&amp;lt;% showcase.badge :partial, :component %&amp;gt;
&amp;lt;% showcase.description &amp;quot;Button is our standard element for what to click on&amp;quot; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the partial, we call two methods on the &lt;code&gt;showcase&lt;/code&gt; local variable. The first method is &lt;code&gt;badge&lt;/code&gt;, which adds badges to the preview:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/preview.rb

class Showcase::Preview
  def badge(*badges)
    @badges.concat badges
  end
end

# =&amp;gt; @badges = [:partial, :component]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second method is &lt;code&gt;description&lt;/code&gt;, which sets the preview description:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/showcase/preview.rb

class Showcase::Preview
  def description(content = nil, &amp;amp;block)
    @description = content || @view_context.capture(&amp;amp;block) if content || block_given?
    @description
  end
end

# =&amp;gt; @description = &amp;quot;Button is our standard element for what to click on&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of this code exists just to populate the &lt;code&gt;@badges&lt;/code&gt;, &lt;code&gt;@description&lt;/code&gt;, and &lt;code&gt;@title&lt;/code&gt; instance variables of our &lt;code&gt;Showcase::Preview&lt;/code&gt; instance.&lt;/p&gt;
&lt;p&gt;This instance is returned by the &lt;code&gt;Showcase::Path#preview_for&lt;/code&gt; method and assigned to the &lt;code&gt;@preview&lt;/code&gt; instance variable in the &lt;code&gt;Showcase::PreviewsController#show&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/controllers/showcase/previews_controller.rb

class Showcase::PreviewsController &amp;lt; Showcase::EngineController
  def show
    @preview = Showcase::Path.new(params[:id]).preview_for view_context
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, just like any controller, the &lt;code&gt;Showcase::PreviewsController&lt;/code&gt; will render the corresponding view. By convention, the view should be located at &lt;code&gt;app/views/showcase/previews/show.html.erb&lt;/code&gt;. To my surprise, it is located at &lt;code&gt;app/views/showcase/engine/show.html.erb&lt;/code&gt;, which isn&amp;#39;t very conventional. However, it works because the &lt;code&gt;Showcase::PreviewsController&lt;/code&gt; inherits from &lt;code&gt;Showcase::EngineController&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If we have a look at the view, it contains the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/showcase/engine/show.html.erb %&amp;gt;

&amp;lt;%= render &amp;quot;showcase/engine/preview&amp;quot;, preview: @preview %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This view renders a partial called &lt;code&gt;showcase/engine/preview&lt;/code&gt; and passes our &lt;code&gt;@preview&lt;/code&gt; instance variable.&lt;/p&gt;
&lt;p&gt;If we have a look at this view, we can see that it will render the &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;badges&lt;/code&gt;, and &lt;code&gt;description&lt;/code&gt; of the preview in between the Tailwind CSS classes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/showcase/engine/_preview.html.erb %&amp;gt;

&amp;lt;% if preview.title %&amp;gt;
  &amp;lt;div class=&amp;quot;...&amp;quot;&amp;gt;
    &amp;lt;h2 class=&amp;quot;...&amp;quot;&amp;gt;&amp;lt;%= preview.title %&amp;gt;&amp;lt;/h2&amp;gt;

    &amp;lt;% preview.badges.each do |badge| %&amp;gt;
      &amp;lt;span class=&amp;quot;...&amp;quot;&amp;gt;&amp;lt;%= badge.to_s.titleize %&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;% if preview.description %&amp;gt;
  &amp;lt;div class=&amp;quot;...&amp;quot;&amp;gt;&amp;lt;%= preview.description %&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we take a step back and reflect on the code architecture, it makes a lot of sense. The &lt;code&gt;Showcase::Preview&lt;/code&gt; model is responsible for storing the preview data, such as the title, badges, and description. The trick here is that this data is populated while rendering a preview template:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# test/dummy/app/views/showcase/previews/components/_button.html.erb %&amp;gt;

&amp;lt;% showcase.badge :partial, :component %&amp;gt;
&amp;lt;% showcase.description &amp;quot;Button is our standard element for what to click on&amp;quot; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As users of the &lt;code&gt;showcase&lt;/code&gt; engine, it feels like we are writing a view, but in reality, we are populating a model that will be used to render the final view. And look at how clean the interface is! We experience a great developer experience when using this gem!&lt;/p&gt;
&lt;h2&gt;Next Up: Rendering Samples using Showcase for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ve seen how the title, badges, and description of a preview are rendered in Showcase.&lt;/p&gt;
&lt;p&gt;Now that we have a good understanding of how &lt;code&gt;showcase&lt;/code&gt; works, we can get our hands dirty with something a bit more meaty in the third and final part of our series: rendering samples!&lt;/p&gt;
&lt;p&gt;See you next time!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Read Code from the Showcase Ruby on Rails Engine</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/10/01/how-to-read-code-from-the-showcase-ruby-on-rails-engine.html"/>
    <id>https://blog.appsignal.com/2025/10/01/how-to-read-code-from-the-showcase-ruby-on-rails-engine.html</id>
    <published>2025-10-01T00:00:00+00:00</published>
    <updated>2025-10-01T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We are going to set up Showcase for a Rails application in the first part of this three-part series on Showcase.</summary>
    <content type="html">&lt;p&gt;Reading a lot of code from very senior engineers is probably one of the best ways to level up as a Ruby on Rails developer. By doing so, we can learn new tips and techniques that we can reuse in our jobs. Thanks to open source, we can read code written by the best developers from all over the world, and for free!&lt;/p&gt;
&lt;p&gt;However, reading code from a Ruby gem or a Rails engine for the first time without being guided can be daunting. There are so many files; how do we even know where to start?&lt;/p&gt;
&lt;p&gt;In this three-part series, we are going to read the source code from the Showcase Rails engine.&lt;/p&gt;
&lt;p&gt;We will learn about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The main files in a Rails engine&lt;/li&gt;
&lt;li&gt;How to read source code without getting lost&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this first part, we&amp;#39;ll set up Showcase in a Rails application and run it locally.&lt;/p&gt;
&lt;p&gt;Let’s get started!&lt;/p&gt;
&lt;h2&gt;What is Showcase for Ruby on Rails?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bullet-train-co/showcase&quot;&gt;Showcase&lt;/a&gt; (written mainly by &lt;a href=&quot;https://github.com/kaspth&quot;&gt;Kasper Timm Hansen&lt;/a&gt;, Rails consultant and former Rails Core team member) is a Rails engine that allows us to document a design system. Here is what it looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-10/showcase_button_page.png&quot; alt=&quot;Showcase button page&quot;/&gt;&lt;/p&gt;
&lt;p&gt;For a good understanding of how Showcase works before reading its code, we should use it in a project. Let&amp;#39;s create a new Rails application and &lt;code&gt;cd&lt;/code&gt; into it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails new showcase-test
cd showcase-test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://github.com/bullet-train-co/showcase?tab=readme-ov-file#installation&quot;&gt;Showcase README&lt;/a&gt;, we need to add &lt;code&gt;showcase-rails&lt;/code&gt; and &lt;code&gt;rouge&lt;/code&gt; to our &lt;code&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# Gemfile

group :development, :test do
  gem &amp;quot;showcase-rails&amp;quot;
  gem &amp;quot;rouge&amp;quot;, require: false
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are not familiar with the &lt;code&gt;rouge&lt;/code&gt; gem, it is a syntax highlighter used to highlight code in the &amp;quot;View source&amp;quot; accordion, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-10/showcase_button_component.png&quot; alt=&quot;Showcase button component&quot;/&gt;&lt;/p&gt;
&lt;p&gt;As we add new gems to our &lt;code&gt;Gemfile&lt;/code&gt;, let&amp;#39;s not forget to run &lt;code&gt;bundle install&lt;/code&gt;. We then have to &lt;code&gt;mount&lt;/code&gt; the engine in our routes to make the engine&amp;#39;s routes available to our main Rails application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# config/routes.rb

Rails.application.routes.draw do
  mount Showcase::Engine, at: &amp;quot;/docs/showcase&amp;quot; if defined?(Showcase::Engine)
  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Build a Component&lt;/h3&gt;
&lt;p&gt;Let’s now create our first component. Every application needs buttons, right? Let’s create one with three different sizes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/components/_button.html.erb %&amp;gt;

&amp;lt;% classes = { small: &amp;quot;btn--sm&amp;quot;, medium: &amp;quot;btn--md&amp;quot;, large: &amp;quot;btn--lg&amp;quot; }.fetch(mode) %&amp;gt;
&amp;lt;%= tag.button content, class: [ &amp;quot;btn&amp;quot;, classes ] %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To style our button, we will need CSS, of course. As this is just a demo application, we&amp;#39;ll simply write our CSS in the &lt;code&gt;application.css&lt;/code&gt; file generated for us:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;/* app/assets/stylesheets/application.css */

.btn {
  font-size: var(--btn-font-size);
  padding: 0.5em 1em;
  border-radius: 0.5em;
  color: white;
  background-color: black;
}

.btn--sm {
  --btn-font-size: 0.75rem;
}

.btn--md {
  --btn-font-size: 1rem;
}

.btn--lg {
  --btn-font-size: 1.25rem;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Finally, let’s create the preview file to document our button component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/showcase/previews/components/_button.html.erb %&amp;gt;

&amp;lt;% showcase.badge :partial, :component %&amp;gt;
&amp;lt;% showcase.description &amp;quot;Button is our standard element for what to click on&amp;quot; %&amp;gt;

&amp;lt;% showcase.sample &amp;quot;Small&amp;quot; do %&amp;gt;
  &amp;lt;%= render &amp;quot;components/button&amp;quot;, content: &amp;quot;Button content&amp;quot;, mode: :small %&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;% showcase.sample &amp;quot;Large&amp;quot;, description: &amp;quot;This is our larger button&amp;quot; do %&amp;gt;
  &amp;lt;%= render &amp;quot;components/button&amp;quot;, content: &amp;quot;Button content&amp;quot;, mode: :large %&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;% showcase.options do |o| %&amp;gt;
  &amp;lt;% o.required :content, &amp;quot;The content to output as the button text&amp;quot; %&amp;gt;
  &amp;lt;% o.optional :mode, &amp;quot;We support three modes&amp;quot;, options: %i[ small medium large ] %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now navigate to &lt;code&gt;http://localhost:3000/docs/showcase/previews/components/button&lt;/code&gt; to see our first preview:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-10/showcase_demo.png&quot; alt=&quot;Showcase demo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Gems and engines allow us to use other people&amp;#39;s code in our Rails applications. If we use those gems and have absolutely no idea how they work, we have an opportunity to learn something new.&lt;/p&gt;
&lt;h2&gt;Setting Up Our Ruby on Rails Development Environment&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s learn about the magic behind gems by reading some source code!&lt;/p&gt;
&lt;h3&gt;Rails Engines File Structure&lt;/h3&gt;
&lt;p&gt;The first step to read or contribute to a Rails engine is to clone the repository locally:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone git@github.com:bullet-train-co/showcase.git
cd showcase
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s open the repository with our text editor and have a look at the different files:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;.
├── Gemfile
├── showcase.gemspec
├── app
│   ├── assets
│   ├── controllers
│   ├── helpers
│   ├── jobs
│   ├── mailers
│   ├── models
│   └── views
├── config
│   └── routes.rb
├── lib
│   ├── showcase
│   ├── showcase-rails.rb
│   └── showcase.rb
└── test
    ├── controllers
    ├── dummy
    ├── fixtures
    ├── helpers
    ├── integration
    ├── mailers
    ├── models
    └── views
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing we should realize is that the file structure is very similar to a standard Rails application with models, views, controllers, and routes. In fact, we can view a Rails engine as a standalone Rails application that will get merged into our main Rails application!&lt;/p&gt;
&lt;p&gt;Our engine also has a &lt;code&gt;/test&lt;/code&gt; folder used to test the engine, with a special &lt;code&gt;/dummy&lt;/code&gt; folder that is particularly useful. If we have a look at the file structure of the &lt;code&gt;/test/dummy&lt;/code&gt; folder, we can see the following tree:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;.
├── Rakefile
├── app
│   ├── assets
│   ├── channels
│   ├── controllers
│   ├── helpers
│   ├── jobs
│   ├── mailers
│   ├── models
│   └── views
├── bin
│   ├── rails
│   ├── rake
│   └── setup
├── config
│   ├── application.rb
│   ├── boot.rb
│   ├── cable.yml
│   ├── database.yml
│   ├── environment.rb
│   ├── environments
│   ├── initializers
│   ├── locales
│   ├── puma.rb
│   ├── routes.rb
│   └── storage.yml
├── config.ru
├── lib
│   └── assets
├── log
└── public
    ├── 404.html
    ├── 422.html
    ├── 500.html
    └── favicon.ico
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wait a minute, isn&amp;#39;t this the exact file structure of a standard Rails application? Yes, it is! The &lt;code&gt;test/dummy&lt;/code&gt; folder contains a real Rails application that we can launch in the browser for development. It is also useful to write tests for our engine in the context of a real Rails application.&lt;/p&gt;
&lt;h3&gt;Running the Rails Engine Locally&lt;/h3&gt;
&lt;p&gt;Now that we have a better understanding of the file structure, it&amp;#39;s useful to be able to run this &lt;code&gt;test/dummy&lt;/code&gt; Rails application on &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As with any other Rails application, the first step is to install dependencies. Before we do so, we have to talk about the &lt;code&gt;showcase.gemspec&lt;/code&gt; and the &lt;code&gt;Gemfile&lt;/code&gt;. If we open the &lt;code&gt;showcase.gemspec&lt;/code&gt;, it currently contains the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# showcase.gemspec

require_relative &amp;quot;lib/showcase/version&amp;quot;

Gem::Specification.new do |spec|
  spec.name        = &amp;quot;showcase-rails&amp;quot;
  spec.version     = Showcase::VERSION
  spec.authors     = [&amp;quot;Daniel Pence&amp;quot;, &amp;quot;Kasper Timm Hansen&amp;quot;]
  spec.email       = [&amp;quot;hey@kaspth.com&amp;quot;]
  spec.homepage    = &amp;quot;https://github.com/kaspth/showcase&amp;quot;
  spec.summary     = &amp;quot;Showcase helps you show off and document your partials, components, view helpers and Stimulus controllers.&amp;quot;
  spec.license     = &amp;quot;MIT&amp;quot;

  spec.metadata[&amp;quot;homepage_uri&amp;quot;]    = spec.homepage
  spec.metadata[&amp;quot;source_code_uri&amp;quot;] = spec.homepage
  spec.metadata[&amp;quot;changelog_uri&amp;quot;]   = &amp;quot;#{spec.homepage}/blob/main/CHANGELOG.md&amp;quot;

  spec.files = Dir.chdir(File.expand_path(__dir__)) do
    Dir[&amp;quot;{app,config,db,lib}/**/*&amp;quot;, &amp;quot;MIT-LICENSE&amp;quot;, &amp;quot;Rakefile&amp;quot;, &amp;quot;README.md&amp;quot;]
  end

  spec.add_dependency &amp;quot;rails&amp;quot;, &amp;quot;&amp;gt;= 6.1.0&amp;quot;
  spec.add_development_dependency &amp;quot;tailwindcss-rails&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of it is just information about the authors, the license, and useful links that you&amp;#39;ll find on the &lt;a href=&quot;https://rubygems.org/gems/showcase-rails/versions/0.4.6?locale=en&quot;&gt;RubyGems website&lt;/a&gt;. However, the most interesting part is the last two lines:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# showcase.gemspec

spec.add_dependency &amp;quot;rails&amp;quot;, &amp;quot;&amp;gt;= 6.1.0&amp;quot;
spec.add_development_dependency &amp;quot;tailwindcss-rails&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first line indicates that &lt;code&gt;showcase&lt;/code&gt; depends on Rails version 6.1 or above.&lt;/p&gt;
&lt;p&gt;The second line indicates a &lt;em&gt;development dependency&lt;/em&gt; on &lt;code&gt;tailwindcss-rails&lt;/code&gt;. This means that &lt;code&gt;showcase&lt;/code&gt; uses &lt;code&gt;tailwindcss-rails&lt;/code&gt; for styling purposes in development, but you do not need to have &lt;code&gt;tailwindcss-rails&lt;/code&gt; installed in your main Rails application to use &lt;code&gt;showcase&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s now have a look at the &lt;code&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# Gemfile

source &amp;quot;https://rubygems.org&amp;quot;
git_source(:github) { |repo| &amp;quot;https://github.com/#{repo}.git&amp;quot; }

# Specify your gem&amp;#39;s dependencies in showcase.gemspec.
gemspec

gem &amp;quot;sqlite3&amp;quot;

rails_version = ENV.fetch(&amp;quot;RAILS_VERSION&amp;quot;, &amp;quot;7.0&amp;quot;)

rails_constraint = if rails_version == &amp;quot;main&amp;quot;
  {github: &amp;quot;rails/rails&amp;quot;}
else
  &amp;quot;~&amp;gt; #{rails_version}.0&amp;quot;
end

gem &amp;quot;rails&amp;quot;, rails_constraint
gem &amp;quot;sprockets-rails&amp;quot;

# Start debugger with binding.b [https://github.com/ruby/debug]
# gem &amp;quot;debug&amp;quot;, &amp;quot;&amp;gt;= 1.0.0&amp;quot;

gem &amp;quot;puma&amp;quot;
gem &amp;quot;rouge&amp;quot;, require: false

group :test do
  # Code omitted for brevity
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Gemfile&lt;/code&gt; should look familiar: It lists additional &lt;em&gt;development dependencies&lt;/em&gt; used to run the &lt;code&gt;/test/dummy&lt;/code&gt; application.&lt;/p&gt;
&lt;p&gt;Those dependencies are from the &lt;code&gt;gemspec&lt;/code&gt;, plus the additional ones listed in our &lt;code&gt;Gemfile&lt;/code&gt;. This &lt;code&gt;Gemfile&lt;/code&gt; also enables us to choose the Rails version we want to use in development by passing the &lt;code&gt;RAILS_VERSION&lt;/code&gt; environment variable, and defaults to version &lt;code&gt;7.0&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: While writing this article, I realized there is a &lt;a href=&quot;https://github.com/bullet-train-co/showcase/pull/85&quot;&gt;small bug&lt;/a&gt; when running Showcase with Rails &lt;code&gt;7.0.X&lt;/code&gt;. It is not a very interesting bug to talk about, so we&amp;#39;ll just ignore it and use Rails &lt;code&gt;8.X&lt;/code&gt; version instead.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;As specified in the &lt;code&gt;Gemfile&lt;/code&gt;, we can use the &lt;code&gt;RAILS_VERSION&lt;/code&gt; environment variable to specify the Rails version we want to use. To do this, we simply have to prefix all of our commands with &lt;code&gt;RAILS_VERSION=8&lt;/code&gt;:&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;RAILS_VERSION=8 bundle install
RAILS_VERSION=8 bin/rails server
RAILS_VERSION=8 bin/rails console
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s try to install the dependencies now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;RAILS_VERSION=8 bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all the dependencies installed, we should now be able to run the Rails server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;RAILS_VERSION=8 bin/rails server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we now visit &lt;code&gt;http://localhost:3000&lt;/code&gt;, we should see the showcase landing page from the &lt;code&gt;test/dummy&lt;/code&gt; application:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-10/showcase_landing_page.png&quot; alt=&quot;Showcase landing page&quot;/&gt;&lt;/p&gt;
&lt;p&gt;We should also be able to see our Rails server logs in the terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;Started GET &amp;quot;/docs/showcase&amp;quot; for ::1 at 2025-07-24 11:57:42 +0200
Processing by Showcase::EngineController#index as HTML
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is going to be very helpful when analyzing how Showcase works, as we&amp;#39;ll simply be able to follow the logs!&lt;/p&gt;
&lt;h2&gt;Next Up: The Button Component Preview&lt;/h2&gt;
&lt;p&gt;In this post, we learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to use &lt;code&gt;showcase&lt;/code&gt; in a real Rails application&lt;/li&gt;
&lt;li&gt;What the different files in a Rails engine are used for&lt;/li&gt;
&lt;li&gt;How to run the &lt;code&gt;test/dummy&lt;/code&gt; Rails application locally&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the next part of this series, we will read some Ruby code written by experienced Rails developers. In order to do this without getting lost, we are going to choose one feature of the &lt;code&gt;showcase&lt;/code&gt; engine — the button component preview — and analyze how it works.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Extend ActiveStorage for Ruby on Rails with Custom Previewers</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/08/13/extend-activestorage-for-ruby-on-rails-with-custom-previewers.html"/>
    <id>https://blog.appsignal.com/2025/08/13/extend-activestorage-for-ruby-on-rails-with-custom-previewers.html</id>
    <published>2025-08-13T00:00:00+00:00</published>
    <updated>2025-08-13T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll dive into how ActiveStorage handles uploaded data and how to extend this process in the second part of this two-part series.</summary>
    <content type="html">&lt;p&gt;In part one of this series, we looked at how to extend the ActiveStorage ingest process with custom analyzers.&lt;/p&gt;
&lt;p&gt;In this post, we will reverse the procedure and explore how to utilize ActiveStorage previewers to display data.&lt;/p&gt;
&lt;h2&gt;What Are ActiveStorage Previewers for Ruby on Rails?&lt;/h2&gt;
&lt;p&gt;Not every uploaded blob is an image. Nonetheless, some non-image blobs can be converted into an image preview. ActiveStorage itself provides built-in previewers for &lt;a href=&quot;https://github.com/rails/rails/blob/c5bb138e43390a191ddb7aa4e0f46e7af8563dcc/activestorage/lib/active_storage/previewer/video_previewer.rb&quot;&gt;videos&lt;/a&gt; and PDFs (via &lt;a href=&quot;https://github.com/rails/rails/blob/c5bb138e43390a191ddb7aa4e0f46e7af8563dcc/activestorage/lib/active_storage/previewer/mupdf_previewer.rb&quot;&gt;MuPDF&lt;/a&gt; or &lt;a href=&quot;https://github.com/rails/rails/blob/c5bb138e43390a191ddb7aa4e0f46e7af8563dcc/activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb&quot;&gt;Poppler&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Displaying a preview in your ERB template works exactly the same as creating variants of an image:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= image_tag song.recording.preview(resize_to_fill: [640, 160]) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we lazily create and display a song preview, taken from the previous article&amp;#39;s example use cases. Observe that &lt;code&gt;preview&lt;/code&gt; takes the same arguments as &lt;code&gt;variant&lt;/code&gt;, which you would use to transform an image.&lt;/p&gt;
&lt;h2&gt;The Skeleton of an ActiveStorage Previewer&lt;/h2&gt;
&lt;p&gt;To understand how a previewer is constructed, let&amp;#39;s look at an example. Before we dive deep into the code, though, let&amp;#39;s examine which previewers are available in the Rails console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;(dev)&amp;gt; ActiveStorage.previewers
=&amp;gt;
[ActiveStorage::Previewer::PopplerPDFPreviewer,
  ActiveStorage::Previewer::MuPDFPreviewer,
  ActiveStorage::Previewer::VideoPreviewer]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are the two flavors of PDF previewers (depending on the present backend), as well as the video previewer mentioned in the introduction. We are going to study the &lt;code&gt;VideoPreviewer&lt;/code&gt; to get a better grasp of what is required to come up with such a solution.&lt;/p&gt;
&lt;p&gt;As of today, &lt;a href=&quot;https://github.com/rails/rails/blob/36601bbb02bc3570f5609db2127c77afca575d6c/activestorage/lib/active_storage/previewer/video_previewer.rb&quot;&gt;the implementation looks like this&lt;/a&gt; (I&amp;#39;ve omitted some helper code for clarity):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module ActiveStorage
  class Previewer::VideoPreviewer &amp;lt; Previewer
    class &amp;lt;&amp;lt; self
      def accept?(blob)
        blob.video? &amp;amp;&amp;amp; ffmpeg_exists?
      end

      def ffmpeg_exists?
        # somehow check if ffmpeg is present
      end
    end

    def preview(**options)
      download_blob_to_tempfile do |input|
        draw_relevant_frame_from input do |output|
          yield io: output, filename: &amp;quot;#{blob.filename.base}.jpg&amp;quot;, content_type: &amp;quot;image/jpeg&amp;quot;, **options
        end
      end
    end

    private
      def draw_relevant_frame_from(file, &amp;amp;block)
        # invoke a ffmpeg command to extract the first image from the video
      end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you get a feeling of déjà vu, that&amp;#39;s understandable, because we encountered a very similar pattern in our previous post. Let&amp;#39;s inspect the two important parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An &lt;code&gt;accept?&lt;/code&gt; method checks whether the previewer is applicable. In this case, it is, if the MIME type is correct and the tooling to transform video (aka &lt;code&gt;ffmpeg&lt;/code&gt;) exists on the machine.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;preview&lt;/code&gt; method does all the heavy lifting. Specifically, it&amp;#39;s used to draw a preview image and store this as a new blob. In the above case, it just uses the first still video image as the preview.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;download_blob_to_tempfile&lt;/code&gt; is a helper method &lt;a href=&quot;https://github.com/rails/rails/blob/36601bbb02bc3570f5609db2127c77afca575d6c/activestorage/lib/active_storage/previewer.rb#L31-L32&quot;&gt;defined in the &lt;code&gt;ActiveStorage::Previewer&lt;/code&gt; base class&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;A Use Case for a Custom Previewer&lt;/h2&gt;
&lt;p&gt;Building on our example from the previous article, we will build a custom previewer for audio waveforms. Let&amp;#39;s get started!&lt;/p&gt;
&lt;p&gt;Audio data is usually displayed using an abstract waveform or envelope representation (where each bar stands for the loudness at a certain point in time). There are other forms, like spectrograms, but for this demonstration, let&amp;#39;s stick with a simple example.&lt;/p&gt;
&lt;p&gt;First of all, we are going to produce previews in the PNG format. We&amp;#39;ll add &lt;a href=&quot;https://github.com/wvanbergen/chunky_png&quot;&gt;ChunkyPNG&lt;/a&gt;, a library that helps with PNG manipulation, to our application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add chunky_png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&amp;#39;s start with a minimal &lt;code&gt;WaveformPreviewer&lt;/code&gt; scaffold:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# lib/active_storage/waveform_previewer.rb

require &amp;quot;chunky_png&amp;quot;

module ActiveStorage
  class WaveformPreviewer &amp;lt; ActiveStorage::Previewer
    class &amp;lt;&amp;lt; self
      def accept?(blob)
        blob.audio? &amp;amp;&amp;amp; blob.metadata[:waveform].present?
      end
    end

    def preview(**options)
      waveform = blob.metadata.fetch(:waveform, [])

      # somehow transform the stored waveform into a PNG image
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;accept?&lt;/code&gt; class method checks if we&amp;#39;re dealing with an audio blob. Additionally, we check whether waveform data has been stored in &lt;code&gt;metadata&lt;/code&gt;, as we did in the previous article. In the &lt;code&gt;preview&lt;/code&gt; method, we will have to implement a way of painting an image representing that waveform and provide it as an attachable IO stream. Let&amp;#39;s go and do this now.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# lib/active_storage/waveform_previewer.rb

require &amp;quot;chunky_png&amp;quot;

module ActiveStorage
  class WaveformPreviewer &amp;lt; ActiveStorage::Previewer
    # class methods omitted

    def preview(**options)
      waveform = blob.metadata.fetch(:waveform, [])

      width = 640
      height = 240
      center = height / 2

      png = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::WHITE)

      frame_width = waveform.size / width

      waveform.each_with_index do |v, idx|
        next unless idx % frame_width == 0

        x = idx / frame_width

        y_val = (v * center).to_i
        y1 = center - y_val
        y2 = center + y_val

        png.rect(x, y1, x, y2, 0x4b0082ff, 0x4b0082ff)
      end

      # Write to in-memory buffer
      io = StringIO.new
      png.write(io)
      io.rewind

      yield io: io, filename: &amp;quot;#{blob.filename.base}.png&amp;quot;, content_type: &amp;quot;image/png&amp;quot;, **options
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To simplify things, we assume a fixed width and height (640 by 240 pixels). We store a new instance of &lt;code&gt;ChunkyPNG::Image&lt;/code&gt; with a white background in the &lt;code&gt;png&lt;/code&gt; variable. Now, we have to iterate over each of the datapoints in the waveform and decide whether to draw a line for it or not. Presumably, our stored waveform has more than 640 datapoints, so we calculate 640 frames containing several datapoints. The &lt;code&gt;frame_width&lt;/code&gt; is determined by dividing the &lt;code&gt;waveform&lt;/code&gt;&amp;#39;s size by 640.&lt;/p&gt;
&lt;p&gt;When drawing the waveform, we can skip every index that is not an integer multiple of the frame width. For the ones that match, we draw a vertically centered rectangle (a line, really, since it&amp;#39;s only one pixel wide) whose height is proportional to the value &lt;code&gt;v&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we have to wrap our composed PNG image in an in-memory buffer so it&amp;#39;s ready for ActiveStorage to be attached. That&amp;#39;s what the &lt;code&gt;yield&lt;/code&gt; call does: it simply creates a new attachment that&amp;#39;s tied to the blob as a preview image.&lt;/p&gt;
&lt;p&gt;The result looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-08/waveform-preview.png&quot; alt=&quot;Waveform preview&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Beautiful, isn&amp;#39;t it? I have one adjustment to propose, though. Drawing a waveform true to size like this looks very technical and might fit an audio editor. But if we consider a simplified version for a social media preview, we can tweak it a bit.&lt;/p&gt;
&lt;p&gt;To make it more visually appealing, we are going to do two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make the bars 5 pixels wide (that&amp;#39;s why we have to multiply our frame size by 5).&lt;/li&gt;
&lt;li&gt;Display only every other frame.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We only need to make a few adaptations to the drawing logic:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# produce bars 5 pixels apart
frame_width = (5 * waveform.size / width).floor

waveform.each_with_index do |v, idx|
  next unless idx % frame_width == 0

  frame_even = (idx / frame_width).even?

  x = idx * width / waveform.size

  y_val = (v * center).to_i
  y1 = center - y_val
  y2 = center + y_val

  png.rect(x, y1, x+6, y2, 0x4b0082ff, 0x4b0082ff) unless frame_even # we skip every other frame for a nice visual effect, and make the bar a bit wider
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition to making the frame width bigger, we have to decide if the frame index is even or odd. We do that by dividing the index by the frame width, and if it&amp;#39;s odd, we simply skip drawing the rectangle. To compensate for the diminished width, we increase the width by &lt;strong&gt;6&lt;/strong&gt; pixels, so there&amp;#39;s a bit of an overlap.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The result is a nice interleaved graph of approximate sound loudness:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-08/waveform-preview-wide.png&quot; alt=&quot;Waveform preview wide&quot;/&gt;&lt;/p&gt;
&lt;p&gt;To actually activate it, we have to prepend it to the list of previewers in our &lt;code&gt;active_storage&lt;/code&gt; initializer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# config/initializers/active_storage.rb

require_relative &amp;quot;../../lib/active_storage/waveform_analyzer.rb&amp;quot;
require_relative &amp;quot;../../lib/active_storage/waveform_previewer.rb&amp;quot;

Rails.application.config.active_storage.analyzers.prepend ActiveStorage::WaveformAnalyzer
Rails.application.config.active_storage.previewers.prepend ActiveStorage::WaveformPreviewer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s now look at a second use case: blurhash image previews.&lt;/p&gt;
&lt;h2&gt;Blurhash Image Previews&lt;/h2&gt;
&lt;p&gt;Full disclosure: when writing this article, I assumed that what follows below would also be attainable using a previewer. And it is, however, it also interferes with the normal variant processing logic for images. This makes it unfeasible for a vanilla Rails application.&lt;/p&gt;
&lt;p&gt;To understand what&amp;#39;s going on, let&amp;#39;s look at the image transformation logic employed by ActiveStorage:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When applying transformations (e.g., &lt;code&gt;blob.variant(...)&lt;/code&gt;), Rails always uses &lt;a href=&quot;https://github.com/rails/rails/blob/0fdb4cfeb9739e570b1669c46327688b11128a62/activestorage/app/models/active_storage/blob/representable.rb#L85-L86&quot;&gt;&lt;code&gt;ActiveStorage::Blob#representation&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;This method &lt;strong&gt;prefers&lt;/strong&gt; to create a preview when possible, so any attempt to create a variation will be rerouted to a previewer that returns &lt;code&gt;true&lt;/code&gt; on &lt;code&gt;self.accept?(blob)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The only accessible parameter in this method is the blob itself. It is not possible to distinguish whether the developer meant to generate a preview or a variant using an option applied to the representation generation logic.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This limits the usability of ActiveStorage previewers for our use case: preparing a blurhash preview as a placeholder for lazily loading an image. In fact, every call to &lt;code&gt;variant&lt;/code&gt; would be &lt;strong&gt;overridden&lt;/strong&gt; by the previewer, so we could not produce scaled down thumbnails, for example.&lt;/p&gt;
&lt;p&gt;That doesn&amp;#39;t mean that we cannot obtain a similar result though: we just have to use different semantics. We will be able to achieve this using a new, as yet unreleased feature which will be part of Rails 8.1 (introduced in &lt;a href=&quot;https://github.com/rails/rails/pull/45100&quot;&gt;this pull request&lt;/a&gt;) — a &lt;strong&gt;custom image transformer&lt;/strong&gt;. Let&amp;#39;s try and put this together!&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll line out our desired API first. We would like to be able to compute a variant from a blurhash:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= image_tag image.variant(blurhash: image.blob.metadata[&amp;quot;blurhash&amp;quot;]) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails will now complain because we have just made up the &lt;code&gt;blurhash: ...&lt;/code&gt; image processing method, but that is expected:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingMethod (One or more of the provided transformation methods is not supported.)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What we have to do, therefore, is subclass an image processing backend and adapt it for blurhash image calculation. In our case, we can reuse the &lt;code&gt;ImageMagick&lt;/code&gt; transformer and insert a conditional that generates our blurhash image if it is present in the &lt;code&gt;transformations&lt;/code&gt; hash:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# lib/active_storage/transformers/image_magick_with_blurhash.rb

require &amp;quot;blurhash&amp;quot;
require &amp;quot;chunky_png&amp;quot;

module ActiveStorage
  module Transformers
    class ImageMagickWithBlurhash &amp;lt; ImageMagick
      private

      def process(file, format:)
        if transformations[:blurhash]
          generate_blurhash_png(transformations[:blurhash])
        else
          super(file, format:)
        end
      end

      def generate_blurhash_png(blurhash)
        raise ArgumentError, &amp;quot;Missing blurhash metadata&amp;quot; unless blurhash

        width = 600
        height = 400

        pixels = decode_blurhash(blurhash, width, height)

        png = ChunkyPNG::Image.new(width, height)
        pixels.each_with_index do |(r, g, b), i|
          x = i % width
          y = i / width
          png[x, y] = ChunkyPNG::Color.rgb(r, g, b)
        end

        tempfile = Tempfile.new([&amp;quot;blurhash&amp;quot;, &amp;quot;.png&amp;quot;], binmode: true)
        png.write(tempfile)
        tempfile.rewind

        tempfile
      end

      # decode_blurhash method omitted
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We decode the blurhash, use ChunkyPNG to create an image again, and fill it with the respective pixels. We then need to store it in a temp file so that the ActiveStorage variant processor can pick it up.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ll save you the dire details of how to produce a PNG from a blurhash, but if you&amp;#39;re interested, you can look at &lt;a href=&quot;https://github.com/julianrubisch/active_storage_analyzers_previews/blob/0dc6440c3f995fd57c8efa446221ab00b104a51a/lib/active_storage/transformers/image_magick_with_blurhash.rb#L39&quot;&gt;this example repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s about it for the image processing part. What&amp;#39;s left to do is to register it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# config/initializers/active_storage.rb

require_relative &amp;quot;../../lib/active_storage/blurhash_analyzer.rb&amp;quot;
require_relative &amp;quot;../../lib/active_storage/transformers/image_magick_with_blurhash.rb&amp;quot;

Rails.application.config.active_storage.analyzers.prepend ActiveStorage::BlurhashAnalyzer

ActiveSupport.on_load(:active_storage_blob) do
  ActiveStorage.variant_transformer = ActiveStorage::Transformers::ImageMagickWithBlurhash
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the default transformer is lazily loaded, this time we have to register it in a load hook, &lt;code&gt;ActiveSupport.on_load(:active_storage_blob)&lt;/code&gt;. Once that is done, we can give it a go — for example, in the &lt;code&gt;_post&lt;/code&gt; partial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/posts/_post.html.erb --&amp;gt;

&amp;lt;div id=&amp;quot;&amp;lt;%= dom_id post %&amp;gt;&amp;quot;&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;strong&amp;gt;Title:&amp;lt;/strong&amp;gt;
    &amp;lt;%= post.title %&amp;gt;
  &amp;lt;/p&amp;gt;

  &amp;lt;p&amp;gt;
    &amp;lt;strong&amp;gt;Body:&amp;lt;/strong&amp;gt;
    &amp;lt;%= post.body %&amp;gt;
  &amp;lt;/p&amp;gt;

  &amp;lt;% post.images.each do |image| %&amp;gt;
    &amp;lt;%= image_tag image.variant(resize_to_fit: [600, 400]), width: 600, height: 400 %&amp;gt;
    &amp;lt;%= image_tag image.variant(blurhash: image.blob.metadata[&amp;quot;blurhash&amp;quot;]) %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we get a scaled-down variant of the original, side by side with the calculated blurhash image:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-08/blurhash-transformer.png&quot; alt=&quot;Blurhash transformer&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Writing the necessary JavaScript to swap out the blurhash preview for the real image is left as homework for the reader.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt; We are in Rails alpha territory here. While it&amp;#39;s pretty certain that this functionality will land in Rails 8.1, there might be breaking changes.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we looked at how to make any content stored in an ActiveStorage blob previewable using a custom class. This comes in handy to graphically represent arbitrary media content like audio waveforms, especially when preparing social media previews like open graph images or X (formerly Twitter) cards.&lt;/p&gt;
&lt;p&gt;We extended ActiveStorage with a custom waveform previewer based on precomputed audio intensity data. Then we created our own variant transformer capable of converting blurhash data into a downloadable PNG file.&lt;/p&gt;
&lt;p&gt;During this deep dive, we learned a lot about ActiveStorage&amp;#39;s internals and the interfaces it provides to craft a customized experience. What other analyzers, previewers, and transformers for your media files can you think of?&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Build Custom ActiveStorage Analyzers for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/07/30/build-custom-activestorage-analyzers-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2025/07/30/build-custom-activestorage-analyzers-for-ruby-on-rails.html</id>
    <published>2025-07-30T00:00:00+00:00</published>
    <updated>2025-07-30T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll dive into how ActiveStorage treats uploaded data, and how to extend this process, in part one of this two-part series.</summary>
    <content type="html">&lt;p&gt;In this series, we will take a close look at the architecture of ActiveStorage for Rails.&lt;/p&gt;
&lt;p&gt;In this first part, we will examine how ActiveStorage treats uploaded data and how to extend this process. The second part will explore how to augment the presentation of uploaded assets.&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s quickly define what ActiveStorage does.&lt;/p&gt;
&lt;h2&gt;What Is ActiveStorage for Ruby on Rails?&lt;/h2&gt;
&lt;p&gt;Without recounting the entire &lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html&quot;&gt;ActiveStorage documentation&lt;/a&gt;, in a nutshell, ActiveStorage is an adapter to various forms of storing (mostly) user-generated files in your Ruby on Rails application in a straightforward way. The available storage backends can be divided into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Local disk storage&lt;/li&gt;
&lt;li&gt;Diverse flavors of cloud storage (most prominently Amazon S3 and compatible object storage providers)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For the sake of completeness, there are also adapters to directly store binary data in your database, like &lt;a href=&quot;https://github.com/lsylvester/active_storage-postgresql&quot;&gt;active_storage-postgresql&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ActiveStorage allows you to transparently attach files to database records for easy access. You have probably already come across this API:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class User &amp;lt; ApplicationRecord
  has_one_attached :avatar
end

class Post &amp;lt; ApplicationRecord
  has_many_attached :images
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the class methods &lt;code&gt;has_one_attached&lt;/code&gt; and &lt;code&gt;has_many_attached&lt;/code&gt; are responsible for wiring up your &lt;code&gt;User&lt;/code&gt; or &lt;code&gt;Post&lt;/code&gt; records with the respective attachments. But how does this magic work?&lt;/p&gt;
&lt;p&gt;Under the hood, ActiveStorage uses two database tables generated when you install it. Here are their entries from the database schema definition:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;create_table &amp;quot;active_storage_attachments&amp;quot;, force: :cascade do |t|
  t.string &amp;quot;name&amp;quot;, null: false
  t.string &amp;quot;record_type&amp;quot;, null: false
  t.bigint &amp;quot;record_id&amp;quot;, null: false
  t.bigint &amp;quot;blob_id&amp;quot;, null: false
  t.datetime &amp;quot;created_at&amp;quot;, null: false
  t.index [&amp;quot;blob_id&amp;quot;], name: &amp;quot;index_active_storage_attachments_on_blob_id&amp;quot;
  t.index [&amp;quot;record_type&amp;quot;, &amp;quot;record_id&amp;quot;, &amp;quot;name&amp;quot;, &amp;quot;blob_id&amp;quot;], name: &amp;quot;index_active_storage_attachments_uniqueness&amp;quot;, unique: true
end

create_table &amp;quot;active_storage_blobs&amp;quot;, force: :cascade do |t|
  t.string &amp;quot;key&amp;quot;, null: false
  t.string &amp;quot;filename&amp;quot;, null: false
  t.string &amp;quot;content_type&amp;quot;
  t.text &amp;quot;metadata&amp;quot;
  t.string &amp;quot;service_name&amp;quot;, null: false
  t.bigint &amp;quot;byte_size&amp;quot;, null: false
  t.string &amp;quot;checksum&amp;quot;
  t.datetime &amp;quot;created_at&amp;quot;, null: false
  t.index [&amp;quot;key&amp;quot;], name: &amp;quot;index_active_storage_blobs_on_key&amp;quot;, unique: true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first table belongs to the join model &lt;code&gt;ActiveStorage::Attachment&lt;/code&gt;. It references a polymorphic &lt;code&gt;record&lt;/code&gt; (that&amp;#39;s why the methods &lt;code&gt;has_(one|many)_attached&lt;/code&gt; can be called from &lt;em&gt;any&lt;/em&gt; ActiveRecord model) as well as a &lt;code&gt;Blob&lt;/code&gt;. As you might correctly assume, this refers to the second table, which is mapped by the &lt;code&gt;ActiveStorage::Blob&lt;/code&gt; model. This acts as a data container for all that is needed to upload or download a file to one of the configured services. Let&amp;#39;s take it apart a bit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;key&lt;/code&gt; attribute refers to how the blob is called at its actual storage location (in most cases, an S3 bucket).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filename&lt;/code&gt; is the original name of the file under which it was uploaded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;content_type&lt;/code&gt; is its analyzed MIME type.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;metadata&lt;/code&gt; is a generic text column that can hold any metadata of the file, in &lt;a href=&quot;https://github.com/rails/rails/blob/0f591639b864de6bf3fd760a6921f5eda17b2905/activestorage/app/models/active_storage/blob.rb#L23-L24&quot;&gt;JSON format&lt;/a&gt;. &lt;em&gt;It&amp;#39;s this column that our custom analyzers will make use of.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service_name&lt;/code&gt; is used to identify the service in &lt;code&gt;config/storage.yml&lt;/code&gt; to which this file was uploaded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;byte_size&lt;/code&gt; and &lt;code&gt;checksum&lt;/code&gt; are exactly what you&amp;#39;d expect these attributes to store.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How Does ActiveStorage Treat Uploaded Data?&lt;/h2&gt;
&lt;p&gt;With these general concepts out of the way, let&amp;#39;s examine the ingest process used by ActiveStorage. In other words, once a file is uploaded, what happens next?&lt;/p&gt;
&lt;p&gt;The answer lies in the &lt;code&gt;Attachment&lt;/code&gt; model&amp;#39;s code. In an &lt;code&gt;after_create_commit&lt;/code&gt; callback, it enqueues a job to &lt;a href=&quot;https://github.com/rails/rails/blob/0f591639b864de6bf3fd760a6921f5eda17b2905/activestorage/app/models/active_storage/attachment.rb#L126-L127&quot;&gt;analyze the blob it pertains to later&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The code that&amp;#39;s used to perform the analysis &lt;a href=&quot;https://github.com/rails/rails/blob/0f591639b864de6bf3fd760a6921f5eda17b2905/activestorage/app/models/active_storage/blob/analyzable.rb#L29-L31&quot;&gt;reads simple enough&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def analyze
  update! metadata: metadata.merge(extract_metadata_via_analyzer)
end

# ...

private
  def extract_metadata_via_analyzer
    analyzer.metadata.merge(analyzed: true)
  end

  def analyzer
    analyzer_class.new(self)
  end

  def analyzer_class
    ActiveStorage.analyzers.detect { |klass| klass.accept?(self) } || ActiveStorage::Analyzer::NullAnalyzer
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We observe that, as indicated before, the &lt;code&gt;analyze&lt;/code&gt; method updates the &lt;code&gt;metadata&lt;/code&gt; column of the blob. This metadata is extracted by an analyzer, but there&amp;#39;s an important detail hidden in how exactly this analyzer is provided. The &lt;code&gt;analyzer_class&lt;/code&gt; is retrieved from a list of analyzers that the &lt;code&gt;ActiveStorage&lt;/code&gt; module itself keeps. Let&amp;#39;s take a brief look at it in the Rails console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(dev)&amp;gt; ActiveStorage.analyzers
=&amp;gt;
[ActiveStorage::Analyzer::ImageAnalyzer::Vips,
  ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick,
  ActiveStorage::Analyzer::VideoAnalyzer,
  ActiveStorage::Analyzer::AudioAnalyzer]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Listed here are all the standard analyzers that ship with ActiveStorage: two for images (because of the two processing backends, Vips and ImageMagick), one for video, and one for audio data. From this list, the &lt;strong&gt;first&lt;/strong&gt; analyzer that responds to &lt;code&gt;accept?(self)&lt;/code&gt; with &lt;code&gt;true&lt;/code&gt; is selected. So, for example, &lt;code&gt;ImageAnalyzer&lt;/code&gt; checks &lt;a href=&quot;https://github.com/rails/rails/blob/0f591639b864de6bf3fd760a6921f5eda17b2905/activestorage/lib/active_storage/analyzer/image_analyzer.rb#L20-L21&quot;&gt;whether a blob holds an image&lt;/a&gt;, and so on. Creating new analyzers, then, only needs a class that extends &lt;a href=&quot;https://github.com/rails/rails/blob/0f591639b864de6bf3fd760a6921f5eda17b2905/activestorage/lib/active_storage/analyzer.rb&quot;&gt;Analyzer&lt;/a&gt; and is &lt;strong&gt;prepended&lt;/strong&gt; to this list. We&amp;#39;ll explore how to do this in the remainder of this article.&lt;/p&gt;
&lt;h2&gt;Two Use Cases for Custom Analyzers&lt;/h2&gt;
&lt;p&gt;When would you reach for a custom analyzer? I contend that most use cases revolve around needs for enhanced &lt;em&gt;presentation&lt;/em&gt; of assets. To back up that claim, I have prepared two prototypical examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;precomputing audio waveform data&lt;/li&gt;
&lt;li&gt;calculating image &lt;a href=&quot;https://blurha.sh/&quot;&gt;blurhashes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Extracting and Storing Audio Sample Data&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s assume that we are building a directory of songs. We might use a &lt;code&gt;Song&lt;/code&gt; model, which has an attached &lt;code&gt;recording&lt;/code&gt; for this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  has_one_attached :recording
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we create a new record of this model and attach an audio file, what happens? In a full-stack scenario, we would use an upload form, but for the sake of investigating the analysis process, let&amp;#39;s just create one from the Rails console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(dev)&amp;gt; song = Song.create(title: &amp;quot;Ruby Blues in D Flat&amp;quot;)
(dev)&amp;gt; song.recording.attach(io: File.open(&amp;quot;/path/to/file&amp;quot;), filename: &amp;quot;ruby_blues.wav&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apart from the usual SQL log, Rails also informs us that it has enqueued a job to process the data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;Enqueued ActiveStorage::AnalyzeJob (Job ID: 2a923033-b0d2-4bfc-b368-d3eb1344e64b) to Async(default) with arguments: #&amp;lt;GlobalID:0x000000012292f428 @uri=#&amp;lt;URI::GID gid://active-storage-analyzers-previews/ActiveStorage::Blob/1&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s now inspect the respective &lt;code&gt;Attachment&lt;/code&gt; and &lt;code&gt;Blob&lt;/code&gt; records:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(dev)&amp;gt; song.recording_attachment
=&amp;gt;
#&amp;lt;ActiveStorage::Attachment:0x00000001240c8eb8
  id: 1,
  name: &amp;quot;recording&amp;quot;,
  record_type: &amp;quot;Song&amp;quot;,
  record_id: 1,
  blob_id: 1,
  created_at: &amp;quot;2025-06-15 15:35:59.470664000 +0000&amp;quot;&amp;gt;

(dev)&amp;gt; song.recording_blob
=&amp;gt;
#&amp;lt;ActiveStorage::Blob:0x00000001235c6f60
  id: 1,
  key: &amp;quot;oi7xszss3y6kfer601zvxfpl1muz&amp;quot;,
  filename: &amp;quot;ruby_blues.wav&amp;quot;,
  content_type: &amp;quot;audio/x-wav&amp;quot;,
  metadata: {&amp;quot;identified&amp;quot; =&amp;gt; true},
  service_name: &amp;quot;local&amp;quot;,
  byte_size: 35765766,
  checksum: &amp;quot;ddUPZtkz3hqVI9wUGOwp4g==&amp;quot;,
  created_at: &amp;quot;2025-06-15 15:35:59.461367000 +0000&amp;quot;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aha! The file has been correctly identified as being of type &lt;code&gt;audio/x-wav&lt;/code&gt;, but apart from that, there&amp;#39;s no other metadata being persisted. We&amp;#39;re here to change that.&lt;/p&gt;
&lt;p&gt;First, let&amp;#39;s write our custom analyzer. We&amp;#39;ll put it in the &lt;code&gt;lib/active_storage&lt;/code&gt; directory, and call it &lt;code&gt;ActiveStorage::WaveformAnalyzer&lt;/code&gt;. To draw upon the existing implementation, we&amp;#39;ll inherit from &lt;code&gt;ActiveStorage::Analyzer::AudioAnalyzer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We saw above that the &lt;code&gt;metadata&lt;/code&gt; method is responsible for returning the appropriate data, so we&amp;#39;ll override it. We have to be careful to call &lt;code&gt;super&lt;/code&gt; and merge any new data into what the parent class already provides.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# lib/active_storage/waveform_analyzer.rb

module ActiveStorage
  class WaveformAnalyzer &amp;lt; ActiveStorage::Analyzer::AudioAnalyzer
    def metadata
      super.merge waveform
    end

    def waveform
      rms_values = []

      download_blob_to_tempfile do |file|
        IO.popen([ ffmpeg_path,
          &amp;quot;-i&amp;quot;, file.path,
          &amp;quot;-ac&amp;quot;, &amp;quot;1&amp;quot;,
          &amp;quot;-f&amp;quot;, &amp;quot;f32le&amp;quot;, &amp;quot;-&amp;quot;
        ], &amp;quot;rb&amp;quot;) do |io|
          frame_size = 4 # mono, 4 bytes (float32)
          chunk_size = 512 * frame_size # 512 frames

          while chunk = io.read(chunk_size)
            floats = chunk.unpack(&amp;quot;e*&amp;quot;) # little-endian float32

            next if floats.empty?

            rms = Math.sqrt(floats.sum { _1 ** 2 } / floats.size)
            rms_values &amp;lt;&amp;lt; rms
          end
        end
      end

      # optionally store as Base64 packed string to save space
      # { waveform: [ rms_values.pack(&amp;quot;e*&amp;quot;) ].pack(&amp;quot;m0&amp;quot;) }

      { waveform: rms_values }
    end

    def ffmpeg_path
      ActiveStorage.paths[:ffmpeg] || &amp;quot;ffmpeg&amp;quot;
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The real meat, though, is of course the computation of waveform datapoints. Because ActiveStorage depends on it, we can utilize &lt;code&gt;ffmpeg&lt;/code&gt; for our purposes. The full call we pass to &lt;code&gt;IO.popen&lt;/code&gt; here reads like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;ffmpeg -i FILE_TO_ANALYZE -ac 1 -f f32le -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;-ac 1&lt;/code&gt; tells ffmpeg to mix the audio down to one channel, while &lt;code&gt;-f f32le&lt;/code&gt; specifies &amp;quot;float 32-bit little endian&amp;quot; as the output format. The final dash &lt;code&gt;-&lt;/code&gt; instructs ffmpeg to output this to STDOUT, so we can actually pick it up in the block passed to &lt;code&gt;IO.popen&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s one important signal processing detail to mention: storing each and every sample as metadata wouldn&amp;#39;t be very efficient — in fact, it would result in storing the entire audio file as a JSON array. Instead, we perform some data compression here by calculating the &lt;a href=&quot;https://en.wikipedia.org/wiki/Root_mean_square&quot;&gt;root mean square&lt;/a&gt; over a frame. That&amp;#39;s only a fancy way of saying we&amp;#39;re calculating the average of 512 (an arbitrary number, but mostly a power of 2) samples, but we square it and subsequently take the square root, because audio sample data can be both positive and negative.&lt;/p&gt;
&lt;p&gt;Our new analyzer isn&amp;#39;t yet wired up to be used by the Rails application, so we do that in an initializer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# config/initializers/active_storage.rb

require_relative &amp;quot;../../lib/active_storage/waveform_analyzer.rb&amp;quot;

Rails.application.config.active_storage.analyzers.prepend ActiveStorage::WaveformAnalyzer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s important to &lt;code&gt;prepend&lt;/code&gt; it to the list here, because, as we&amp;#39;ve observed above, the &lt;strong&gt;first&lt;/strong&gt; analyzer in the list that accepts audio files will be used.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s put it to use in the Rails console again:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(dev)&amp;gt; song = Song.create(title: &amp;quot;Ruby Blues in D Flat&amp;quot;)
(dev)&amp;gt; song.recording.attach(io: File.open(&amp;quot;/path/to/file&amp;quot;), filename: &amp;quot;ruby_blues.wav&amp;quot;)
(dev)&amp;gt; song.reload.recording_attachment.metadata[:waveform]
=&amp;gt;
[0.00012394643736996606,
 0.00087319937227967,
 0.0037783625670793465,
 0.005877352246693453,
 ... etc.]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have a compressed representation of the audio in our metadata, how can we make use of it? We&amp;#39;ll take a look at an idiomatic ActiveStorage method in the second part of this series, but for starters, many JavaScript audio widgets allow you to specify precomputed waveform data, &lt;a href=&quot;https://wavesurfer.xyz/examples/?predecoded.js&quot;&gt;like WaveSurfer does in this example&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Calculating Image Blurhashes in Ruby&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blurha.sh/&quot;&gt;Blurhashes&lt;/a&gt; are compressed representations of images you can use instead of generic placeholders for an enhanced lazy loading experience. The &lt;a href=&quot;https://github.com/Gargron/blurhash&quot;&gt;blurhash&lt;/a&gt; Ruby gem provides a straightforward way to encode such strings from images. We add it to our application&amp;#39;s dependencies like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add blurhash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we begin writing our custom analyzer, it&amp;#39;s important to note that the image processing backend comes in two flavors: Vips or ImageMagick. Since its API is a bit simpler, we&amp;#39;ll concentrate on the latter and configure it in &lt;code&gt;config/application.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;config.active_storage.variant_processor = :mini_magick
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now begin our implementation by subclassing the &lt;code&gt;ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick&lt;/code&gt; base analyzer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# lib/active_storage/blurhash_analyzer.rb

module ActiveStorage
  class BlurhashAnalyzer &amp;lt; ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick
    attr_accessor :thumbnail

    def metadata
      read_image do |image|
        build_thumbnail(image)
        super.merge blurhash
      end
    end

    def blurhash
      {
        blurhash: ::Blurhash.encode(
          thumbnail.width,
          thumbnail.height,
          pixels
        )
      }
    end

    def build_thumbnail(image)
      # we scale down the image for faster blurhash processing
      @thumbnail ||= MiniMagick::Image.open(
        ::ImageProcessing::MiniMagick.source(image.path).resize_to_limit(200, 200).loader(page: 0).call.path
      )
    end

    def pixels = @thumbnail.get_pixels.flatten

    protected

    def processor = &amp;quot;ImageMagick&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&amp;#39;s going on here? We encounter an already familiar pattern: we populate the database column of the same name with more data using the &lt;code&gt;metadata&lt;/code&gt; method. &lt;code&gt;read_image&lt;/code&gt; is just a helper method provided by Rails that opens the image as a file, ready for us to use. The &lt;code&gt;build_thumbnail&lt;/code&gt; method is actually optional, but very helpful for speeding up the blurhash calculation. We simply use &lt;code&gt;MiniMagick&lt;/code&gt; to scale down the image to fit within the bounds of 200x200 pixels. The &lt;code&gt;blurhash&lt;/code&gt; method then employs this method to build the compressed string representation from the downscaled image.&lt;/p&gt;
&lt;p&gt;Like above, we prepend it to the list of analyzers in our initializer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# config/initializers/active_storage.rb

require_relative &amp;quot;../../lib/active_storage/waveform_analyzer.rb&amp;quot;
require_relative &amp;quot;../../lib/active_storage/blurhash_analyzer.rb&amp;quot;

Rails.application.config.active_storage.analyzers.prepend ActiveStorage::WaveformAnalyzer
Rails.application.config.active_storage.analyzers.prepend ActiveStorage::BlurhashAnalyzer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it&amp;#39;s time to test it out. For this, we&amp;#39;ll use a &lt;a href=&quot;https://picsum.photos/id/128/600/400&quot;&gt;test image from picsum.photos&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-07/picsum-photo.jpg&quot; alt=&quot;Test image from picsum&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s open a Rails console and attach this image to a post:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(dev)&amp;gt; post = Post.create(title: &amp;quot;Active Storage Analyzers&amp;quot;)
(dev)&amp;gt; post.images.attach(io: URI.open(&amp;quot;https://picsum.photos/id/128/1200/800&amp;quot;), filename: &amp;quot;picsum_128.jpg&amp;quot;)

# we inspect its metadata we will now find a blurhash representation of the image:

(dev)&amp;gt; post.reload.images.first.metadata[&amp;quot;blurhash&amp;quot;]
=&amp;gt; &amp;quot;LWDJS1o#D%kD~qbIIUof%2WARkfP&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For reference, converted to an actual preview image, it looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-07/blurhash.png&quot; alt=&quot;Blurhash preview image&quot;/&gt;&lt;/p&gt;
&lt;p&gt;To make use of this in our application frontend, the blurhash needs to be decoded and presented. Typically, this involves using the official &lt;a href=&quot;https://github.com/woltapp/blurhash/tree/master/TypeScript&quot;&gt;TypeScript library&lt;/a&gt; and a bespoke Stimulus controller. In the second part of this series, we&amp;#39;ll take a look at implementing a pure ActiveStorage-generated preview.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s it for this first part!&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this article, we&amp;#39;ve taken a few first steps to customize how ActiveStorage handles and processes media data. We&amp;#39;ve learned that the &lt;code&gt;ActiveStorage::Blob&lt;/code&gt; model is where the data describing an attachment is stored. When writing bespoke analyzers, it&amp;#39;s necessary to put any results in its &lt;code&gt;metadata&lt;/code&gt; column.&lt;/p&gt;
&lt;p&gt;We then looked at two examples explaining when and how to write your own custom ActiveStorage analyzers: extracting interleaved waveform data from audio files and calculating image blurhashes. Both implementations demonstrate the diligence that has been put into ActiveStorage&amp;#39;s background media processing API: the glue code that is necessary to plug binaries like ffmpeg or ImageMagick into the analysis pipeline is minimal.&lt;/p&gt;
&lt;p&gt;In the second and final part of this series, we will reverse this process and examine ways to implement custom ActiveStorage previewers, providing compact graphic representations of the calculated metadata.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Deep Dive into Solid Queue for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/06/18/a-deep-dive-into-solid-queue-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2025/06/18/a-deep-dive-into-solid-queue-for-ruby-on-rails.html</id>
    <published>2025-06-18T00:00:00+00:00</published>
    <updated>2025-06-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the second part of our series, let&#039;s dive deeper into some of Solid Queue&#039;s more advanced features.</summary>
    <content type="html">&lt;p&gt;Our previous article in this series established that Solid Queue is an excellent choice if you need a system for processing background jobs. It minimizes external dependencies — no need for Redis! — by storing all jobs in your database. Despite that, it is incredibly performant.&lt;/p&gt;
&lt;p&gt;But just being performant is not enough for a production-ready background job system. Rails developers have come to expect a lot over the years. We don&amp;#39;t just want to enqueue jobs to run in the background. We want to schedule jobs, run them on a recurring schedule, and we might even want to limit how many jobs can run concurrently. We want more features!&lt;/p&gt;
&lt;p&gt;Amazingly, Solid Queue provides all of those features out of the box. Let&amp;#39;s dive deeper into Solid Queue and learn how that&amp;#39;s possible!&lt;/p&gt;
&lt;h2&gt;Scheduling Jobs with Solid Queue for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;First, it&amp;#39;s time for a small recap. Solid Queue uses your database — and only your database — to store job data. Everything it does is backed by one database table or another. Scheduling jobs — that is, designating jobs to run at some specific point in the future — is no different. Any scheduled job is stored in &lt;code&gt;solid_queue_scheduled_executions&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/generators/solid_queue/install/templates/db/queue_schema.rb
create_table &amp;quot;solid_queue_scheduled_executions&amp;quot;, force: :cascade do |t|
  t.bigint &amp;quot;job_id&amp;quot;, null: false
  t.string &amp;quot;queue_name&amp;quot;, null: false
  t.integer &amp;quot;priority&amp;quot;, default: 0, null: false
  t.datetime &amp;quot;scheduled_at&amp;quot;, null: false
  t.datetime &amp;quot;created_at&amp;quot;, null: false
  t.index [ &amp;quot;job_id&amp;quot; ], name: &amp;quot;index_solid_queue_scheduled_executions_on_job_id&amp;quot;, unique: true
  t.index [ &amp;quot;scheduled_at&amp;quot;, &amp;quot;priority&amp;quot;, &amp;quot;job_id&amp;quot; ], name: &amp;quot;index_solid_queue_dispatch_all&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This table is almost identical to the &lt;code&gt;solid_queue_ready_executions&lt;/code&gt; table. The only difference is the addition of the &lt;code&gt;scheduled_at&lt;/code&gt; column, which tells us when a scheduled job is supposed to be executed. Let&amp;#39;s confirm that by looking at what happens when we schedule a job.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;MyJob.set(wait_until: Date.tomorrow.noon).perform_later
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT
  INTO &amp;quot;solid_queue_scheduled_executions&amp;quot; (&amp;quot;job_id&amp;quot;, &amp;quot;queue_name&amp;quot;, &amp;quot;priority&amp;quot;, &amp;quot;scheduled_at&amp;quot;, &amp;quot;created_at&amp;quot;)
  VALUES (1, &amp;#39;default&amp;#39;, 0, &amp;#39;2025-04-02 12:00:00.000000&amp;#39;, &amp;#39;2025-04-01 12:00:00.000000&amp;#39;)
  RETURNING &amp;quot;id&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are no surprises there. Solid Queue adds a new row to the &lt;code&gt;solid_queue_scheduled_executions&lt;/code&gt; table, which contains the data that we&amp;#39;d expect. But how do we go from such a record existing to actually running a job at the right time?&lt;/p&gt;
&lt;p&gt;We need a process that continuously polls the &lt;code&gt;solid_queue_scheduled_executions&lt;/code&gt; table. That process is called the Dispatcher, and it is responsible for executing scheduled jobs on time. It begins when Solid Queue starts — no additional configuration is required. However, if needed you can start only the dispatcher process by running Solid Queue with a specific configuration.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# Start Solid Queue with this configuration, for example, with bin/jobs -c config/only_dispatcher.yml
production:
  dispatchers:
    - polling_interval: 1
      batch_size: 500
      concurrency_maintenance_interval: 300
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In case you were wondering how the Dispatcher process is supervised, that is the responsibility of the aptly named &lt;code&gt;Supervisor&lt;/code&gt;. It keeps track of any running processes within Solid Queue, including worker processes and Dispatchers.&lt;/p&gt;
&lt;p&gt;So, how does the Dispatcher actually work? It defines a &lt;code&gt;poll&lt;/code&gt; method called within a loop to continuously check for scheduled jobs. The polling code is spread over several classes and modules, but in a heavily simplified form, it looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/solid_queue/dispatcher.rb
class Dispatcher &amp;lt; Processes::Poller
  # Batch size and polling interval are based on your configuration
  def poll
    job_ids = ScheduledExecution.due.ordered.limit(batch_size).pluck(:job_id)
    jobs = Job.where(id: job_ids)
    Job.dispatch_all(jobs).map(&amp;amp;:id).then do |dispatched_job_ids|
      ScheduledExecution.where(job_id: dispatched_job_ids).delete_all
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The query to retrieve &amp;#39;ready&amp;#39; scheduled executions is straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT &amp;quot;solid_queue_scheduled_executions&amp;quot;.*
  FROM &amp;quot;solid_queue_scheduled_executions&amp;quot;
  WHERE &amp;quot;solid_queue_scheduled_executions&amp;quot;.&amp;quot;scheduled_at&amp;quot; &amp;lt;= &amp;#39;2025-04-02 12:00:01.000000&amp;#39;
  ORDER BY &amp;quot;solid_queue_scheduled_executions&amp;quot;.&amp;quot;scheduled_at&amp;quot; ASC,
    &amp;quot;solid_queue_scheduled_executions&amp;quot;.&amp;quot;priority&amp;quot; ASC,
    &amp;quot;solid_queue_scheduled_executions&amp;quot;.&amp;quot;job_id&amp;quot; ASC
  LIMIT 500
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, any scheduled job with &lt;code&gt;scheduled_at&lt;/code&gt; in the past is ready to be dispatched. As we covered in part one of this series, when Solid Queue dispatches a job, it creates a &lt;code&gt;ReadyExecution&lt;/code&gt; record and destroys the corresponding &lt;code&gt;ScheduledExecution&lt;/code&gt; record. The &lt;code&gt;ReadyExecution&lt;/code&gt; record is then picked up by regular worker processes, and the corresponding job runs.&lt;/p&gt;
&lt;p&gt;So far, so good. Scheduled jobs are really not that complicated! Let&amp;#39;s look at something more complex: recurring tasks.&lt;/p&gt;
&lt;h2&gt;Recurring Tasks&lt;/h2&gt;
&lt;p&gt;Recurring tasks are an oft-requested feature for background job processors. Simply put, they are background jobs that should run on a recurring schedule. They are similar to &lt;a href=&quot;https://en.wikipedia.org/wiki/Cron&quot;&gt;Cron jobs&lt;/a&gt; in that you define a schedule (such as every five minutes, every day at noon, and so on) for when work should occur.&lt;/p&gt;
&lt;p&gt;In Solid Queue, you configure your recurring jobs using the &lt;code&gt;config/recurring.yml&lt;/code&gt; file. For example, if we wanted to run a &lt;code&gt;CleanupData&lt;/code&gt; job every day at noon, this is how we would do it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;production:
  cleanup_data:
    class: CleanupData
    args: []
    schedule: every day at noon
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Solid Queue uses &lt;a href=&quot;https://github.com/floraison/fugit&quot;&gt;Fugit&lt;/a&gt; to parse schedule expressions, which is why human-readable schedules such as &amp;#39;every day at noon&amp;#39; are permitted. When using scheduled tasks, you define the class of the job to be run and any job arguments. The excellent &lt;a href=&quot;https://github.com/rails/solid_queue?tab=readme-ov-file#recurring-tasks&quot;&gt;SolidQueue recurring tasks ReadMe&lt;/a&gt; provides more details. We&amp;#39;re here to learn how it works, so let&amp;#39;s look under the hood.&lt;/p&gt;
&lt;p&gt;Recurring tasks are represented by the &lt;code&gt;RecurringTask&lt;/code&gt; model, which is backed by a corresponding &lt;code&gt;solid_queue_recurring_tasks&lt;/code&gt; table. The columns therein correspond to the fields available in the configuration file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/generators/solid_queue/install/templates/db/queue_schema.rb
create_table &amp;quot;solid_queue_recurring_tasks&amp;quot;, force: :cascade do |t|
  t.string &amp;quot;key&amp;quot;, null: false
  t.string &amp;quot;schedule&amp;quot;, null: false
  t.string &amp;quot;command&amp;quot;, limit: 2048
  t.string &amp;quot;class_name&amp;quot;
  t.text &amp;quot;arguments&amp;quot;
  t.string &amp;quot;queue_name&amp;quot;
  t.integer &amp;quot;priority&amp;quot;, default: 0
  t.boolean &amp;quot;static&amp;quot;, default: true, null: false
  t.text &amp;quot;description&amp;quot;
# ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you start SolidQueue, recurring task records are created according to your recurring tasks configuration file. To create jobs at the right time, we once again need a new process — this time called the Scheduler. The Scheduler is a sibling to the Dispatcher, which we already know about. It works in almost the same way: A new process is spun up when Solid Queue starts, and this process runs an endless loop. The difference between the Scheduler and the Dispatcher is what happens within that loop. Where the Dispatcher queries the &lt;code&gt;solid_queue_scheduled_executions&lt;/code&gt; table, the Scheduler queries &lt;code&gt;solid_queue_recurring_tasks&lt;/code&gt; — and schedules jobs at the right time. So, how exactly does the Scheduler know what the right time is, and when to schedule the right jobs?&lt;/p&gt;
&lt;p&gt;To answer that question, we have to examine the implementation closely. The scheduler class creates a new &lt;code&gt;RecurringSchedule&lt;/code&gt; object that defines a &lt;code&gt;schedule&lt;/code&gt; method. That method is repeatedly called for each scheduled task. Here&amp;#39;s a simplified version:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/solid_queue/scheduler/recurring_schedule.rb
def schedule(task)
  scheduled_task = Concurrent::ScheduledTask.new(task.delay_from_now, args: [ self, task, task.next_time ]) do |thread_schedule, thread_task, thread_task_run_at|
    thread_schedule.schedule(thread_task)
    thread_task.enqueue(at: thread_task_run_at)
  end
  scheduled_task.tap(&amp;amp;:execute)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s untangle this code. Solid Queue uses &lt;a href=&quot;https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ScheduledTask.html&quot;&gt;&lt;code&gt;Concurrent::ScheduledTask&lt;/code&gt;&lt;/a&gt; (from the &lt;a href=&quot;https://github.com/ruby-concurrency/concurrent-ruby&quot;&gt;concurrent-ruby library&lt;/a&gt;) to spawn a new thread. That thread is scheduled to run at the time specified by the recurring task&amp;#39;s schedule. When that thread runs, it first recursively spawns another thread to schedule the next recurring task. Then, it enqueues the &amp;#39;current&amp;#39; scheduled job.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at an example of a simple recurring task to get a handle on things.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;production:
  my_periodic_task:
    class: CleanupData
    args: []
    schedule: every hour
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we start Solid Queue at 8:30, the variables within the schedule method are assigned the following values. Not verbatim, mind you. We&amp;#39;re greatly simplifying here.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;task&lt;/td&gt;
&lt;td&gt;my_periodic_task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;task.delay_from_now&lt;/td&gt;
&lt;td&gt;30 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;task.next_time&lt;/td&gt;
&lt;td&gt;9:00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;So, our background thread is scheduled to run thirty minutes from now, which is 9:00. When that time rolls around, the background thread is executed. It runs &lt;code&gt;thread_task.enqueue(at: 9:00)&lt;/code&gt; — so an instance of &lt;code&gt;CleanupData&lt;/code&gt; is queued for execution. It also calls itself recursively via &lt;code&gt;thread_schedule.schedule&lt;/code&gt;. Because it is now 9:00, the variables for this invocation have changed.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;task&lt;/td&gt;
&lt;td&gt;my_periodic_task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;task.delay_from_now&lt;/td&gt;
&lt;td&gt;59 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;task.next_time&lt;/td&gt;
&lt;td&gt;10:00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;So, the background thread is scheduled to run again at 10:00, and the cycle continues. You may be wondering what happens if the scheduling thread is killed, for example during a re-deploy or system crash. Won&amp;#39;t that upset your schedules? Luckily, the answer is no. Cron schedules are static. An expression like &amp;#39;Every Hour&amp;#39; always resolves to 10:00, 11:00, 12:00, and so on, regardless of when Solid Queue starts. Any interruptions to the scheduling thread don&amp;#39;t change that.&lt;/p&gt;
&lt;p&gt;Here are some other implementation details to be aware of. First, this pattern of scheduling the next occurrence of a recurring task before executing it is inspired by &lt;a href=&quot;https://github.com/bensheldon/good_job/blob/994ecff5323bf0337e10464841128fda100750e6/lib/good_job/cron_manager.rb#L84&quot;&gt;GoodJob&lt;/a&gt;. Second, &lt;code&gt;RecurringTask.enqueue&lt;/code&gt; does not create a new &lt;code&gt;Job&lt;/code&gt; and &lt;code&gt;ReadyExecution&lt;/code&gt; record as you might expect. Instead, it creates yet another record, namely &lt;code&gt;RecurringExecution&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/generators/solid_queue/install/templates/db/queue_schema.rb
create_table &amp;quot;solid_queue_recurring_executions&amp;quot;, force: :cascade do |t|
  t.bigint &amp;quot;job_id&amp;quot;, null: false
  t.string &amp;quot;task_key&amp;quot;, null: false
  t.datetime &amp;quot;run_at&amp;quot;, null: false
  # ...
  t.index [ &amp;quot;job_id&amp;quot; ], name: &amp;quot;index_solid_queue_recurring_executions_on_job_id&amp;quot;, unique: true
  t.index [ &amp;quot;task_key&amp;quot;, &amp;quot;run_at&amp;quot; ], name: &amp;quot;index_solid_queue_recurring_executions_on_task_key_and_run_at&amp;quot;, unique: true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This record is solely to avoid executing recurring jobs multiple times. It has an index on &lt;code&gt;task_key&lt;/code&gt; and &lt;code&gt;run_at&lt;/code&gt; with unique constraints to serve that purpose. A &lt;code&gt;RecurringTask&lt;/code&gt; is only queued if there is no prior &lt;code&gt;RecurringExecution&lt;/code&gt; for the same time and the same job.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/solid_queue/recurring_task.rb
def enqueue(at:)
  active_job = if using_solid_queue_adapter?
    # Create a RecurringExecution and enqueues the job
    enqueue_and_record(run_at: at)
  else
    # ...
  end
  rescue RecurringExecution::AlreadyRecorded
    payload[:skipped] = true
    false
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Attentive readers will notice that this code snippet points to a limitation in Solid Queue. That is, if you are not using Solid Queue as a backend to run your cron-style tasks — yes, you can do that — Solid Queue can&amp;#39;t guarantee that recurring jobs are enqueued only once. If you find yourself in such a situation, you should be aware of that.&lt;/p&gt;
&lt;p&gt;You may also be wondering what happens if the Scheduler process dies or is killed — for example, during a deployment. Since recurrences are managed by a thread, won&amp;#39;t killing the thread break schedules? Luckily, the answer to that is no.&lt;/p&gt;
&lt;h2&gt;Concurrency Controls&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s look at one final feature of Solid Queue, namely concurrency controls. Sometimes, you want to limit how many jobs of a certain kind can run simultaneously. You can do so using Solid Queue with &lt;code&gt;limits_concurrency&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyJob &amp;lt; ApplicationJob
  limits_concurrency to: 1, key: -&amp;gt;(user) { user.id }, duration: 15.minutes, group: &amp;#39;UserOnboarding&amp;#39;
  # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we are telling SolidQueue to run a maximum of one instance of &lt;code&gt;MyJob&lt;/code&gt; for each user. Let&amp;#39;s examine the configuration in more detail.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;to&lt;/code&gt;: The maximum number of jobs you want running concurrently.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;key&lt;/code&gt;: A required argument to designate which jobs should be limited together. In our example, jobs with the same User ID are limited to a single concurrent execution. You may use any job arguments as &lt;code&gt;key&lt;/code&gt;, but constants such as strings or symbols are also allowed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;duration&lt;/code&gt;: The maximum time for which Solid Queue can guarantee concurrency after a job is enqueued. If your jobs run longer than that, concurrency controls will not apply and jobs may overlap. We&amp;#39;ll learn why later!&lt;/li&gt;
&lt;li&gt;&lt;code&gt;group&lt;/code&gt;: You can use this option to limit concurrency across different job classes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to learn more, I refer you to &lt;a href=&quot;https://github.com/rails/solid_queue/?tab=readme-ov-file#concurrency-controls&quot;&gt;the concurrency control documentation&lt;/a&gt;. Concurrency controls are easily Solid Queue&amp;#39;s most sophisticated feature. If scheduled tasks didn&amp;#39;t already make your head spin, learning how this feature works surely will.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start with the basics. Like other Solid Queue features, concurrency controls are backed by various models and their corresponding database tables. Two that you need to be particularly aware of are &lt;code&gt;Semaphore&lt;/code&gt; and &lt;code&gt;BlockedExecution&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/generators/solid_queue/install/templates/db/queue_schema.rb
create_table &amp;quot;solid_queue_semaphores&amp;quot;, force: :cascade do |t|
  t.string &amp;quot;key&amp;quot;, null: false
  t.integer &amp;quot;value&amp;quot;, default: 1, null: false
  t.datetime &amp;quot;expires_at&amp;quot;, null: false
  # ...
end

create_table &amp;quot;solid_queue_blocked_executions&amp;quot;, force: :cascade do |t|
  t.bigint &amp;quot;job_id&amp;quot;, null: false
  t.string &amp;quot;queue_name&amp;quot;, null: false
  t.integer &amp;quot;priority&amp;quot;, default: 0, null: false
  t.string &amp;quot;concurrency_key&amp;quot;, null: false
  t.datetime &amp;quot;expires_at&amp;quot;, null: false
  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s look at &lt;code&gt;Semaphore&lt;/code&gt; first. As the name suggests, this is an implementation of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Semaphore_(programming)&quot;&gt;counting semaphore pattern&lt;/a&gt;. Whenever Solid Queue enqueues a job with &lt;code&gt;limits_concurrency&lt;/code&gt;, it first tries to acquire a semaphore lock based on a concurrency key. This concurrency key is based on the arguments passed to &lt;code&gt;limits_concurrency&lt;/code&gt;, namely the job class, the key, and the group name — if any was provided. If the semaphore is available, the job is enqueued. If it is not, a &lt;code&gt;BlockedExecution&lt;/code&gt; record is created instead.&lt;/p&gt;
&lt;p&gt;Semaphores have a &lt;code&gt;value&lt;/code&gt; to support multiple concurrent jobs. You can think of it as the remaining capacity of the semaphore. Acquiring a semaphore means decrementing that value, and releasing it means incrementing it. A semaphore is considered unavailable once its value is zero. Let&amp;#39;s look at an example of how the locking mechanism works for a simple job.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyJob &amp;lt; ApplicationJob
  # Only three jobs of &amp;#39;MyJob&amp;#39; should run at the same time
  limits_concurrency to: 3, key: -&amp;gt; { &amp;#39;MyJob&amp;#39; }, duration: 15.minutes
  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s see what happens if we try to enqueue this job multiple times in succession.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first instance of &lt;code&gt;MyJob&lt;/code&gt; is enqueued. There is no semaphore yet, so one is created. Its initial value is &lt;code&gt;limit - 1&lt;/code&gt;. Because your limit is three, the initial value of the semaphore is two.&lt;/li&gt;
&lt;li&gt;The second instance of &lt;code&gt;MyJob&lt;/code&gt; is enqueued. Solid Queue attempts to acquire a lock for that job. The job can be enqueued because the value is two, which is greater than zero. The value of the semaphore is decremented to one.&lt;/li&gt;
&lt;li&gt;A third instance of our job is enqueued. We repeat the same procedure as before. The value of the semaphore is now zero.&lt;/li&gt;
&lt;li&gt;A fourth instance of &lt;code&gt;MyJob&lt;/code&gt; is enqueued. Acquiring the semaphore fails because its value is now zero. A &lt;code&gt;BlockedExecution&lt;/code&gt; record is created for the job.&lt;/li&gt;
&lt;li&gt;The first instance of our job finishes. When it finishes, it releases the semaphore, so the semaphore value is once again one.&lt;/li&gt;
&lt;li&gt;On finishing, the first job instance also calls a method to release any blocked jobs.&lt;/li&gt;
&lt;li&gt;The fourth instance of &lt;code&gt;MyJob&lt;/code&gt; is released and again tries to acquire a lock. The semaphore value is one, so the lock can be acquired and the blocked job queued. The semaphore value is now zero.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The code for releasing a semaphore when a job finishes is straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/solid_queue/claimed_execution.rb
def perform
  result = execute
  # ...
ensure
  # This method releases the Semaphore and makes it available for the next blocked job.
  job.unblock_next_blocked_job
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is one more detail that we haven&amp;#39;t touched on yet. Why do semaphores have an expiry date, and why do we need to set a duration when using &lt;code&gt;limits_concurrency&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s consider what happens when a job crashes without releasing its semaphore — for example, when a worker processing that job dies. Unless we add some mechanism to clean up semaphores, the lock held by that job will be retained forever. In the worst case, this would forever block other jobs from being processed.&lt;/p&gt;
&lt;p&gt;Semaphores have an expiry that corresponds to the duration given in the job definition to avoid that situation. If a semaphore expires — which happens if no jobs are enqueued — the semaphore will be destroyed. We already know the process responsible for that — it&amp;#39;s our friend, the &lt;em&gt;Dispatcher&lt;/em&gt;. It instantiates the &lt;code&gt;ConcurrencyMaintenance&lt;/code&gt; class, which does two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, it removes any expired semaphores.&lt;/li&gt;
&lt;li&gt;Second, it will check if there are any blocked jobs and release them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jobs are released one by one, so concurrency limits will still hold. Consider, however, what happens if your job runs longer than the given duration. In that case, the semaphore will be cleaned up, although the job will still run. If another job is then enqueued, those jobs will overlap.&lt;/p&gt;
&lt;h2&gt;Monitoring Solid Queue for Rails with AppSignal&lt;/h2&gt;
&lt;p&gt;As we&amp;#39;ve established, Solid Queue can do a lot. However, with all these moving parts, monitoring becomes crucial. Luckily, &lt;a href=&quot;https://www.appsignal.com/ruby/solid-queue-monitoring&quot;&gt;AppSignal provides built-in support for Solid Queue&lt;/a&gt;, with ready-made dashboards for job execution times, throughput, and failure rates. Simply &lt;a href=&quot;https://appsignal.com/users/sign_up&quot;&gt;install AppSignal&lt;/a&gt; in your Rails application, and you&amp;#39;re good to go.&lt;/p&gt;
&lt;p&gt;AppSignal will automatically detect your usage of Solid Queue and create an active job dashboard that contains graphs for important metrics, such as error rate and throughput.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-06/solid-queue-dashboard.png&quot; alt=&quot;Solid Queue Job Dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;If you ever see jobs that misbehave — either because they run slowly or have too many errors — assign them a status and assignee to effectively resolve the issue.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-06/solid-queue-jobs.png&quot; alt=&quot;Solid Queue Job Details&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Obviously, you shouldn&amp;#39;t have to look at dashboards all day to figure out if there is a problem. AppSignal Alerts has your back. Simply create a new alert for job metrics, such as failure rate and job duration, and you&amp;#39;re all set.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-06/solid-queue-trigger.png&quot; alt=&quot;Solid Queue Alerts&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Solid Queue is amazing for adding powerful job processing to your application without hassle. AppSignal does the same when it comes to monitoring!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve covered a lot of ground in our exploration of Solid Queue&amp;#39;s advanced features. From scheduled jobs to complex dependency chains, each feature builds on the solid foundation we discussed in part one. As we&amp;#39;ve seen, it&amp;#39;s not easy to build a job processing backend. But by diving deep into the Solid Queue source code and its workings, we&amp;#39;ve gained an understanding and some appreciation for the challenges involved.&lt;/p&gt;
&lt;p&gt;In any case, Solid Queue is a wonderful addition to the Rails ecosystem, due to its excellent database design and process coordination. It provides the tools you need while maintaining its core promise: simplicity and reliability, without external dependencies.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Set Up Tracing for a Ruby on Rails Application in AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/05/21/set-up-tracing-for-a-ruby-on-rails-application-in-appsignal.html"/>
    <id>https://blog.appsignal.com/2025/05/21/set-up-tracing-for-a-ruby-on-rails-application-in-appsignal.html</id>
    <published>2025-05-21T00:00:00+00:00</published>
    <updated>2025-05-21T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s use AppSignal to detect, diagnose, remove performance bottlenecks, and employ tracing in a Ruby on Rails application.</summary>
    <content type="html">&lt;p&gt;In this guide, we&amp;#39;ll harness AppSignal to detect, diagnose, and remove performance bottlenecks and employ proper tracing in a Ruby on Rails application.
From setting up tracing to capturing errors and logging, we’ve got you covered.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll ensure our application runs smoother than ever,
even under the heaviest loads!&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s quickly touch on how to define tracing and its benefits.&lt;/p&gt;
&lt;h2&gt;What Is Tracing?&lt;/h2&gt;
&lt;p&gt;Tracing is the process of following a request and operation through an application. In Ruby applications, tracing captures the execution flow, providing deep insights into the performance of various components.&lt;/p&gt;
&lt;h3&gt;Benefits of Tracing&lt;/h3&gt;
&lt;p&gt;Tracing has several benefits, including:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Performance Optimization&lt;/strong&gt;: Tracing identifies slow parts of an application that need performance improvements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better Debugging&lt;/strong&gt;: Detailed traces allow you to quickly pin down code issues and determine their causes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved Reliability&lt;/strong&gt;: Tracing tracks application behavior, ensuring more reliable and effective system operation.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;A Scenario: Our Lagging Rails App&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s say it&amp;#39;s Black Friday, the biggest shopping day of the year. Your Rails-based e-commerce platform is buzzing with thousands of eager customers, their carts brimming with products, ready to check out. Everything seems perfect — until the system starts lagging.&lt;/p&gt;
&lt;p&gt;Transactions fail. Abandoned carts skyrocket. Panic ensues. This is every developer&amp;#39;s nightmare. But what if you had a secret weapon? A tool that not only alerts you to issues in real-time but also dives deep into the heart of your application, tracing every request, every database query, and every background job? Enter &lt;strong&gt;AppSignal&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;How to Set Up Tracing for Ruby on Rails Apps using AppSignal&lt;/h2&gt;
&lt;p&gt;To demonstrate the power of tracing using AppSignal, I have created a &lt;a href=&quot;https://github.com/DanielAmah/ecommerce-appsignal-integration&quot;&gt;sample Rails e-commerce project&lt;/a&gt; that we can integrate with AppSignal. This will help you see firsthand how tracing can identify and resolve these problems.&lt;/p&gt;
&lt;h3&gt;Prerequisites&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ruby Version&lt;/strong&gt;: AppSignal is compatible with Ruby 2.5 and above.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AppSignal Account&lt;/strong&gt;: &lt;a href=&quot;https://appsignal.com/users/sign_up&quot;&gt;Create an account on AppSignal&lt;/a&gt; (there&amp;#39;s a free trial available)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now you&amp;#39;re ready to set up tracing in your Ruby application using AppSignal.&lt;/p&gt;
&lt;h3&gt;Step 1: Install the AppSignal Gem&lt;/h3&gt;
&lt;p&gt;Here&amp;#39;s the gem:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;appsignal&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simply run &lt;code&gt;bundle install&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2: Initialize AppSignal&lt;/h3&gt;
&lt;p&gt;Run the AppSignal installation command to set up the necessary configuration files.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec appsignal install &amp;lt;YOUR-PUSH-API-KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Follow the prompt on your terminal. You can choose a config file or environment variables to configure AppSignal in your app. We will choose the config file option.&lt;/p&gt;
&lt;p&gt;This command will generate an &lt;code&gt;appsignal.yml&lt;/code&gt; configuration file in your config directory.
This file will be pre-filled with your AppSignal push API key and some basic configuration settings.&lt;/p&gt;
&lt;h3&gt;Step 3: Configure AppSignal&lt;/h3&gt;
&lt;p&gt;Ensure &lt;code&gt;config/appsignal.yml&lt;/code&gt; is correctly configured for your environments. This file contains configuration settings for different environments (development, test, production). &lt;a href=&quot;https://docs.appsignal.com/support/limiting-requests.html#ignore-actions&quot;&gt;Ignore actions&lt;/a&gt; that provide little value.&lt;/p&gt;
&lt;p&gt;And ta da! You&amp;#39;re all set up:
&lt;img src=&quot;/images/blog/2025-05/appsignal-data.png&quot; alt=&quot;AppSignal receives data&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Instrumenting our Ruby on Rails Application&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;AppSignal can automatically instrument several key components of our Rails application&lt;/a&gt;, such as database queries and web requests. We can also add custom instrumentation to trace specific parts of our application.&lt;/p&gt;
&lt;h3&gt;Custom Rails Instrumentation&lt;/h3&gt;
&lt;p&gt;To instrument specific parts of our code, we will wrap a couple of lines of code with the &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/instrumentation.html&quot;&gt;&lt;code&gt;Appsignal.instrument&lt;/code&gt; method&lt;/a&gt;. This method can be used to trace specific blocks of code, such as more complex parts of controller actions or background jobs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/orders_controller.rb

class OrdersController &amp;lt; ApplicationController
  def index

    @orders = Order.all #=&amp;gt; Candidate for N+1 queries


    Appsignal.instrument(&amp;#39;calculate_totals.orders&amp;#39;) do
      @orders.each do |order|
        order.total_price = order.line_items.sum { |item| item.quantity * item.product.price }
      end
    end

    render json: @orders.as_json(include: {
      user: { only: [:id, :name, :email] },
      line_items: { include: { product: { only: [:id, :name, :price] } }, only: [:id, :quantity, :price] }
    })

  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sending a request to this endpoint will result in an N+1 query being reported.&lt;/p&gt;
&lt;p&gt;An &lt;a href=&quot;https://blog.appsignal.com/2020/06/09/n-plus-one-queries-explained.html&quot;&gt;N+1 issue occurs&lt;/a&gt; when our application makes multiple database queries to load associated records for each object in a collection, instead of using a single, efficient query. This can significantly degrade performance, especially when dealing with large datasets.&lt;/p&gt;
&lt;p&gt;The fix in this case is to eager load the get all orders query — simply replace this code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@orders = Order.all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@orders = Order.includes(:user, line_items: :product).all
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Collecting and Reporting Ruby Errors with AppSignal&lt;/h3&gt;
&lt;p&gt;AppSignal allows you to &lt;a href=&quot;https://www.appsignal.com/tour/errors&quot;&gt;capture and report application errors&lt;/a&gt; using the &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/exception-handling.html&quot;&gt;&lt;code&gt;Appsignal.set_error&lt;/code&gt; method&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here’s an example of capturing and reporting an error in the order controller index action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def index
  @orders = Order.all #=&amp;gt; Candidate for N+1 queries
  # Introduce a deliberate error based on order count
  if @orders.size &amp;gt; 10
    raise &amp;quot;Too many orders&amp;quot;
  end
  render json: @orders.as_json(include: {
    user: { only: [:id, :name, :email] },
    line_items: { include: { product: { only: [:id, :name, :price] } }, only: [:id, :quantity, :price] }
  })
rescue =&amp;gt; e
  # Report error to AppSignal
  Appsignal.set_error(e)
  render json: { error: e.message }, status: :internal_server_error
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we demonstrate custom error handling within a specific controller action. However, it&amp;#39;s important to note that AppSignal reports errors by default, so explicit error reporting with &lt;code&gt;Appsignal.set_error&lt;/code&gt; is not usually necessary.&lt;/p&gt;
&lt;p&gt;For consistent and centralized error handling across all controller actions, it’s recommended you use &lt;code&gt;rescue_from&lt;/code&gt; at the controller level. This approach ensures that any unhandled exceptions in your controller actions are properly reported and managed.&lt;/p&gt;
&lt;p&gt;Here’s an example of how you can implement this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class OrdersController &amp;lt; ApplicationController
  # Use rescue_from to handle errors for all controller actions
  rescue_from StandardError, with: :handle_error

  def index
    @orders = Order.includes(:user, line_items: :product).all

    Appsignal.instrument(&amp;#39;orders.index.custom_query&amp;#39;) do
      # Fake some complexity
      @orders.each do |order|
        order.total = order.line_items.sum { |item| item.quantity * item.product.price }
      end
    end

    render json: @orders.as_json(include: {
      user: { only: [:id, :name, :email] },
      line_items: { include: { product: { only: [:id, :name, :price] } }, only: [:id, :quantity, :price] }
    })
  end

  private

  # Error handling method
  def handle_error(exception)
    Appsignal.set_error(exception)
    render json: { error: exception.message }, status: :internal_server_error
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the &lt;em&gt;Error&lt;/em&gt; &amp;gt; &lt;em&gt;Issue list&lt;/em&gt; tab, we can see a list of all errors, the status of each issue, and how long ago the error occurred:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-05/error-issue-list.png&quot; alt=&quot;Errors Issue List&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Clicking on the &lt;code&gt;RuntimeError&lt;/code&gt;, we can see the error log and the code line that is triggering this error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2025-05/runtime-error.png&quot; alt=&quot;Error details in AppSignal&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s touch on some more advanced tracing techniques before we wrap up.&lt;/p&gt;
&lt;h3&gt;Advanced Tracing Techniques&lt;/h3&gt;
&lt;p&gt;We&amp;#39;ll implement some advanced tracing techniques on the order endpoint of our e-commerce app.&lt;/p&gt;
&lt;p&gt;These techniques will help us manage high-traffic environments efficiently, ensure data privacy, and secure access to trace data.&lt;/p&gt;
&lt;h4&gt;High Traffic Use Cases&lt;/h4&gt;
&lt;p&gt;In high-throughput environments, collecting trace data for every request can lead to significant overhead. Sampling helps reduce this by collecting trace data for only a subset of requests.&lt;/p&gt;
&lt;h4&gt;Adding Metadata to Transactions&lt;/h4&gt;
&lt;p&gt;You can supply extra context on errors and performance issues using tags and sample data. This can help to add information that is not already part of the request, session, or environment parameters. &lt;a href=&quot;https://docs.appsignal.com/guides/custom-data.html&quot;&gt;Read more about how to pass and add additional metadata&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Asynchronous Processing&lt;/h4&gt;
&lt;p&gt;Asynchronous processing is a common technique used to handle tasks that are too time-consuming to be performed within the request-response cycle, such as sending emails, processing background jobs, and handling large data imports. Ruby on Rails applications typically use background job libraries like Sidekiq, Resque, or Delayed Job to manage these tasks.&lt;/p&gt;
&lt;p&gt;AppSignal seamlessly integrates with libraries such as &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/active-job.html&quot;&gt;Active Job&lt;/a&gt;, &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/delayed-job.html&quot;&gt;DelayedJob&lt;/a&gt;, &lt;a href=&quot;https://www.appsignal.com/ruby/shoryuken-monitoring&quot;&gt;Shoryuken&lt;/a&gt;, &lt;a href=&quot;https://www.appsignal.com/ruby/sidekiq-monitoring&quot;&gt;Sidekiq&lt;/a&gt;, and &lt;a href=&quot;https://www.appsignal.com/ruby/que-monitoring&quot;&gt;Que&lt;/a&gt; to provide insights into the performance and errors of your background jobs.&lt;/p&gt;
&lt;h4&gt;Security Considerations&lt;/h4&gt;
&lt;p&gt;Security is a very important piece of any production-ready application.
We need to ensure that sensitive data is not included in trace data to maintain user privacy. The values of filter parameters will be replaced with &lt;code&gt;[FILTERED]&lt;/code&gt; when transmitted to AppSignal.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s modify &lt;code&gt;config/appsignal.yml&lt;/code&gt; to include several sensitive request params that need masking.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# config/appsignal.yml
default: &amp;amp;defaults
  push_api_key: &amp;quot;&amp;lt;%= ENV[&amp;#39;APPSIGNAL_PUSH_API_KEY&amp;#39;] %&amp;gt;&amp;quot;
  name: &amp;quot;ECommerceApp&amp;quot;
  filter_parameters:
    - password
    - email
    - api_token
    - token
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you use the &lt;a href=&quot;https://guides.rubyonrails.org/security.html#logging&quot;&gt;Rails &lt;code&gt;filter_parameters&lt;/code&gt;&lt;/a&gt; config option, AppSignal will merge its config with Rails&amp;#39;s config, so there&amp;#39;s no need to configure it twice.&lt;/p&gt;
&lt;p&gt;By implementing these advanced tracing techniques, you can efficiently manage high-traffic environments and ensure the privacy of sensitive data. &lt;a href=&quot;https://docs.appsignal.com/ruby/configuration/options.html#option-filter_parameters&quot;&gt;Read more on filter parameters&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve seen that setting up tracing for a Ruby app using AppSignal involves understanding tracing fundamentals, preparing your application, and following a step-by-step setup process.
Advanced methods and debugging techniques can then be used to enhance tracing.&lt;/p&gt;
&lt;p&gt;Regularly applying tracing in development and maintenance cycles leads to higher performance and reliability.
&lt;a href=&quot;https://appsignal.com/users/sign_up&quot;&gt;Get started with AppSignal today&lt;/a&gt; for enhanced monitoring and deep performance insights into your Ruby applications.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Solid Queue for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/05/07/an-introduction-to-solid-queue-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2025/05/07/an-introduction-to-solid-queue-for-ruby-on-rails.html</id>
    <published>2025-05-07T00:00:00+00:00</published>
    <updated>2025-05-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this two-part series, we&#039;ll dig deep into Solid Queue&#039;s internals, discover what makes it unique, and learn more about why it was created in the first place.</summary>
    <content type="html">&lt;p&gt;One of the most exciting additions to Rails 8 is undoubtedly Solid Queue, a new
library for processing background jobs.&lt;/p&gt;
&lt;p&gt;You might not think it&amp;#39;s that big of a deal. After all, there are plenty of
other queuing systems out there. If you work with Rails, you&amp;#39;ll likely know about Sidekiq and Resque — both are exceptionally performant and reliable. There is also GoodJob and the venerable DelayedJob.
With all those options available, do we really need another queuing system?&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s find out together. In this two-part series, we&amp;#39;ll dig deep into Solid Queue&amp;#39;s internals, discover what makes it unique, and learn more
about why it was created in the first place.&lt;/p&gt;
&lt;h2&gt;Why Solid Queue for Ruby on Rails?&lt;/h2&gt;
&lt;p&gt;Since Rails 7, the team at 37Signals has been on a quest to reduce the operational overhead needed to launch a new Rails
application. As part of this, they made SQLite the new default database for Rails apps - even in production.
Furthermore, they started an effort to eliminate additional infrastructure dependencies to take full advantage of this
new default.&lt;/p&gt;
&lt;p&gt;37Signals had used &lt;a href=&quot;https://github.com/resque/resque&quot;&gt;Resque&lt;/a&gt; until then, and Resque requires Redis to function. So does &lt;a href=&quot;https://github.com/sidekiq/sidekiq&quot;&gt;Sidekiq&lt;/a&gt;, for that matter. To get rid of
Redis, they had to create a queuing system that relies only on your database — and that queuing system turned out to be
&lt;a href=&quot;https://github.com/rails/solid_queue&quot;&gt;Solid Queue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So that&amp;#39;s its main selling point: No additional dependencies; just use your database. Very nice! However, as with any queuing
system — and especially one that is the new Rails default — Solid Queue needs to satisfy some stringent requirements.&lt;/p&gt;
&lt;p&gt;It must provide all the features Rails developers are used to from other background job systems. As the Rails
default, it must support all databases that Rails works with. Obviously, it needs to satisfy standard safety
requirements — as in, it must never, ever lose jobs. Last but not least, it must be fast enough to be a viable
option for large production systems.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s quite a tall order! So, how does Solid Queue address all those requirements?&lt;/p&gt;
&lt;h2&gt;Solid Queue From The Top&lt;/h2&gt;
&lt;p&gt;There are many details to consider, but let&amp;#39;s start with a high-level architectural overview. You need to be aware of
two significant components: Jobs and Workers.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Job&lt;/code&gt; is an &lt;code&gt;ActiveRecord&lt;/code&gt; model, and what the user interacts with. Note that that&amp;#39;s not
necessarily true for other &lt;code&gt;ActiveJob&lt;/code&gt; backends — it&amp;#39;s just how SolidQueue implements background jobs. If you need to
create a new background job, this is the class that you inherit from. &lt;code&gt;Job&lt;/code&gt; also defines methods that enable you to
enqueue work, such as &lt;code&gt;Job.perform_later&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/jobs/my_job.rb
class MyJob &amp;lt; ApplicationJob
  queue_as :default

  def perform
    # Do something later
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Workers, as the name suggests, are the elements that perform the actual work. These are generally not directly created
by the programmer but automatically created based on how you configure your application. For example, to have your
application spawn two workers listening to all and two specific queues respectively, you&amp;#39;d use the following
configuration file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# config/queue.yml
production:
  workers:
    - queues: &amp;quot;*&amp;quot;
    - queues: [default, critical]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Workers are spawned as processes, running in the background, waiting for jobs to be assigned to them. As you may have guessed,
your database is the missing link between jobs and workers. Whenever Solid Queue does anything, one database table or
other is involved. SolidQueue does a lot of things, so
&lt;a href=&quot;https://github.com/rails/solid_queue/blob/main/lib/generators/solid_queue/install/templates/db/queue_schema.rb&quot;&gt;a lot of tables&lt;/a&gt;
are needed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/generators/solid_queue/install/templates/db/queue_schema.rb
ActiveRecord::Schema[7.1].define(version: 1) do
  create_table &amp;quot;solid_queue_jobs&amp;quot;, force: :cascade do |t|
    # ...
  end

  create_table &amp;quot;solid_queue_ready_executions&amp;quot;, force: :cascade do |t|
    # ...
  end

  create_table &amp;quot;solid_queue_scheduled_executions&amp;quot;, force: :cascade do |t|
    # ...
  end

  create_table &amp;quot;solid_queue_claimed_executions&amp;quot;, force: :cascade do |t|
    # ...
  end

  create_table &amp;quot;solid_queue_blocked_executions&amp;quot;, force: :cascade do |t|
    # ...
  end

  create_table &amp;quot;solid_queue_failed_executions&amp;quot;, force: :cascade do |t|
    #...
  end

  # Lots more tables below...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;The Life and Death of a SOLID Job&lt;/h2&gt;
&lt;p&gt;To understand what all those tables do and how they relate to the various features of Solid Queue, let&amp;#39;s look at the
life cycle of a job. When a user enqueues a job to be executed later — let&amp;#39;s say MyJob — a record is created in the
&lt;code&gt;solid_queue_jobs&lt;/code&gt; table. The record contains all the data required to execute the job — arguments, its name, the queue
it is put in, and so forth. If the job is enqueued to run as soon as possible (rather than scheduled to run at some
later point in time), an additional record is written to &lt;code&gt;solid_queue_ready_executions&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, running
&lt;code&gt;MyJob.perform_later&lt;/code&gt; results in the following SQL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT INTO &amp;quot;solid_queue_jobs&amp;quot; (&amp;quot;queue_name&amp;quot;, &amp;quot;class_name&amp;quot;, &amp;quot;arguments&amp;quot;, &amp;quot;priority&amp;quot;, &amp;quot;active_job_id&amp;quot;, &amp;quot;scheduled_at&amp;quot;, &amp;quot;finished_at&amp;quot;, &amp;quot;concurrency_key&amp;quot;, &amp;quot;created_at&amp;quot;, &amp;quot;updated_at&amp;quot;)
  VALUES (&amp;#39;default&amp;#39;, &amp;#39;MyJob&amp;#39;, &amp;#39;{&amp;quot;job_class&amp;quot;: &amp;quot;MyJob&amp;quot;,&amp;quot;...&amp;quot;,}&amp;#39;, 0, &amp;#39;...&amp;#39;, &amp;#39;2024-12-01 14:00:00&amp;#39;, NULL, NULL, &amp;#39;2024-12-01 14:00:00&amp;#39;, &amp;#39;2024-12-01 14:00:00&amp;#39;)
  RETURNING &amp;quot;id&amp;quot;
INSERT INTO &amp;quot;solid_queue_ready_executions&amp;quot; (&amp;quot;job_id&amp;quot;, &amp;quot;queue_name&amp;quot;, &amp;quot;priority&amp;quot;, &amp;quot;created_at&amp;quot;)
  VALUES (1, &amp;#39;default&amp;#39;, 0, &amp;#39;2024-12-01 14:00:00&amp;#39;)
  RETURNING &amp;quot;id&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your workers poll this table for new records. A worker process that finds a new record will first claim it by writing
another record to the &lt;code&gt;solid_queue_claimed_executions&lt;/code&gt; table — we&amp;#39;ll learn why that is necessary later. Only then will
the worker actually execute the job. Below is some heavily edited code to illustrate what is happening (much more is
happening in the actual code). If you are curious about the nitty-gritty details, I highly recommend you &lt;a href=&quot;https://github.com/rails/solid_queue/blob/main/lib/solid_queue/worker.rb&quot;&gt;check out the original source code&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Worker
  def run
    loop do
      break if shutting_down?

      unless poll &amp;gt; 0
        # Polling interval is configurable and defaults to 1ms
        sleep(polling_interval)
      end
    end
  end

  def poll
    # Claim jobs and then execute claimed jobs.
    claim_executions.then do |executions|
      executions.each do |execution|
        # Actually execute the job
      end
    end
  end

  def claim_executions
    # Query the ready executions table and claim a job for execution.
    with_polling_volume do
      SolidQueue::ReadyExecution.claim
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once a worker finishes a job, it removes the corresponding records from the &lt;code&gt;solid_queue_jobs,&lt;/code&gt;
&lt;code&gt;solid_queue_ready_executions&lt;/code&gt;, and &lt;code&gt;solid_queue_claimed_executions&lt;/code&gt; tables. That&amp;#39;s all there is to it — just polling
some tables, creating and removing records. Not so tricky, right? It would be if there weren&amp;#39;t critical
non-functional requirements to consider, too.&lt;/p&gt;
&lt;h2&gt;On Performance&lt;/h2&gt;
&lt;p&gt;To achieve production-ready performance, Solid Queue uses ingenious database design. You may have wondered why
workers poll &lt;code&gt;solid_queue_ready_executions&lt;/code&gt; rather than &lt;code&gt;solid_queue_jobs&lt;/code&gt;. The additional table seems redundant at
first glance.&lt;/p&gt;
&lt;p&gt;Consider that &lt;code&gt;solid_queue_jobs&lt;/code&gt; may contain thousands or millions of records, and querying that pile of data takes
time. In comparison, &lt;code&gt;solid_queue_ready_executions&lt;/code&gt; is tiny, as it only contains records for jobs that must be executed
right now! That leads to some serious speedup.&lt;/p&gt;
&lt;p&gt;The introduction of additional tables also simplifies queries. Workers only use two different queries for polling. They either
poll all queues or specific ones. That, in turn, allows for some nice
&lt;a href=&quot;https://en.wikipedia.org/wiki/Database_index#Covering_index&quot;&gt;covering&lt;/a&gt; indices.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT job_id
  FROM solid_queue_ready_executions
  WHERE queue_name = &amp;quot;default&amp;quot;
  ORDER BY priority ASC, job_id ASC
  LIMIT 4
  FOR UPDATE SKIP LOCKED
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Indices for polling solid queue ready executions
create_table &amp;quot;solid_queue_ready_executions&amp;quot;, force: :cascade do |t|
  t.index [ &amp;quot;priority&amp;quot;, &amp;quot;job_id&amp;quot; ], name: &amp;quot;index_solid_queue_poll_all&amp;quot;
  t.index [ &amp;quot;queue_name&amp;quot;, &amp;quot;priority&amp;quot;, &amp;quot;job_id&amp;quot; ], name: &amp;quot;index_solid_queue_poll_by_queue&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All that still wouldn&amp;#39;t be enough to achieve truly outstanding performance. Traditionally, queuing systems that rely on
polling tables have had a significant problem. One worker would block all others while querying and updating the polling
table.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take a look at why. Consider the following query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT id
  FROM jobs
  WHERE queue = &amp;quot;default&amp;quot;
  AND claimed = 0
  ORDER_BY priority, id
  LIMIT 2
  FOR UPDATE;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;FOR UPDATE&lt;/code&gt; statement locks the rows selected by the query. This is necessary to avoid nasty race conditions, such as
multiple workers grabbing the same job. But that also means that any worker running this query would block read access
to the table. Thus, other workers would have to wait for that query to finish. The polling table becomes a bottleneck
that hinders rapid job execution.&lt;/p&gt;
&lt;p&gt;Luckily, modern databases (PostgreSQL &amp;gt;= 9.5, MySQL &amp;gt;= 8.0) solve this problem. The &lt;code&gt;SKIP LOCKED&lt;/code&gt; statement allows the
database to lock only the records that are being updated. The rest of the table remains unlocked and free to be polled
concurrently.&lt;/p&gt;
&lt;p&gt;SQLite does not support &lt;code&gt;SKIP LOCKED&lt;/code&gt;, so worker processes must queue up. In most cases, this shouldn&amp;#39;t be an
issue. SQLite writes are fast as the database is present on disk. Even so, this is a limitation that you should
be aware of.&lt;/p&gt;
&lt;p&gt;Whether you&amp;#39;re using SQLite or another database, &lt;a href=&quot;https://www.appsignal.com/ruby/solid-queue-monitoring&quot;&gt;AppSignal provides Solid Queue performance monitoring&lt;/a&gt; out of the box! We&amp;#39;ll talk more about this in part two of this series.&lt;/p&gt;
&lt;h2&gt;Safety First&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve spent some time discussing &lt;code&gt;solid_queue_ready_executions&lt;/code&gt;, but another table is instrumental for
ensuring that Solid Queue functions reliably. A key requirement of any queuing system is that any job being
enqueued is executed at least once. In other words, jobs must never be lost—we already alluded to this in the
introduction.&lt;/p&gt;
&lt;p&gt;Without additional safety measures, this could quickly happen. Imagine that a worker starts working on a job and, in
doing so, updates the corresponding job record to claim it. Of course, this is necessary to avoid multiple workers
running a job simultaneously.&lt;/p&gt;
&lt;p&gt;Imagine that suddenly, this worker process dies without finishing execution. Your machine might crash, and the OS may kill the worker for consuming too much memory — accidents happen, you know. The job it claimed will remain stuck
forever because no other workers can grab it. Thus, it will never be executed, and your users will be sad and
angry. The end.&lt;/p&gt;
&lt;p&gt;That is, unless we add additional safety measures. Solid Queue solves this problem by introducing yet more tables —
&lt;code&gt;solid_queue_claimed_executions&lt;/code&gt; and &lt;code&gt;solid_queue_processes&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Schema[7.1].define(version: 1) do
  create_table &amp;quot;solid_queue_claimed_executions&amp;quot;, force: :cascade do |t|
    t.bigint &amp;quot;job_id&amp;quot;, null: false
    t.bigint &amp;quot;process_id&amp;quot;
    # ...
  end

  create_table &amp;quot;solid_queue_processes&amp;quot;, force: :cascade do |t|
    t.datetime &amp;quot;last_heartbeat_at&amp;quot;, null: false
    t.integer &amp;quot;pid&amp;quot;, null: false
    #  ...
  end
  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ve already mentioned &lt;code&gt;solid_queue_claimed_executions&lt;/code&gt;. Let&amp;#39;s look at what happens when a worker claims
a job. For one, it sets the &lt;code&gt;claimed&lt;/code&gt; flag in the &lt;code&gt;solid_queue_jobs&lt;/code&gt; table. Additionally, a record is created in
&lt;code&gt;solid_queue_claimed_executions&lt;/code&gt;. This record contains the &lt;code&gt;job_id&lt;/code&gt; of the job being claimed and the id of the worker
process that makes the claim.&lt;/p&gt;
&lt;p&gt;So, what is the &lt;code&gt;solid_queue_processes&lt;/code&gt; table good for? Any worker process will create and periodically update a record
in this table by setting &lt;code&gt;last_heartbeat_at&lt;/code&gt;. Of course, that alone wouldn&amp;#39;t solve our problem.&lt;/p&gt;
&lt;p&gt;We need another process to keep track of running processes: the so-called supervisor. This process runs in the background
and periodically checks &lt;code&gt;solid_queue_processes&lt;/code&gt;. A record with a &lt;code&gt;last_heartbeat_at&lt;/code&gt; older than a threshold — which
defaults to 5 minutes — indicates that the corresponding worker has met a tragic fate.&lt;/p&gt;
&lt;p&gt;If such a record is found, the supervisor jumps into action. First, it removes the record from &lt;code&gt;solid_queue_processes&lt;/code&gt;.
Then, it marks any jobs previously claimed by the now-deceased worker as up-for-grabs. Thus, other workers can claim
them, avoiding the stuck-job situation.&lt;/p&gt;
&lt;h2&gt;More to Discover in Solid Queue&lt;/h2&gt;
&lt;p&gt;In this post, we covered a fair bit of Solid Queue&amp;#39;s internals. We looked at its high-level architecture and how its most
essential feature—enqueuing and executing a job—works under the hood. We also learned the critical role of
&lt;code&gt;FOR UPDATE SKIP LOCKED&lt;/code&gt; in performance. Finally, we learned how the supervisor process helps avoid stuck
jobs.&lt;/p&gt;
&lt;p&gt;But there is more to discover. Solid Queue offers many more features we haven&amp;#39;t touched on, such as scheduling
recurring and sequential jobs. Stay tuned as we continue our deep dive in part two of this series.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Pre-build a Secure Authentication Layer with Authentication Zero for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/04/16/pre-build-a-secure-authentication-layer-with-authentication-zero-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2025/04/16/pre-build-a-secure-authentication-layer-with-authentication-zero-for-ruby-on-rails.html</id>
    <published>2025-04-16T00:00:00+00:00</published>
    <updated>2025-04-16T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s build a configurable generator that equips your Ruby on Rails app with an authentication scaffold.</summary>
    <content type="html">&lt;p&gt;Authentication is a critical element of any web application. The Ruby on Rails ecosystem has no shortage of solutions for this topic, as no authentication layer has been backed into the framework yet.&lt;/p&gt;
&lt;p&gt;Devise is a crowd favorite. Although it has ended up as the de-facto standard and sports many built-in features and great plugins (such as integrations with NoSQL databases, encryption, roles, and UID support), it works as a separate entity from your application.&lt;/p&gt;
&lt;p&gt;Some have devised a different solution: a configurable generator that equips your app with an authentication scaffold. We&amp;#39;ll dive into that option in this post.&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s examine the uses of Authentication Zero.&lt;/p&gt;
&lt;h2&gt;The Purpose of Authentication Zero&lt;/h2&gt;
&lt;p&gt;It&amp;#39;s best to avoid building authentication yourself, as there are noteworthy security concerns to consider when doing so. Authentication Zero&amp;#39;s (not to be confused with Auth0, the other, Ruby-friendly authentication solution) approach is to give you something that follows security and Ruby on Rails best practices. You are then free to adjust some parts to fit your needs.&lt;/p&gt;
&lt;p&gt;Yet, that also means that you now have two additional responsibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not to turn the provided code into a weak authentication system&lt;/li&gt;
&lt;li&gt;To keep it up to date&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Authentication in a web application is a way to ensure that someone coming back to your application is who they say they are and can access data related to themselves (in short). We usually use a secret linked to a user model or a third-party provider with or without a complimentary second-factor authentication.&lt;/p&gt;
&lt;p&gt;The simplest mechanism (a pair consisting of a user identifier and a password) implies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;User&lt;/code&gt; model with &lt;code&gt;username&lt;/code&gt; (or &lt;code&gt;email&lt;/code&gt;) and &lt;code&gt;password&lt;/code&gt; fields.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Users&lt;/code&gt; controller to handle the creation of a user account (with a page to enter details, and an action to create the user and then redirect them elsewhere).&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Sessions&lt;/code&gt; controller that shows a login form and handles both login and logout, verifying that the submitted credentials match an existing user.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don&amp;#39;t get fooled, though; these elements don&amp;#39;t just cover &amp;quot;a few lines&amp;quot;. Each of these three lines implies details related to security, error handling, and also, yes, a friendly User Interface. Authentication Zero gives you all this alongside plenty of advanced features such as email and password validation, email verification, API authentication, logs, rate limiting, lock mechanisms, and more.&lt;/p&gt;
&lt;h2&gt;Our Project&lt;/h2&gt;
&lt;p&gt;To demonstrate how Authentication Zero works and how to use it, we are going to integrate it into a sample Ruby on Rails application. We will start from a freshly created application and add the authentication layer with Authentication Zero. We will cover several of the key options we can use with the generator to go from very simple authentication to something using Two-Factor Authentication as well as logging, password-less authentication, and more.&lt;/p&gt;
&lt;p&gt;We will also compare Authentication Zero with other solutions — namely Devise and Auth0 — particularly in terms of their features and usage.&lt;/p&gt;
&lt;h2&gt;Setup for a Ruby on Rails 7.1 Application&lt;/h2&gt;
&lt;p&gt;Setting up from a barebone Ruby on Rails 7.1 application is pretty standard. Simply add the &lt;code&gt;authentication-zero&lt;/code&gt; gem to the Gemfile, run bundle, and then the generator:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails generate authentication
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s review the output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;gemfile  bcrypt (~&amp;gt; 3.1.7)
 create  db/migrate/20240512081634_create_users.rb
 create  db/migrate/20240512081635_create_sessions.rb
 create  app/models/current.rb
 create  app/models/session.rb
 create  app/models/user.rb
 create  test/fixtures/users.yml
 create  app/controllers/identity/email_verifications_controller.rb
 create  app/controllers/identity/emails_controller.rb
 create  app/controllers/identity/password_resets_controller.rb
  force  app/controllers/application_controller.rb
 create  app/controllers/home_controller.rb
 create  app/controllers/passwords_controller.rb
 create  app/controllers/registrations_controller.rb
 create  app/controllers/sessions_controller.rb
 create  app/views/home/index.html.erb
 create  app/views/identity/emails/edit.html.erb
 create  app/views/identity/password_resets/edit.html.erb
 create  app/views/identity/password_resets/new.html.erb
 create  app/views/passwords/edit.html.erb
 create  app/views/registrations/new.html.erb
 create  app/views/sessions/index.html.erb
 create  app/views/sessions/new.html.erb
 create  app/views/user_mailer/email_verification.html.erb
 create  app/views/user_mailer/password_reset.html.erb
 create  app/mailers/user_mailer.rb
 # routes (removed for clarity)
 # tests (removed for clarity)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The generator does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensures that the &lt;code&gt;bcrypt&lt;/code&gt; gem is installed.&lt;/li&gt;
&lt;li&gt;Adds &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Session&lt;/code&gt; models, plus a &lt;code&gt;Current&lt;/code&gt; utility class for tracking the signed‑in user.&lt;/li&gt;
&lt;li&gt;Adds controllers for sign‑up, authentication, password management, and sign‑in/out.&lt;/li&gt;
&lt;li&gt;Adds views where needed for those workflows.&lt;/li&gt;
&lt;li&gt;Adds a mailer for email verification and password‑reset emails.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just to let you know, we have not used additional parameters when running the generator. This is the most straightforward authentication architecture we can have, with just an email and password to authenticate a user. We will cover additional features later in the article.&lt;/p&gt;
&lt;p&gt;We already have a working sign-up and sign-in process with email‑based password resets. The implementation is what is essential here.&lt;/p&gt;
&lt;p&gt;First, the &lt;code&gt;ApplicationController&lt;/code&gt; has been updated. As it&amp;#39;s the base for any controller, it&amp;#39;s the first thing we must look at. It now contains two &lt;code&gt;before_action&lt;/code&gt; hooks and their methods. The first one (&lt;code&gt;set_current_request_details&lt;/code&gt;) heavily uses the &lt;code&gt;Current&lt;/code&gt; class to store the user agent and IP address of the current request. The other one is there to check if there is an existing session for the request and also uses the &lt;code&gt;Current&lt;/code&gt; class. If no session exists, the request is redirected to the login screen.&lt;/p&gt;
&lt;p&gt;So, when we try to get to &lt;code&gt;http://localhost:3000&lt;/code&gt;, we are redirected to the login screen. A thing to note: by default, any controller inheriting this one will try to authenticate the user through a session. So, if you do not need that in some controllers or actions, you will have to expressly declare an exception through &lt;code&gt;skip_before_action&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Current Class&lt;/h3&gt;
&lt;p&gt;Here, the first thing to spot is the reliance on &lt;code&gt;ActiveSupport::CurrentAttributes&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Current &amp;lt; ActiveSupport::CurrentAttributes
  attribute :session
  attribute :user_agent, :ip_address

  delegate :user, to: :session, allow_nil: true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is an:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Abstract super class that provides a thread-isolated attributes singleton, which resets automatically before and after each request. This allows you to keep all the per-request attributes easily available to the whole system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html&quot;&gt;Source: Ruby on Rails API docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Note the &lt;code&gt;delegate&lt;/code&gt; definition too: that will tie up nicely to the &lt;code&gt;Session&lt;/code&gt; model and its association to the &lt;code&gt;User&lt;/code&gt; one.&lt;/p&gt;
&lt;h3&gt;Session Model&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;Session&lt;/code&gt; model is a lovely way to handle many things related to a current request&amp;#39;s session and its associated data, including a user.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Session &amp;lt; ApplicationRecord
  belongs_to :user

  before_create do
    self.user_agent = Current.user_agent
    self.ip_address = Current.ip_address
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;User Model&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;User&lt;/code&gt; model is also fairly standard and uses plenty of good practices, from &lt;code&gt;has_secure_password&lt;/code&gt; to token generators, email, and password validations.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  has_secure_password

  # ...

  validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :password, allow_nil: true, length: { minimum: 12 }

  normalizes :email, with: -&amp;gt; { _1.strip.downcase }

  before_validation if: :email_changed?, on: :update do
    self.verified = false
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note how the email is validated, formatted properly, and the &lt;code&gt;before_validation&lt;/code&gt; hook used to ensure that the email is marked as not verified.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;  after_update if: :password_digest_previously_changed? do
    sessions.where.not(id: Current.session).delete_all
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Finally, note how sessions are destroyed whenever the password changes.&lt;/p&gt;
&lt;h3&gt;Sessions Controller&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;SessionsController&lt;/code&gt; is a fairly standard approach, reusing the &lt;code&gt;Current&lt;/code&gt; class to facilitate finding sessions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class SessionsController &amp;lt; ApplicationController
  # ...

  def index
    @sessions = Current.user.sessions.order(created_at: :desc)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See how readable this looks:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def create
  if user = User.authenticate_by(email: params[:email], password: params[:password])
    @session = user.sessions.create!
    cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }

    redirect_to root_path, notice: &amp;quot;Signed in successfully&amp;quot;
  else
    redirect_to sign_in_path(email_hint: params[:email]), alert: &amp;quot;That email or password is incorrect&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one makes great use of Ruby on Rails features to make the session creation very readable, while taking care of setting all the right information in the session (thanks to an Active Record association between the &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Session&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Next up, we&amp;#39;ll look at identity controllers.&lt;/p&gt;
&lt;h3&gt;Identity Controllers&lt;/h3&gt;
&lt;p&gt;Three controllers have been added in the &lt;code&gt;identity&lt;/code&gt; namespace. One handles both sending and verifying the verification email, one handles the password resets (also sending the email and managing the password update itself), and one holds the email address update for a user.&lt;/p&gt;
&lt;p&gt;That code is concise and clear, so it&amp;#39;s perfect to tweak for your needs. Remember to keep an eye on &lt;a href=&quot;https://github.com/lazaronixon/authentication-zero/blob/main/CHANGELOG.md&quot;&gt;Authentication Zero&amp;#39;s changelog&lt;/a&gt; for security issues and other significant updates. You are responsible for porting any security fixes or significant bug fixes to your version of the code generated two weeks, three months, or five years from now. And this is not just a matter of stapling Dependabot onto the repository: you might need to regenerate the code. Here&amp;#39;s a trick to avoid conflicts between your custom additions and the generated code: use your own modules alongside the generated code. By relying on this mechanism, you will keep the code clearer and limit any issues when updating it.&lt;/p&gt;
&lt;p&gt;The generated views are relatively simple and might not match the CSS engine you have selected (it didn&amp;#39;t match the Tailwind one at the time of this post). But that does not matter. The views are the visible side of the iceberg. You can use your favorite Tailwind or Bootstrap template on top of the generated controllers and reuse the blueprint of the generated views for guidance.&lt;/p&gt;
&lt;h2&gt;Beyond the Base Features&lt;/h2&gt;
&lt;p&gt;Now that we have seen a primary use case of Authentication Zero and how it works, we can check more advanced features. Here is a short list of a few that are particularly interesting.&lt;/p&gt;
&lt;h3&gt;Pwned&lt;/h3&gt;
&lt;p&gt;With so many database leaks and bad password strategies, more than simple validations might be needed. The &lt;code&gt;pwned&lt;/code&gt; gem allows you to check any password against the &lt;a href=&quot;https://haveibeenpwned.com/API/v3#PwnedPasswords&quot;&gt;Pwned Password API&lt;/a&gt;. Adding this to a Ruby on Rails application is simple (add the gem and a validator on the password field). To add it automatically to your application with Authentication Zero, just add the &lt;code&gt;--pwned&lt;/code&gt; flag when using the generator.&lt;/p&gt;
&lt;p&gt;This will generate a &lt;code&gt;User&lt;/code&gt; model with an additional validator using the Pwned gem:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;validates :password, not_pwned: { message: &amp;quot;might easily be guessed&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Two-factor Authentication&lt;/h3&gt;
&lt;p&gt;Two-factor authentication (2FA) is all the rage nowadays and should be mandatory for many applications. Authentication Zero proposes two ways to handle this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two-factor authentication (with an authenticator app) and recovery codes (&lt;code&gt;--two-factor&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Two-factor authentication using a hardware security key (&lt;code&gt;--webauthn&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both will generate additional code for the &lt;code&gt;User&lt;/code&gt; model, its migration, and controllers and views to handle the different factors.&lt;/p&gt;
&lt;h3&gt;Password Protect Significant Changes&lt;/h3&gt;
&lt;p&gt;In many applications, requesting an additional password check before accessing or changing data is legitimate. The generator&amp;#39;s &lt;code&gt;--sudo&lt;/code&gt; option integrates a &lt;code&gt;require_sudo&lt;/code&gt; before the action filter that you can use in controllers to enforce this requirement.&lt;/p&gt;
&lt;h3&gt;User Event Tracking&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--trackable&lt;/code&gt; option ensures that actions (such as logins and logouts) are traced so that you can display a log for security purposes.&lt;/p&gt;
&lt;p&gt;This feature relies on an &lt;code&gt;Event&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Event &amp;lt; ApplicationRecord
  belongs_to :user

  before_create do
    self.user_agent = Current.user_agent
    self.ip_address = Current.ip_address
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, this model is associated with a user and uses the &lt;code&gt;Current&lt;/code&gt; class (covered earlier in this post) through a &lt;code&gt;before_create&lt;/code&gt; hook. With just this and by relying on similar hooks in the &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Session&lt;/code&gt; model, events such as logins (when a &lt;code&gt;Session&lt;/code&gt; is created), logouts (when a &lt;code&gt;Session&lt;/code&gt; is destroyed), email verifications or changed passwords will be tracked in the database.&lt;/p&gt;
&lt;p&gt;The following are tracked:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User agent&lt;/li&gt;
&lt;li&gt;IP address&lt;/li&gt;
&lt;li&gt;Action&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;li&gt;User (since each event is attached to a user)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This feature is primarily for security purposes, providing an audit trail for critical actions related to the security or identity of a user account. The generator also generates a controller and view for a user to see the events. There is no integrated feature to purge it but it&amp;#39;d not be complicated to add one.&lt;/p&gt;
&lt;h3&gt;Password-less Authentication&lt;/h3&gt;
&lt;p&gt;Authentication Zero&amp;#39;s &lt;code&gt;--passwordless&lt;/code&gt; option allows your application to email a magic link to a user so they can sign in to a freshly prepared session.&lt;/p&gt;
&lt;h3&gt;&amp;quot;Sign-in as&amp;quot; Button&lt;/h3&gt;
&lt;p&gt;One feature that kept being requested was the ability to masquerade as a user to check how a product looks from their perspective or help them with specific updates. Authentication Zero supports this through its &lt;code&gt;--masqueradable&lt;/code&gt; option.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s finally take a quick look at how Authentication Zero compares to Devise and Auth0.&lt;/p&gt;
&lt;h2&gt;Authentication Zero Compared to Devise and Auth0&lt;/h2&gt;
&lt;p&gt;As mentioned in the introduction to this post, Devise is very popular in the Ruby on Rails ecosystem. Authentication Zero presents a significant interest to many: you get your authentication layer with the knowledge that it relies on good practices. Still, once generated and integrated into your code, any additions might need some complex gymnastics. Adding 2FA two years into a product&amp;#39;s lifetime might be tricky. With Devise and Omniauth, it&amp;#39;s mostly a matter of configuration.&lt;/p&gt;
&lt;p&gt;In Auth0, your application has a very different approach, with almost no authentication layer. Instead, all authentication is done by Auth0, a third-party authentication provider. Integrating key security features such as 2FA involves activating them in Auth0 settings.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Authentication Zero can be a great option for your Ruby application, but it comes with a large bag of homework for you and your team:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Customization of UI (granted, that&amp;#39;s also the case for Devise)&lt;/li&gt;
&lt;li&gt;Backporting any updates that would come later in Authentication Zero&lt;/li&gt;
&lt;li&gt;Manual integration of additional features once the code has been integrated&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When integrating such vital elements, measure your team&amp;#39;s needs and abilities. At the very least, Authentication Zero is an example of elegant Ruby and Ruby on Rails code. The fact that it integrates significant features of modern authentication layers (such as 2FA, passwordless, logs, sudo mode, and masquerading) makes it a promising solution for a team that doesn&amp;#39;t want to add complexity from a large library, such as Devise.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Advanced Queries in ActiveRecord for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2025/02/26/advanced-queries-in-activerecord-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2025/02/26/advanced-queries-in-activerecord-for-ruby-on-rails.html</id>
    <published>2025-02-26T00:00:00+00:00</published>
    <updated>2025-02-26T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s delve into advanced ActiveRecord queries, shining a spotlight on more complex joins, custom SQL, and strategic employment of database-specific features.</summary>
    <content type="html">&lt;p&gt;ActiveRecord: Rails&amp;#39; de facto ORM. We use it every day, and it makes life a lot easier; we type less, read less, have complexity hidden from us, and don&amp;#39;t have to specify all kinds of boilerplate we&amp;#39;d otherwise have to.&lt;/p&gt;
&lt;p&gt;But its most basic methods, easy-to-read symbol notation, and complete query abstraction aren&amp;#39;t always up to the task at hand. As our apps grow, more complex data models and relationships follow, and the limitations of basic querying methods become apparent. Simple, short, and readable queries that once worked well become insufficient and need not just modification but rewriting. This progression calls for a deeper understanding and utilization of ActiveRecord&amp;#39;s capabilities beyond the basic finders and associations. Often you need a combination of SQL and ActiveRecord to get the job done.&lt;/p&gt;
&lt;p&gt;Here, we&amp;#39;ll delve into advanced ActiveRecord queries, shining a spotlight on more complex joins, custom SQL, and strategic employment of database-specific features. This will help us better approach our data handling in Rails and meet the needs of our increasingly complex database relationships.&lt;/p&gt;
&lt;h2&gt;Complex Joins and Associations in ActiveRecord for Rails&lt;/h2&gt;
&lt;p&gt;What may begin as a simple one-to-one or one-to-many relationship can quickly expand into a labyrinth of interconnected models, necessitating a deeper dive into complex SQL joins to maintain efficient data retrieval and manipulation.&lt;/p&gt;
&lt;p&gt;ActiveRecord supports more complex joins out of the box; you can easily join multiple tables together using pure AR and symbol notation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Join our single item from an order to its order, the transaction, and the seller profile, as well as the purchaser (user),
# finding all purchased items by a particular user from a particular seller
  PurchasedItem.joins(order: [:user, transaction: :seller_profile])
               .where(
                 orders: {users: {id: user_id},
                 transactions: {seller_profiles: {id: seller_profile_id}}}
               )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is great and often suffices. But what happens the moment we realize we need some information from the transaction and the user too? We can&amp;#39;t just &lt;code&gt;.select(:id)&lt;/code&gt; and hope it&amp;#39;ll somehow magically know we want the &lt;code&gt;id&lt;/code&gt; of the &lt;code&gt;Transaction&lt;/code&gt; and not the &lt;code&gt;PurchasedItem&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is where we need ActiveRecord to leverage SQL strings.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;PurchaseItems.joins(...)
             .where(...)
             .select(&amp;#39;desired, purchased_item, columns&amp;#39;)
             .select(&amp;#39;transactions.id AS transaction_id, transaction.total AS total_order_amount,
                      users.name AS purchaser_name, users.email AS purchaser_email&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we&amp;#39;ve selected whatever columns we wanted from each &lt;code&gt;PurchasedItem&lt;/code&gt; (which is of course more efficient than simply selecting all of its columns, AR&amp;#39;s default behavior). We&amp;#39;ve fetched the transaction IDs and total amounts associated with each individual purchased item of a given order, as well as the user&amp;#39;s name and email.&lt;/p&gt;
&lt;p&gt;We also don&amp;#39;t always join exactly the way our models might expect, depending on our use case:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Here we join on the users table in order to retrieve not the purchaser, but our sales member who was responsible for the sale,
# as well as joining the order on some special identifier our company uses internally
Transaction.joins(&amp;#39;JOIN users AS sales_member ON sales_member.id = transactions.sales_member_id&amp;#39;)
           .joins(&amp;#39;JOIN orders ON orders.proprietary_internal_company_identifier = transactions.proprietary_internal_company_order_identifier&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, when discovering our team&amp;#39;s &lt;code&gt;sales_members&lt;/code&gt; via the &lt;code&gt;users&lt;/code&gt; table, sometimes a &lt;code&gt;User&lt;/code&gt; is more than just a &lt;code&gt;User&lt;/code&gt;. This leads us to our next topic: self-referential associations.&lt;/p&gt;
&lt;h2&gt;Self-Referential Associations&lt;/h2&gt;
&lt;p&gt;Self-referential associations define both sides of a relationship within the same model. This involves creating a join table that links two instances of a model to each other.&lt;/p&gt;
&lt;p&gt;For example, in an organizational context, where users might have &lt;code&gt;managers&lt;/code&gt; and &lt;code&gt;sales_members&lt;/code&gt;, the &lt;code&gt;User&lt;/code&gt; model can be configured to reflect these relationships:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  # Associates sales_members to a user where the user is the manager
  has_many :sales_members, class_name: &amp;quot;User&amp;quot;, foreign_key: &amp;quot;manager_id&amp;quot;
  # Links a user to their manager, also a User
  belongs_to :manager, class_name: &amp;quot;User&amp;quot;, optional: true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;This allows us to traverse the hierarchy in both directions, allowing for queries that can retrieve a user&amp;#39;s manager or their list of &lt;code&gt;sales_members&lt;/code&gt; (e.g., &lt;code&gt;user.manager&lt;/code&gt; or &lt;code&gt;manager.sales_members&lt;/code&gt;). It also greatly simplifies self joins, so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# return all users who have managers
User.joins(:manager)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-SQL&quot;&gt;SELECT users.* FROM users
INNER JOIN users ON users.id = users.manager_id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this relatively simple example, we&amp;#39;ve simply placed a &lt;code&gt;manager_id&lt;/code&gt; column on the &lt;code&gt;User&lt;/code&gt;, but there&amp;#39;s nothing stopping you from using join tables to create more complex many-to-many associations.&lt;/p&gt;
&lt;p&gt;That said, keep in mind that, while powerful and often necessary, self-referential joins can lead to more complex queries. This isn&amp;#39;t always avoidable, but if you don&amp;#39;t actually need them, creating another model might be a better choice. As always, balance needs with the potential cost.&lt;/p&gt;
&lt;h2&gt;Database-Specific Features in ActiveRecord for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re on Rails, there&amp;#39;s a pretty good chance you&amp;#39;re using PostgreSQL. ActiveRecord, together with PostgreSQL, &lt;a href=&quot;https://guides.rubyonrails.org/active_record_postgresql.html#json-and-jsonb&quot;&gt;supports column types like JSON, JSONb&lt;/a&gt;, and &lt;a href=&quot;https://guides.rubyonrails.org/active_record_postgresql.html#array&quot;&gt;arrays&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Do you have a particular model with many potentially optional columns? Or one that&amp;#39;s likely to grow, where you&amp;#39;ll need a lot of model-specific columns? This might be anything from user preferences, to video stats, to a literal need to just store pure JSON (like webhook responses).&lt;/p&gt;
&lt;p&gt;If so, JSON or JSONb might be a good choice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JSON vs. JSONb: Which is the right fit?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you need to query them often, you should probably go with JSONb as it has more advanced querying options, and supports indexing — specifically, &lt;a href=&quot;https://pganalyze.com/blog/gin-index&quot;&gt;GIN indexing&lt;/a&gt;. JSONb uses more space and is slightly slower to write, but is much more efficient to retrieve: the &amp;quot;b&amp;quot; in JSONb stands for &amp;quot;binary&amp;quot;. JSON is stored as a string, whereas JSONb is stored as binary, meaning it can be queried more efficiently, and doesn&amp;#39;t require parsing when retrieved.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s an example of setting up and querying JSONb:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# note you will likely want to set a default to an empty hash, so you don&amp;#39;t have to check if the value is NULL in order to attempt to access any keys
add_column :videos, :video_stats, :jsonb, default: {}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# using some ActiveRecord syntax:
Video.where(&amp;#39;video_stats @&amp;gt; ?&amp;#39;, {filename: &amp;quot;uploaded_video_1080p.mp4&amp;quot;}.to_json)

# alternatively, you might find a string more readable, or simply need it for more involved queries:
Video.where(&amp;quot;
  video_stats -&amp;gt;&amp;gt; &amp;#39;filename&amp;#39; LIKE &amp;#39;%mp4&amp;#39; AND
  (video_stats -&amp;gt;&amp;gt; &amp;#39;duration&amp;#39;)::float &amp;lt; 6.0 OR
  (video_stats -&amp;gt;&amp;gt; &amp;#39;processed&amp;#39;)::boolean = false&amp;quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see the appeal here: if you&amp;#39;re hosting or cataloging video, there are all kinds of stats and metadata (relevant probably only to the videos themselves) that you might want to keep track of, for everything from bitrate, to resolution, to compression rate, video and codec type, upload and processed dates, fidelity, filetype, and so on. JSON can be an attractive choice for a model that demands a lot of columns and that may continue to grow as your app does.&lt;/p&gt;
&lt;h2&gt;Bulk Updates with &lt;code&gt;update_all&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;While we might often retrieve many records at once, we often find ourselves updating records one by one. Sometimes, though, a group of records needs to be updated with the same value. Maybe we&amp;#39;ve just run a job that&amp;#39;s processed a bunch of records in some way, perhaps by making API calls to an external payment service for each.&lt;/p&gt;
&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; update them one at a time as we process them, but this puts unnecessary strain on our workers and database. Instead, we can simply use &lt;code&gt;update_all&lt;/code&gt;, which updates all records it&amp;#39;s called on with a given value per column:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# be sure to include `updated_at` if you&amp;#39;ve got timestamps and their value is important to you, as update_all doesn&amp;#39;t do so automatically
@processed_records.update_all(processed: true)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This achieves an atomic update of all records involved in a single transaction. Nice!&lt;/p&gt;
&lt;p&gt;We also have &lt;code&gt;insert_all&lt;/code&gt;, introduced a few years ago by Rails 6. You will likely not use this as often, but it&amp;#39;s still valuable, especially if you need to create a lot of records at once in real time on the main thread.&lt;/p&gt;
&lt;p&gt;Maybe you allow your clients to upload their large orders to you. After processing their CSV, you construct their order:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# insert_all accepts an array of hashes.
# if you&amp;#39;ve got timestamps on a model, you need to include them both, or it will fail
new_orders = [
  {name: &amp;quot;Item 1&amp;quot;, completed: false, created_at: Time.now, updated_at: Time.now},
  {name: &amp;quot;Item 2&amp;quot;, completed: false, created_at: Time.now, updated_at: Time.now},
  ...,
  {name: &amp;quot;Item 15922&amp;quot;, completed: false, created_at: Time.now, updated_at: Time.now},
]

Order.insert_all(new_orders)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Always consider whether or not you need callbacks or validations on the items you&amp;#39;re updating.&lt;/p&gt;
&lt;p&gt;Because these transactions are atomic, there&amp;#39;s no way to trigger callbacks. Sometimes that&amp;#39;s exactly what we want, which is perfect. If not, though, we might not be able to use &lt;code&gt;update_all&lt;/code&gt;. If you need a callback (or, more rarely for a bulk update, a validation) to do something specific for each individual record, we probably can&amp;#39;t.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say you&amp;#39;ve only got a callback to ensure some other part of our app knows that at least one record of a particular type has been updated (in order to trigger a recount). You can probably do something like &lt;code&gt;update_all&lt;/code&gt; on the first n-1 records, then &lt;code&gt;.update&lt;/code&gt; on the last. This not only allows us to update everything at once, but it also avoids triggering potentially expensive callbacks on every single item.&lt;/p&gt;
&lt;p&gt;Bonus: Got a mass-update scenario where you need to insert a lot of records at once? Check out &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-upsert_all&quot;&gt;&lt;code&gt;upsert_all&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;ActiveRecord for Ruby on Rails: Words of Caution&lt;/h2&gt;
&lt;p&gt;It goes without saying, but be mindful of potential &lt;a href=&quot;https://guides.rubyonrails.org/security.html#sql-injection&quot;&gt;SQL injection&lt;/a&gt;. Rails does a good job of protecting us from it most of the time, but it does give us enough rope to hang ourselves with. I personally like to often use single quotes for SQL strings to remind myself not to use string interpolation for things like params that users have control over.&lt;/p&gt;
&lt;p&gt;Be mindful that JSON/JSONb columns are a single column and not self-documenting like the rest of your schema. The keys you add to any given JSON column are arbitrary, and you will have to document them yourself if you need to keep track of them all without having to hunt around your code for clues, because they&amp;#39;re not going to show up in the schema.&lt;/p&gt;
&lt;p&gt;Alternatively, you can use ActiveRecord&amp;#39;s &lt;code&gt;store_accessor&lt;/code&gt;, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Video &amp;lt; ApplicationRecord
  store :video_stats, accessors: [:filename, :duration, :processed]
  #...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will allow you to both explicitly declare/document all the potential keys that exist in your JSON/JSONb column, and also access them directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;#  like this:
video.duration

# rather than this:
video.video_stats[&amp;quot;duration&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just be sure not to accidentally give any of the keys the same name as an existing column on the same model if you&amp;#39;re going with this route! You can have JSON keys with the same names as columns, but if you&amp;#39;re using this &lt;code&gt;store_accessor&lt;/code&gt; method, you&amp;#39;ll run into &lt;a href=&quot;https://en.wikipedia.org/wiki/Name_collision&quot;&gt;name collisions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lastly, as mentioned, always make sure that if you&amp;#39;re using &lt;code&gt;update_all&lt;/code&gt;, &lt;code&gt;insert_all&lt;/code&gt;, or &lt;code&gt;upsert_all&lt;/code&gt;, you either don&amp;#39;t need any callbacks or validations, or that those requirements are met after the fact. In some cases, this could be as simple as firing off a job after you&amp;#39;ve updated or created these records. In other cases, there might not be an easy path.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Leveraging ActiveRecord&amp;#39;s advanced features can help make our Rails apps more flexible, efficient, and maintainable. But as with anything, good things are rarely free; keep in mind that all things being equal, simpler is better. If you can accomplish the same task more simply, you probably should. It&amp;#39;s always important to balance complexity with simplicity.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s always more to learn when it comes to Rails, and ActiveRecord is no exception. The more tools you add to your arsenal, the more effective you&amp;#39;ll be, and the better your applications will run.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>AppSignal’s Top 5 Ruby Posts in 2024</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/12/17/appsignals-top-5-ruby-posts-in-2024.html"/>
    <id>https://blog.appsignal.com/2024/12/17/appsignals-top-5-ruby-posts-in-2024.html</id>
    <published>2024-12-17T00:00:00+00:00</published>
    <updated>2024-12-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s look back at our top 5 Ruby posts this year.</summary>
    <content type="html">&lt;p&gt;As the year draws to an end, we&amp;#39;re excited to share our top five most-read Ruby articles of 2024, in reverse order:&lt;/p&gt;
&lt;h2&gt;Top 5 Ruby Blog Posts in 2024 💎&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2024/07/24/whats-coming-in-ruby-on-rails-7-2-database-features-in-active-record.html&quot;&gt;What&amp;#39;s Coming in Ruby on Rails 7.2: Database Features in Active Record&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Taking a look at some noteworthy changes in Ruby on Rails 7.2, particularly in Active Record.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2024/02/21/hotwire-modals-in-ruby-on-rails-with-stimulus-and-turbo-frames.html&quot;&gt;Hotwire Modals in Ruby on Rails with Stimulus and Turbo Frames&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the first part of this series, we explore two Hotwire methods to make modals accessible in your Rails application.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2024/04/24/should-you-use-ruby-on-rails-or-hanami.html&quot;&gt;Should You Use Ruby on Rails or Hanami?&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We updated this smash post from 2023 to clarify some differences in models and data persistence between Rails and Hanami.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2024/05/22/five-things-to-avoid-in-ruby.html&quot;&gt;Five Things to Avoid in Ruby&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this post, Martin ran through five common Ruby mistakes and how to combat them.&lt;/p&gt;
&lt;p&gt;Finally, last but certainly not least is our top-performing post of the &lt;strong&gt;year&lt;/strong&gt; (not just in Ruby), with over 30k views to date:&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2024/10/07/whats-new-in-ruby-on-rails-8.html&quot;&gt;What&amp;#39;s New in Ruby on Rails 8&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Exploring everything that Rails 8 has to offer.&lt;/p&gt;
&lt;h2&gt;Season&amp;#39;s Greetings ❆ ⛄&lt;/h2&gt;
&lt;p&gt;Have a wonderful festive break, and we&amp;#39;ll see you in the new year!&lt;/p&gt;
&lt;p&gt;If you haven’t already, don’t forget to &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter&lt;/a&gt;, so you never miss an upcoming post.&lt;/p&gt;
&lt;p&gt;See you in the new year!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Server-sent Events and WebSockets in Rack for Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/11/27/server-sent-events-and-websockets-in-rack-for-ruby.html"/>
    <id>https://blog.appsignal.com/2024/11/27/server-sent-events-and-websockets-in-rack-for-ruby.html</id>
    <published>2024-11-27T00:00:00+00:00</published>
    <updated>2024-11-27T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the final part of our three-part series, we&#039;ll use server-sent events (SSEs) and WebSockets to establish a persistent connection in a Rack app.</summary>
    <content type="html">&lt;p&gt;In the previous part of this series, we discovered how to create persistent connections in Rack in theory, but now we&amp;#39;ll put what we learned into practice.&lt;/p&gt;
&lt;p&gt;The web has two formalized specifications for communication over a persistent connection: server-sent events (SSEs) and WebSockets.&lt;/p&gt;
&lt;p&gt;WebSockets are widely used and highly popular, but SSEs are far less well-known. Let&amp;#39;s explore them first.&lt;/p&gt;
&lt;h2&gt;Server-sent Events&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events&quot;&gt;Server-sent events (SSEs)&lt;/a&gt; enable a client to hold an open connection with the server, but only the server can publish messages to the client. It isn&amp;#39;t a bi-directional protocol.&lt;/p&gt;
&lt;p&gt;SSEs are a JavaScript API, so let&amp;#39;s modify our app to serve an HTML page with the required script:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    req = Rack::Request.new(env)
    path = req.path_info

    case path
    when &amp;quot;/&amp;quot;
      sse_js(env)
    end
  end

  private

    def sse_js(env)
      body = &amp;lt;&amp;lt;~HTML
        &amp;lt;!DOCTYPE html&amp;gt;
        &amp;lt;html&amp;gt;
          &amp;lt;head&amp;gt;
            &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;
            &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot;&amp;gt;
            &amp;lt;title&amp;gt;SSE - Demo&amp;lt;/title&amp;gt;

            &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
              const eventSource = new EventSource(&amp;quot;/sse&amp;quot;)
              eventSource.addEventListener(&amp;quot;message&amp;quot;, event =&amp;gt; {
                document.body.insertAdjacentHTML(
                  &amp;quot;beforeend&amp;quot;,
                  `&amp;lt;p&amp;gt;${event.data}&amp;lt;/p&amp;gt;`
                )
              })
            &amp;lt;/script&amp;gt;
          &amp;lt;/head&amp;gt;
          &amp;lt;body&amp;gt;
          &amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
      HTML

      [200, { &amp;quot;content-type&amp;quot; =&amp;gt; &amp;quot;text/html&amp;quot; }, [body]]
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The API is encapsulated in the &lt;code&gt;EventSource&lt;/code&gt; class and new messages from the server trigger events that we listen for. Next, we need to build the endpoint that sends the events:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    req = Rack::Request.new(env)
    path = req.path_info

    case path
    when &amp;quot;/&amp;quot;
      sse_js(env)
    when &amp;quot;/sse&amp;quot;
      sse(env)
    end
  end

  private

    def sse_js(env)
        # ...
    end

    def sse(env)
      body = proc do |stream|
        Thread.new do
          5.times do
            stream.write &amp;quot;data: #{Time.now}!\n\n&amp;quot;
            sleep 1
          end
        ensure
          stream.close
        end
      end

      [200, { &amp;quot;content-type&amp;quot; =&amp;gt; &amp;quot;text/event-stream&amp;quot; }, body]
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From a server point of view, this is fairly similar to the streaming bodies example we used in the previous part of this series. It&amp;#39;s worth noting the &lt;code&gt;content-type&lt;/code&gt; header and the string format written back to the client.&lt;/p&gt;
&lt;p&gt;Run the server (make sure you switch back to Puma):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle exec puma
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open up &lt;code&gt;localhost:9292&lt;/code&gt; on your web browser, and you&amp;#39;ll see the time written to the document five times at one-second intervals.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;This technique is great when the server just needs to notify the client about updates. The above example is fairly contrived, though, as it uses a loop, so let&amp;#39;s look at how we can use this technique in a real application.&lt;/p&gt;
&lt;h2&gt;Ruby &lt;code&gt;Queue&lt;/code&gt;s&lt;/h2&gt;
&lt;p&gt;Ruby provides a &lt;code&gt;Queue&lt;/code&gt; data structure for communication between threads. We can use that to &lt;em&gt;publish&lt;/em&gt; data back to a client. Let&amp;#39;s stick with the same use case of publishing the current time five times at one-second intervals, but now we&amp;#39;ll publish from a background thread.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
      # ...
  end

  private

    def sse_js(env)
        # ...
    end

    def sse(env)
      queue = Thread::Queue.new
      trigger_background_loop(queue)

      body = proc do |stream|
        Thread.new do
          loop do
            data = queue.pop
            stream.write &amp;quot;data: #{data}!\n\n&amp;quot;
          end
        ensure
          stream.close
        end
      end

      [200, { &amp;quot;content-type&amp;quot; =&amp;gt; &amp;quot;text/event-stream&amp;quot; }, body]
    end

    def trigger_background_loop(queue)
      Thread.new do
        5.times do
          queue.push(Time.now)
          sleep 1
        end
      end
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example, we spawn another background thread to push the current time to the queue every second. In the SSE thread, we call &lt;code&gt;queue.pop&lt;/code&gt;, which blocks until something is added to the queue.&lt;/p&gt;
&lt;p&gt;Using this technique, we can use a pub/sub system such as Redis to add data to the &lt;code&gt;queue&lt;/code&gt; from a background thread, which is then published to the client.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s SSEs covered! Next, let&amp;#39;s look at WebSockets.&lt;/p&gt;
&lt;h2&gt;WebSockets&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API&quot;&gt;WebSockets&lt;/a&gt; are a bi-directional, full-duplex communication protocol that supports both binary and text data for client-server communication. They&amp;#39;re widely used in the modern web and underpin Rails&amp;#39; Action Cable framework.&lt;/p&gt;
&lt;p&gt;A WebSocket is created using an HTTP connection, but as a protocol, it&amp;#39;s completely independent of HTTP.&lt;/p&gt;
&lt;p&gt;To create a WebSocket connection, the client must make an HTTP request with these headers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Connection: Upgrade
Upgrade: websocket
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server will respond with the status &lt;code&gt;101&lt;/code&gt;, meaning &lt;code&gt;Switching Protocols&lt;/code&gt;. The TCP connection used for the HTTP request is upgraded to a WebSocket connection.&lt;/p&gt;
&lt;p&gt;We won&amp;#39;t get into the nitty-gritty of the WebSocket protocol in this post. It&amp;#39;s fairly fiddly since it&amp;#39;s a binary protocol. If you&amp;#39;re curious, &lt;a href=&quot;https://www.honeybadger.io/blog/building-a-simple-websockets-server-from-scratch-in-ruby/&quot;&gt;Starr Horne has written an amazing article on WebSockets&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at how to upgrade a TCP socket to a WebSocket connection.&lt;/p&gt;
&lt;h3&gt;Upgrading from HTTP to WebSockets&lt;/h3&gt;
&lt;p&gt;As described above, we&amp;#39;ll need to send a &lt;code&gt;101&lt;/code&gt; response. After this, we&amp;#39;ll write to the socket using WebSockets&amp;#39; binary protocol for the communication to work.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;digest/sha1&amp;#39;

class App
  def call(env)
    req = Rack::Request.new(env)

    key = req.get_header(&amp;quot;HTTP_SEC_WEBSOCKET_KEY&amp;quot;)
    response_key = Digest::SHA1.base64digest([key, &amp;quot;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&amp;quot;].join)

    body = proc do |stream|
      response = &amp;quot;Hello world!&amp;quot;
      output = [0b10000001, response.size, response]
      stream.write output.pack(&amp;quot;CCA#{ response.size }&amp;quot;)
    ensure
      stream.close
    end

    [101, { &amp;quot;Upgrade&amp;quot; =&amp;gt; &amp;quot;websocket&amp;quot;, &amp;quot;Connection&amp;quot; =&amp;gt; &amp;#39;upgrade&amp;#39;, &amp;quot;Sec-WebSocket-Accept&amp;quot; =&amp;gt; response_key }, body]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have to create a response key to securely create the connection. The UUID used to generate it is a global constant found in the specification. We won&amp;#39;t go into the binary format of the string we&amp;#39;re writing into the WebSocket connection, but it&amp;#39;s all described in &lt;a href=&quot;https://www.honeybadger.io/blog/building-a-simple-websockets-server-from-scratch-in-ruby/&quot;&gt;&amp;#39;Building a simple websockets server from scratch in Ruby&amp;#39; by Starr Horne&lt;/a&gt; if you&amp;#39;re curious.&lt;/p&gt;
&lt;h3&gt;Demo&lt;/h3&gt;
&lt;p&gt;Run the server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle exec puma
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The easiest way to create a connection is to use a WebSocket client. I recommend &lt;a href=&quot;https://github.com/vi/websocat&quot;&gt;&lt;code&gt;websocat&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ websocat ws://127.0.0.1:9292/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&amp;#39;ll see the string &lt;code&gt;Hello world!&lt;/code&gt; printed out! The connection is now active. In theory, we can write and receive messages over this socket now. We still need to implement receiving or publishing messages on the server for it to work in practice, but that&amp;#39;s for another post.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A final word of warning: always remember that persistent connections come with challenges when using a threaded web server like Puma. A persistent connection ties up a thread and can cause significant performance issues unless you open a sizeable can of worms to implement your own threading mechanism.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;That concludes our three-part deep dive into Rack! We first looked at how to set up a basic Rack app, before diving into socket hijacking for persistent connections.&lt;/p&gt;
&lt;p&gt;Lastly, in this part, we used two specifications provided by our web platform to communicate over persistent connections: server-sent events (SSEs) and WebSockets.&lt;/p&gt;
&lt;p&gt;I hope you&amp;#39;ve found this series useful. Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Rack for Ruby: Socket Hijacking</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/11/20/rack-for-ruby-socket-hijacking.html"/>
    <id>https://blog.appsignal.com/2024/11/20/rack-for-ruby-socket-hijacking.html</id>
    <published>2024-11-20T00:00:00+00:00</published>
    <updated>2024-11-20T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Rack is the foundation for every popular Ruby web framework in existence. In part two of this three-part series, we&#039;ll run through socket hijacking.</summary>
    <content type="html">&lt;p&gt;In the first part of this series, we set up a basic Rack app, learned how to process a request and send a response.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll take over connections from Rack and hold persistent connections to enable pathways such as WebSockets.&lt;/p&gt;
&lt;p&gt;First, though, let&amp;#39;s look at how an HTTP connection actually works.&lt;/p&gt;
&lt;h2&gt;HTTP Connections&lt;/h2&gt;
&lt;p&gt;As this diagram shows, a TCP socket is opened, and a request is sent to a server. The server responds and closes the connection. All communication is in plain text.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-11/http-seq.png&quot; alt=&quot;HTTP sequence diagram&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Using a technique called socket hijacking, we can take control of a socket from Rack when a request comes in. Rack offers two techniques for socket hijacking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rack/rack/blob/main/SPEC.rdoc#partial-hijack-&quot;&gt;&lt;strong&gt;Partial hijack&lt;/strong&gt;&lt;/a&gt;: Rack sends the HTTP response headers and hands over the connection to the application.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rack/rack/blob/main/SPEC.rdoc#full-hijack-&quot;&gt;&lt;strong&gt;Full hijack&lt;/strong&gt;&lt;/a&gt;: Rack simply hands over the connection to the client without writing anything to the socket.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Partial Hijacking&lt;/h2&gt;
&lt;p&gt;This is how you do a partial hijack:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    body = proc do |stream|
      5.times do
        stream.write &amp;quot;#{Time.now}\n\n&amp;quot;
        sleep 1
      end
    ensure
      stream.close
    end

    [200, { &amp;quot;content-type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot;, &amp;quot;rack.hijack&amp;quot; =&amp;gt; body }, []]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rack.hijack&lt;/code&gt; is a &lt;em&gt;Rack header&lt;/em&gt;, set in the same Hash as the HTTP response headers. Rack will look for such headers and process them as per the specification, instead of writing them to the HTTP response.&lt;/p&gt;
&lt;p&gt;Run the above app and &lt;code&gt;curl&lt;/code&gt; to it. You&amp;#39;ll see that it writes the time at one-second intervals.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ curl -i localhost:9292
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Full Hijacking&lt;/h2&gt;
&lt;p&gt;This is how you&amp;#39;d do a full hijack:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    headers = [
      &amp;quot;HTTP/1.1 200 OK&amp;quot;,
      &amp;quot;Content-Type: text/plain&amp;quot;
    ]

    stream = env[&amp;quot;rack.hijack&amp;quot;].call
    stream.write(headers.map { |header| header + &amp;quot;\r\n&amp;quot; }.join)
    stream.write(&amp;quot;\r\n&amp;quot;)
    stream.flush

    begin
      5.times do
        stream.write &amp;quot;#{Time.now}\n\n&amp;quot;
        sleep 1
      end
    ensure
      stream.close
    end

    [-1, {}, []]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, we call the proc passed to us using the &lt;code&gt;rack.hijack&lt;/code&gt; key, instead of setting one ourselves in the response. This gives us complete control over the socket. At the end, we return an array with the status &lt;code&gt;-1&lt;/code&gt; only because Rack expects an array to be returned. The contents of this array are ignored since we&amp;#39;ve taken over the socket.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;This is a bad practice, rife with gotchas and weird behavior. Don&amp;#39;t do it. Samuel Williams, who is a maintainer of Rack, &lt;a href=&quot;https://github.com/rack/rack/discussions/2162#discussioncomment-8698730&quot;&gt;recommends against it as well&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Streaming Bodies in Rack for Ruby&lt;/h2&gt;
&lt;p&gt;While full hijacking is a terrible idea, partial hijacking is a useful tool. But it still feels hacky, so Rack 3 formally adopted that approach into the spec by introducing the concept of &lt;em&gt;streaming bodies&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    body = proc do |stream|
      5.times do
        stream.write &amp;quot;#{Time.now}\n\n&amp;quot;
        sleep 1
      end
    ensure
      stream.close
    end

    [200, { &amp;quot;content-type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot; }, body]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we provide a block as the response body rather than an array. Rack keeps the connection open until the block finishes executing.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s a huge gotcha here when using Puma. Puma is a multi-threaded server that assigns a thread to each incoming request. We&amp;#39;re taking over the socket from Rack, but we&amp;#39;re still tying up a Puma thread as long as the connection is open.&lt;/p&gt;
&lt;p&gt;Puma concurrency can be configured, but threads are limited, and tying one up for long periods is not a good idea. Let&amp;#39;s see this in action first.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle exec puma -w 1 -t 1:1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In two separate terminal windows, run the following command at the same time:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ curl localhost:9292
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One request is immediately served, but the other is held until the first one completes. This is because we started Puma with a single worker and single thread, meaning it can only serve a single request at a time.&lt;/p&gt;
&lt;p&gt;We can get around this by creating our own thread.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    body = proc do |stream|
      Thread.new do
        5.times do
          stream.write &amp;quot;#{Time.now}\n\n&amp;quot;
          sleep 1
        end
      ensure
        stream.close
      end
    end

    [200, { &amp;quot;content-type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot; }, body]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now if you try the above experiment again, you&amp;#39;ll see both &lt;code&gt;curl&lt;/code&gt; requests are served concurrently because they don&amp;#39;t tie up a Puma thread.&lt;/p&gt;
&lt;p&gt;Once again, I must warn against this approach, unless you know what you&amp;#39;re doing. These demonstrations are largely academic, as systems programming is a deep and complex topic.&lt;/p&gt;
&lt;h2&gt;Falcon Web Server&lt;/h2&gt;
&lt;p&gt;Since the threading problem is specific to the Puma web server, let&amp;#39;s look at another option: Falcon. This is a new, highly concurrent Rack-compliant web server built on the &lt;a href=&quot;https://github.com/socketry/async&quot;&gt;&lt;code&gt;async&lt;/code&gt; gem&lt;/a&gt;. It uses Ruby Fibers instead of Threads, which are cheaper to create and have much lower overhead.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;async&lt;/code&gt; gem hooks into all Ruby I/O and other waiting operations, such as &lt;code&gt;sleep&lt;/code&gt;, and uses these to switch between different Fibers (ensuring a program is never held up doing nothing).&lt;/p&gt;
&lt;p&gt;Revert your app to the previous version where we&amp;#39;re not spawning a new thread:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    body = proc do |stream|
      5.times do
        stream.write &amp;quot;#{Time.now}\n\n&amp;quot;
        sleep 1
      end
    ensure
      stream.close
    end

    [200, { &amp;quot;content-type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot; }, body]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then remove Puma and install Falcon.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle remove puma
$ bundle add falcon
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the Falcon server. We need to explicitly bind it because it only serves &lt;code&gt;https&lt;/code&gt; traffic by default.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle exec falcon serve -n 1 -b http://localhost:9292
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server only uses a single thread, which you can confirm with the command below. You&amp;#39;ll need to grab your specific &lt;code&gt;pid&lt;/code&gt; from Falcon&amp;#39;s logs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ top -pid &amp;lt;pid&amp;gt; -stats pid,th
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The thread count printed by the above command will be &lt;code&gt;2&lt;/code&gt; because the MRI uses a thread internally.&lt;/p&gt;
&lt;p&gt;Try the earlier experiment again and run two &lt;code&gt;curl&lt;/code&gt; requests simultaneously.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ curl localhost:9292
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&amp;#39;ll see they&amp;#39;re both served at the same time, thanks to Ruby Fibers!&lt;/p&gt;
&lt;p&gt;Falcon is relatively new. Ruby Fibers were only introduced in Ruby 3.0. Since Falcon is Rack-compliant, it can be used with Rails too, but the docs recommend using it with v7.1 or newer only. As such, it&amp;#39;s a bit risky to use Falcon in production but it&amp;#39;s a very exciting development in the Ruby world, in my opinion. I can&amp;#39;t wait to see its progress in the next few years.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve now learned how to create persistent connections in Rack and how to run them without blocking other requests, but the use cases so far have been academic and contrived. In the next and final part of this series, we&amp;#39;ll examine how we can use this technique in a practical way.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The Basics of Rack for Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/10/30/the-basics-of-rack-for-ruby.html"/>
    <id>https://blog.appsignal.com/2024/10/30/the-basics-of-rack-for-ruby.html</id>
    <published>2024-10-30T00:00:00+00:00</published>
    <updated>2024-10-30T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Rack is the foundation for every popular Ruby web framework in existence. In the first part of a three-part series, let&#039;s set up a Rack app.</summary>
    <content type="html">&lt;p&gt;Rack is the foundation for every popular Ruby web framework in existence. It standardizes an interface between a Ruby application and a web server. This mechanism allows us to pair any Rack-compliant web server (such as Puma, Unicorn, or Falcon) with any Rack-compliant web framework (like Rails, Sinatra, Roda, or Hanami).&lt;/p&gt;
&lt;p&gt;Separating the concerns like this is immensely powerful and provides a lot of flexibility. It does, however, also come with limitations.&lt;/p&gt;
&lt;p&gt;Rack 2 operated on the assumption that every request must provide a response and close the connection. It made no facility for persistent connections to enable pathways like WebSockets.&lt;/p&gt;
&lt;p&gt;Developers had to make use of a hacky escape hatch to take over connections from Rack to implement WebSockets or similar persistent connections.&lt;/p&gt;
&lt;p&gt;This all changed with Rack 3. But first, let&amp;#39;s backtrack and take a closer look at Rack itself.&lt;/p&gt;
&lt;h2&gt;A Barebones Rack App&lt;/h2&gt;
&lt;p&gt;A basic Rack app looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    [200, { &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot; }, [&amp;quot;Hello World&amp;quot;]]
  end
end

run App.new
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rack/rack/blob/main/SPEC.rdoc#label-The+Environment&quot;&gt;&lt;code&gt;env&lt;/code&gt;&lt;/a&gt; is a hash containing request-specific information such as HTTP headers. When a request is made, the &lt;code&gt;call&lt;/code&gt; method is called, and we return an array representing the response.&lt;/p&gt;
&lt;p&gt;The first element is the HTTP response code, in this case &lt;code&gt;200&lt;/code&gt;. The second element is a hash containing any &lt;a href=&quot;https://github.com/rack/rack/blob/main/SPEC.rdoc#label-The+Headers&quot;&gt;Rack and HTTP response headers&lt;/a&gt; we wish to send. Finally, the last element is an array of strings representing the response body.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s organize this app into a folder and run it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ mkdir rack-demo
$ cd rack-demo
$ bundle init
$ bundle add rack rackup
$ touch app.rb
$ touch config.ru
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Fill in &lt;code&gt;app.rb&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class App
  def call(env)
    [200, { &amp;quot;content-type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot; }, [&amp;quot;Hello World&amp;quot;]]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;config.ru&lt;/code&gt; with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require_relative &amp;quot;app&amp;quot;

run App.new
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can run this app using the default WEBrick server by running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ bundle exec rackup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server will run on port &lt;code&gt;9292&lt;/code&gt;. We can verify this with a &lt;code&gt;curl&lt;/code&gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ curl localhost:9292
Hello World
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s got the basic app running!&lt;/p&gt;
&lt;h2&gt;Changing Web Servers&lt;/h2&gt;
&lt;p&gt;WEBrick is a development-only server, so let&amp;#39;s swap it out for Puma:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ bundle add puma
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now try running &lt;code&gt;rackup&lt;/code&gt; again. You&amp;#39;ll see it has automatically detected Puma in the bundle and started that instead of WEBrick!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ bundle exec rackup
Puma starting in single mode...
* Puma version: 6.4.2 (ruby 3.2.2-p53) (&amp;quot;The Eagle of Durango&amp;quot;)
*  Min threads: 0
*  Max threads: 5
*  Environment: development
*          PID: 45877
* Listening on http://127.0.0.1:9292
* Listening on http://[::1]:9292
Use Ctrl-C to stop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I recommend starting Puma directly instead of using &lt;code&gt;rackup&lt;/code&gt;, as that allows us to pass configuration arguments should we want to. The &lt;code&gt;-w 4&lt;/code&gt; below starts Puma using 4 workers, meaning 4 instances of Puma are started up simultaneously.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ bundle exec puma -w 4
[45968] Puma starting in cluster mode...
[45968] * Puma version: 6.4.2 (ruby 3.2.2-p53) (&amp;quot;The Eagle of Durango&amp;quot;)
[45968] *  Min threads: 0
[45968] *  Max threads: 5
[45968] *  Environment: development
[45968] *   Master PID: 45968
[45968] *      Workers: 4
[45968] *     Restarts: (✔) hot (✔) phased
[45968] * Listening on http://0.0.0.0:9292
[45968] Use Ctrl-C to stop
[45968] - Worker 0 (PID: 45981) booted in 0.0s, phase: 0
[45968] - Worker 1 (PID: 45982) booted in 0.0s, phase: 0
[45968] - Worker 2 (PID: 45983) booted in 0.0s, phase: 0
[45968] - Worker 3 (PID: 45984) booted in 0.0s, phase: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This basic app demonstrates the Rack interface. An incoming HTTP request is parsed into the &lt;code&gt;env&lt;/code&gt; hash and provided to the application. The application processes the request and supplies an array as the response that the server formats and sends to the client.&lt;/p&gt;
&lt;h2&gt;Rack Compliance In Frameworks&lt;/h2&gt;
&lt;p&gt;Every compliant web framework follows the Rack spec under the hood and provides an access point to go down to this level.&lt;/p&gt;
&lt;p&gt;In Rails, we can send a Rack response in a controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class HomeController
  def index
    self.response = [200, {}, [&amp;quot;I&amp;#39;m Home!&amp;quot;]]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly, in Roda:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;route do |r|
  r.on &amp;quot;home&amp;quot; do
    r.halt [200, {}, [&amp;quot;I&amp;#39;m Home!&amp;quot;]]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every Rack-compliant framework will have a slightly different syntax for accomplishing this, but since they&amp;#39;re all sending Rack responses under the hood, they will have an API for you to access that response.&lt;/p&gt;
&lt;p&gt;You can find the full, relatively accessible &lt;a href=&quot;https://github.com/rack/rack/blob/main/SPEC.rdoc&quot;&gt;Rack specification on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;As this demo shows, Rack operates under the assumption that a request comes in, is processed by a web application, and a response is sent back. Throwing persistent connections into the mix totally breaks this model, yet Rack-compliant frameworks like Rails implement WebSockets.&lt;/p&gt;
&lt;p&gt;In the next post, part two of a three-part series, we&amp;#39;ll cover how to take over connections from Rack so we can hold persistent connections to enable pathways such as WebSockets.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Optimize Database Performance in Ruby on Rails and ActiveRecord</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/10/30/optimize-database-performance-in-ruby-on-rails-and-activerecord.html"/>
    <id>https://blog.appsignal.com/2024/10/30/optimize-database-performance-in-ruby-on-rails-and-activerecord.html</id>
    <published>2024-10-30T00:00:00+00:00</published>
    <updated>2024-10-30T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s dive into some strategies to optimize database performance in Rails and ActiveRecord.</summary>
    <content type="html">&lt;p&gt;In Rails, we&amp;#39;re more likely to use SQL databases than other frameworks. Unlike NoSQL databases, which can be scaled horizontally with relative ease, SQL databases like PostgreSQL or MySQL are much less amenable to easy scaling.&lt;/p&gt;
&lt;p&gt;As a result, our database usually becomes the primary bottleneck as our business grows. Although SQL databases are very efficient, as our growing customer base puts an increasing load on our servers, we begin scaling our instance counts, workers, etc. But we can&amp;#39;t just make copies of our database for each new server we spin up. This makes optimizing database performance critical for any serious Rails project.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll explore strategies for optimizing performance to minimize the load on our database. We&amp;#39;ll start with some more basic topics like eager loading and the N+1 query problem, database indexing, &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;pluck&lt;/code&gt;, and immediate loading. Then, we&amp;#39;ll move on to more involved topics like performance profiling, scaling via techniques like database sharding, and background jobs using read replicas.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;Getting ActiveRecord N+1s Out of the Way&lt;/h2&gt;
&lt;p&gt;No discussion of database performance involving ActiveRecord (or any ORM) is complete without addressing the &lt;a href=&quot;https://blog.appsignal.com/2018/04/24/active-record-performance-the-n-1-queries-antipattern.html&quot;&gt;infamous N+1 problem&lt;/a&gt;. While most readers are likely familiar with it, it&amp;#39;s still worth revisiting, as N+1 queries can (and do) creep into our projects over time, especially as our codebase evolves and we add or update features.&lt;/p&gt;
&lt;p&gt;The N+1 problem generally occurs when iterating through a (potentially large) group of retrieved records. Because we haven&amp;#39;t loaded their association/s, each loop means an additional query for each associated model we access inside the loop. This can quickly spiral out of control, resulting in a staggering number of queries and causing serious consequences such as crashed pages, an exhausted database connection pool, and memory running out, ultimately grinding our site to a halt.&lt;/p&gt;
&lt;p&gt;There are a few useful tools at your disposal to help identify and resolve N+1 problems. The first is simply looking at your server output; generally, this works pretty well, as N+1s are easy to spot. For a more assisted approach, the &lt;a href=&quot;https://github.com/flyerhzm/bullet&quot;&gt;&lt;code&gt;Bullet&lt;/code&gt;&lt;/a&gt; gem is a popular tool that automatically detects N+1s in applications and suggests ways to fix them. Another, arguably better option is &lt;a href=&quot;https://github.com/charkost/prosopite&quot;&gt;&lt;code&gt;prosopite&lt;/code&gt;&lt;/a&gt;, a less well-known option that generally provides better results with fewer false positives (and false negatives).&lt;/p&gt;
&lt;p&gt;Eager loading should be used with care, however; while you&amp;#39;re probably safe loading &lt;code&gt;has_one&lt;/code&gt; associations, &lt;code&gt;has_many&lt;/code&gt; can sometimes be dangerous: what happens when we&amp;#39;ve got a query of multiple joined, &lt;a href=&quot;https://en.wikipedia.org/wiki/Many-to-many_(data_model)&quot;&gt;many-to-many&lt;/a&gt; tables, and eager load one or more of the many-to-many associations?&lt;/p&gt;
&lt;p&gt;We can end up loading &lt;em&gt;way&lt;/em&gt; too many records into memory. This can crash our app just as surely as a bad N+1. If you&amp;#39;re at this point, and both the N+1 and eager loading approaches are causing you problems, it may be time to reevaluate the query itself. Maybe you&amp;#39;re trying to load associations you just want to &lt;code&gt;COUNT&lt;/code&gt; (which is something you might be able to build into your query), tighten your pagination limits, or consider offloading the query to a background job if possible (which we&amp;#39;ll cover).&lt;/p&gt;
&lt;h2&gt;Database Performance in Rails: Considerations and Optimization&lt;/h2&gt;
&lt;p&gt;Sometimes, breaking queries down into a couple of steps can both improve performance and simplify a query. Ever find yourself trying to get a subset of records to complete a mind-bending query? In certain cases, just finding the IDs of the subset you want and then feeding them into a second, less complex query can help reduce the need for complex joins, subqueries, etc.&lt;/p&gt;
&lt;p&gt;Have you ever fallen victim to Rails&amp;#39; (usually beneficial) default lazy-loading and evaluation, and wished you could load something &lt;em&gt;immediately&lt;/em&gt; rather than the first time it&amp;#39;s used? Sometimes we need not only the results of a query, but another aspect of it too, like the number of records we&amp;#39;ve retrieved. Our seemingly harmless code looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@users = User.where(&amp;quot;email ILIKE ?&amp;quot;, search)
@total_users = @users.size
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Normally, this is fine, but what if we need to count users before we do anything with them (maybe we&amp;#39;re displaying the number of users returned in our live search at the top of the page, followed by the users themselves)? We might add &lt;code&gt;COUNT&lt;/code&gt; to our query, but then we need to retrieve the records again.&lt;/p&gt;
&lt;p&gt;We can get around this by explicitly calling &lt;code&gt;load&lt;/code&gt; on our query — for example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@users = User.where(&amp;quot;email ILIKE ?&amp;quot;, search).load
@total_users = @users.size
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple change can save us a query. With our users already loaded, our display of &lt;code&gt;@total_users&lt;/code&gt; above the user list will no longer trigger an expensive &lt;code&gt;COUNT&lt;/code&gt; followed by another query to get users.&lt;/p&gt;
&lt;p&gt;In the other direction, we have &lt;a href=&quot;https://www.rubydoc.info/github/rails/rails/ActiveRecord%2FRelation:load_async&quot;&gt;&lt;code&gt;load_async&lt;/code&gt;&lt;/a&gt; with Rails 7. This allows us to asynchronously query the database with multiple requests at once, rather than being locked to one synchronous query at a time. This can be a total game-changer if you&amp;#39;re in a situation that can benefit from it. Be careful though, as this (probably unsurprisingly) uses Ruby threads under the hood, so comes with all the same issues you&amp;#39;d expect from threads. That&amp;#39;s not to mention the fact that it (potentially) opens our database up to a greatly increased number of connections, and can exhaust our connection pool if we aren&amp;#39;t careful.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;A large part of database performance in Rails comes down to how you utilize ActiveRecord. While AR is an invaluable tool, it can also lead to performance issues if not used with care. As you&amp;#39;re probably aware, it loads every column of every table involved in a query (&lt;code&gt;SELECT *&lt;/code&gt;) by default, regardless of how much of that data we actually use. It&amp;#39;s pretty easy to forget this though, especially as our queries evolve, and we can end up loading a lot of unnecessary data. It&amp;#39;s important to consider whether or not you&amp;#39;re really using all of the columns you&amp;#39;re loading, or if a &lt;code&gt;select&lt;/code&gt; (or even a &lt;code&gt;pluck&lt;/code&gt;) statement might be a better fit for your use case.&lt;/p&gt;
&lt;p&gt;You&amp;#39;re probably familiar with eager loading in Rails, but one thing that&amp;#39;s often overlooked is that Rails makes it easy to introduce &lt;em&gt;optional&lt;/em&gt; eager-loading (and &lt;code&gt;WHERE&lt;/code&gt; clauses, and &lt;code&gt;SELECT&lt;/code&gt;s). If you&amp;#39;ve ever had slightly different requirements for two very similar queries, you might feel like you&amp;#39;re left with two options: write two queries, or write covering both use cases, each retrieving more than you need. This might happen in a &lt;code&gt;before_action&lt;/code&gt;, where you typically want the same thing for each action in a particular controller. But one controller might benefit from eager loading, more careful column selection, or a search results page which is just filtering the usual index based on some criteria.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s another way, though:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  def search_user_posts(before_date: nil, optional_selects: [], optional_eager_loads: [], exclude_inactive: false, search: false)
    optional_before_date_constraint = [&amp;quot;created_at &amp;lt; ?&amp;quot;, before_date] if before_date
    optional_joins = {user_posts: [posts: :comments]} if search

    self.posts
        .includes(optional_eager_loads)
        .joins(:shared_joins)
        .joins(optional_joins)
        .select(:columns_shared_by_queries)
        .select(optional_selects)
        .where(&amp;quot;your shared WHERE constraints&amp;quot;)
        .where(optional_before_date_constraint)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails is smart enough to know not to execute anything here if there&amp;#39;s nothing passed in; &lt;code&gt;where(nil)&lt;/code&gt; will behave as though it doesn&amp;#39;t exist, as will &lt;code&gt;includes([])&lt;/code&gt;, etc. This can really clean up your code and help you tailor performance to your needs.&lt;/p&gt;
&lt;p&gt;Finally, rethinking your &lt;code&gt;JOIN&lt;/code&gt;s and &lt;code&gt;WHERE&lt;/code&gt;s can sometimes be beneficial. Are you querying a very large dataset where using a &lt;code&gt;JOIN&lt;/code&gt; could significantly reduce the initial result set? Try it out, and don&amp;#39;t be afraid to run &lt;a href=&quot;https://apidock.com/rails/ActiveRecord/Relation/explain&quot;&gt;&lt;code&gt;#explain&lt;/code&gt;&lt;/a&gt; on your query. It can help you determine if switching to a &lt;code&gt;JOIN&lt;/code&gt; might work better and identify any missing indexes on critical queries.&lt;/p&gt;
&lt;h2&gt;Database Indexing&lt;/h2&gt;
&lt;p&gt;One of our most important (and nearly universally necessary) tools for database performance is indexing. As you&amp;#39;re probably aware, indexes are special data structures (typically &lt;a href=&quot;https://en.wikipedia.org/wiki/B-tree&quot;&gt;B-trees&lt;/a&gt;) that a database uses to quickly find records, improving retrieval times from &lt;code&gt;O(n)&lt;/code&gt; to &lt;code&gt;O(log(n))&lt;/code&gt;. However, it&amp;#39;s important to add indexes carefully, as indexing columns unnecessarily can actually &lt;a href=&quot;https://www.arkware.com/striking-a-balance-the-pitfalls-of-over-indexing-in-databases/&quot;&gt;harm performance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The trick is to identify the columns and associations that your application needs to query frequently, and then create indexes on those. This allows the database to quickly locate the relevant data without having to scan the entire table.&lt;/p&gt;
&lt;p&gt;You can also use partial indexing. Partial indexes allow you to index a subset of a table&amp;#39;s rows based on a specified condition. This is particularly useful when your queries frequently target only a specific portion of the data.&lt;/p&gt;
&lt;p&gt;For example, maybe your app has a core of frequent visitors, the majority of whom have their own accounts. They make up the lion&amp;#39;s share of your traffic, but represent a small minority of your total users. You could define a partial index on the &lt;code&gt;users&lt;/code&gt; table like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AddPartialIndexToUsersOnGuest &amp;lt; ActiveRecord::Migration[7.1]
  def change
    add_index :users, :guest, where: &amp;quot;(guest = false)&amp;quot; # the `where: condition` here makes this a partial index.
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we&amp;#39;ve specifically targeted rows where &lt;code&gt;guest&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;. This optimizes the performance of any query searching for non-guest &lt;code&gt;users&lt;/code&gt; by reducing the total rows covered by the index.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s important to revisit your indexing profile and database schema periodically. The specific indexing needs of your application may change over time as the user base, codebase, and feature set evolve. Doing so often means you end up reviewing your indexes during comfortable periods where you have time to analyze and think about your database, rather than in a panic when an influx of traffic crashes your app.&lt;/p&gt;
&lt;p&gt;All of this brings us to our next topic: performance profiling.&lt;/p&gt;
&lt;h2&gt;Performance Profiling for Your Ruby on Rails App&lt;/h2&gt;
&lt;p&gt;So, we&amp;#39;ve identified some major categories of performance optimization; what now? Do we go through our entire app from top to bottom, optimizing line by line? Probably not.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s pretty unlikely that you have evenly distributed traffic across your whole site. It&amp;#39;s even less likely that you can afford to go through your entire codebase; premature optimization may not actually be the root of all evil, but you also don&amp;#39;t have time for it. So how do we know what to focus on?&lt;/p&gt;
&lt;p&gt;This is where profiling tools come in. While your local server output can provide valuable performance insights into response times, memory use, and query performance, you are only one user. Your local database likely doesn&amp;#39;t reflect the scale or patterns of your production database. Valuable as it is for spotting potential performance issues, your local environment doesn&amp;#39;t provide the context you need to make decisions about what genuinely needs your attention.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.appsignal.com/learning-center/what-is-an-apm&quot;&gt;Application Performance Monitoring tools&lt;/a&gt; (APMs) like &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;AppSignal for Ruby&lt;/a&gt; are essential for monitoring and maintaining our site&amp;#39;s performance. They provide comprehensive insights not just at the database layer, but across the rest of the stack, allowing us to pinpoint specific areas (pages, endpoints, and even specific queries) that are causing headaches. With a good APM tool, we can track the performance of any request over various timescales, monitor background jobs, spot memory leaks, set up custom error and performance alerts, and more.&lt;/p&gt;
&lt;p&gt;APMs provide visualization tools that make it much easier to spot and understand complex performance trends, leading to quicker, more informed decisions. They help prioritize optimization efforts by highlighting not only the slowest queries and most resource-intensive parts of an application, but also the endpoints that consume the most total time (&lt;code&gt;total request count * average response time&lt;/code&gt;). This means we can focus on what&amp;#39;s actually important. Remember that page we were worried about that takes a couple of seconds to load? Turns out, its total impact on our site amounts to practically nothing. Our products page though, clocking in at a reasonable 150ms? That&amp;#39;s taking up 15% of our total server time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To find out more about measuring database performance using AppSignal, check out our post &lt;a href=&quot;https://blog.appsignal.com/2024/06/12/monitor-the-performance-of-your-ruby-on-rails-application-using-appsignal.html&quot;&gt;Monitor the Performance of Your Ruby on Rails Application Using AppSignal&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Background Jobs&lt;/h2&gt;
&lt;p&gt;It isn&amp;#39;t always an option, but running queries in background jobs is a powerful tool for improving performance.&lt;/p&gt;
&lt;p&gt;You might be asking yourself how this could be, given that we only have one database; what difference does it make if we&amp;#39;re merely calling on it from another thread or process? But there are a few main ways background jobs can come to our database&amp;#39;s rescue.&lt;/p&gt;
&lt;p&gt;First, just by nature of the fact that our background jobs run asynchronously, we&amp;#39;re pretty free to run relatively resource-intensive tasks — like setting up &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-insert_all&quot;&gt;bulk inserts&lt;/a&gt; (or &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-upsert_all&quot;&gt;upserts&lt;/a&gt;), for example (note: neither validations nor callbacks are triggered for bulk operations) — and potentially distributing large numbers of queries across periods of time (perhaps especially for write operations), possibly with the use of &lt;code&gt;find_each&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;#39;re likely used to using a job to process a CSV or spreadsheet; those tasks tend to lend themselves to asynchronous processing as well as bulk insertions. If you&amp;#39;re doing this on your server&amp;#39;s main thread, you&amp;#39;re likely doing something wrong. But sometimes other, less obvious things we&amp;#39;re currently doing on our main thread can be moved to a background job to improve performance on the main thread &lt;em&gt;and&lt;/em&gt; reduce load on the database.&lt;/p&gt;
&lt;p&gt;Consider a scenario where your site allows people to make potentially large purchases of relatively inexpensive items. A school, for example, might order tens of thousands of pencils at once, and maybe your system creates a &lt;code&gt;PurchaseItem&lt;/code&gt; for every single item purchased. Instead of creating or updating thousands of rows one at a time, we can set up a bulk insert in a background job, create all the individual hashes inside a loop, and use &lt;code&gt;insert_all&lt;/code&gt; to get the job done — taking advantage of our background worker to set up our bulk inserts, and then writing to the database once, instead of locking it with thousands of writes.&lt;/p&gt;
&lt;h2&gt;Read Replicas&lt;/h2&gt;
&lt;p&gt;Background jobs are great, but eventually, even if we&amp;#39;re spacing out those large, numerous database queries, we hit a point where we&amp;#39;re asking too much of our database. We&amp;#39;re probably okay in terms of write performance (we tend to read from our database far more than we write to it, after all), but we&amp;#39;re constantly bombarding it with queries. Our site&amp;#39;s response times are steadily rising, and user experience is suffering.&lt;/p&gt;
&lt;p&gt;At this point, the next logical step may be to introduce &lt;a href=&quot;https://blogs.oracle.com/mysql/post/read-replicas-mysql-database-service&quot;&gt;read replicas&lt;/a&gt; to offload work from the primary database. Read replicas allow you to distribute the read query load across one or more replicas of your database. This can improve performance across the board, as it greatly reduces the number of reads (and thus the total I/O) of your primary database, leaving it less swamped by requests (most of which are likely reads in the first place).&lt;/p&gt;
&lt;p&gt;Ideally, large database operations are performed in background jobs reading from replicas, setting up a bulk &lt;code&gt;insert&lt;/code&gt; or &lt;code&gt;upsert&lt;/code&gt;, and then making one atomic write to your main database. This means you can potentially raise not only the read performance of your site, but can also boost write performance to some degree, as your primary database is consistently freer to accept write requests.&lt;/p&gt;
&lt;p&gt;While &lt;a href=&quot;https://catalinionescu.dev/blog/how-to-connect-to-multiple-databases/&quot;&gt;read replicas are not painless to set up&lt;/a&gt;, they can provide the extra performance our site needs.&lt;/p&gt;
&lt;h2&gt;Database Sharding At A High Level&lt;/h2&gt;
&lt;p&gt;Remember how we said SQL databases don&amp;#39;t scale horizontally? Technically, they can scale horizontally through &lt;a href=&quot;https://blog.appsignal.com/2022/12/07/database-performance-optimization-and-scaling-in-rails.html&quot;&gt;database sharding&lt;/a&gt;. Sharding involves splitting up your database into multiple databases, which are then distributed across multiple servers (and often across geographical locations).&lt;/p&gt;
&lt;p&gt;There are several approaches for doing this, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Geographical sharding (based on user locations).&lt;/li&gt;
&lt;li&gt;Vertical sharding (splitting into different tables or groups of tables based on access patterns, with each shard storing a different subset of the database’s tables).&lt;/li&gt;
&lt;li&gt;Functional sharding (splitting based on business function; like separating out billing from user content).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more. Each approach has its use cases, and some naturally lend themselves better to some businesses than others.&lt;/p&gt;
&lt;p&gt;As you&amp;#39;ve doubtlessly realized by now, sharding is generally difficult, and depending on your business, sometimes not even possible. It adds complexity, developmental and maintenance overhead to our apps, and potentially a lot of it. The good news is that if you don&amp;#39;t have the resources to cover this, you probably don&amp;#39;t need it in the first place. The bad news is that even if you do, it still complicates things and means increased costs (presumably balanced by the potential benefits).&lt;/p&gt;
&lt;p&gt;But if you&amp;#39;ve done all you can with query optimization, read replicas, and caching, sharding might just be the next necessary step — and one that can greatly increase your I/O capability.&lt;/p&gt;
&lt;h2&gt;Words of Caution and Final Words: Database Optimization in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;We need to be conscientious in how we approach our optimizations. It&amp;#39;s possible to over-index (or index the wrong things, which hurts write performance). In the same way that N+1s can get out of control if we&amp;#39;re not careful, so can eager loading, and it&amp;#39;s not always immediately obvious when we&amp;#39;re developing locally (hence the importance of at least approximating your real-world site locally).&lt;/p&gt;
&lt;p&gt;Keep an eye on things via an APM, as traffic and user patterns can change and new ones can emerge, not just over time but potentially week to week and season to season, depending on your business.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s no one-size-fits-all approach, so we have to be mindful of how we tailor our optimizations to our own site. Remember not to waste too much time trying to optimize things until you have an idea of the impact they&amp;#39;ll have on your site.&lt;/p&gt;
&lt;p&gt;That doesn&amp;#39;t mean you shouldn&amp;#39;t try to write generally efficient code, just that there&amp;#39;s generally no reason to agonize over tweaking things to be absolutely optimal or wasting time on micro-optimizations. Try to keep the time-to-benefit ratio in mind, and how what you&amp;#39;re working on will likely fit into your existing site as it is now, and as it evolves.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re interested in diving deeper into aspects of database optimization like sharding, you can &lt;a href=&quot;https://edgeguides.rubyonrails.org/active_record_multiple_databases.html&quot;&gt;learn more from the Rails guides&lt;/a&gt;. Also, check out &lt;a href=&quot;https://pawelurbanek.com/rails-load-async&quot;&gt;Paweł Urbanek&amp;#39;s great article on &lt;code&gt;load_async&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy optimizing!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>What&#039;s New in Ruby on Rails 8</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/10/07/whats-new-in-ruby-on-rails-8.html"/>
    <id>https://blog.appsignal.com/2024/10/07/whats-new-in-ruby-on-rails-8.html</id>
    <published>2024-10-07T00:00:00+00:00</published>
    <updated>2024-10-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore everything that Rails 8 has to offer.</summary>
    <content type="html">&lt;p&gt;The first &lt;a href=&quot;https://rubyonrails.org/2024/9/27/rails-8-beta1-no-paas-required&quot;&gt;Rails 8 beta&lt;/a&gt; has officially been released, bringing an exciting set
of features, bug fixes, and improvements. This version builds on the foundation
of
Rails 7.2,
while introducing new features and optimizations to make Rails development even
more productive and enjoyable.&lt;/p&gt;
&lt;p&gt;Key highlights include an integration with Kamal 2 for hassle-free deployments, the
introduction of Propshaft as the new default asset pipeline, and extensive
ActiveRecord enhancements. Rails 8 also brings several SQLite integration upgrades
that make it a viable option for production use.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive in and explore everything that Rails 8 has to offer!&lt;/p&gt;
&lt;h2&gt;Effortless Deployments with Kamal 2 and Thruster&lt;/h2&gt;
&lt;p&gt;Rails 8 makes deploying your applications simple with
Kamal 2 and Thruster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://kamal-deploy.org/&quot;&gt;Kamal 2&lt;/a&gt; reduces the need for reliance on managed cloud services and Platform as a Service (PaaS) platforms by
enabling quick and easy deployment to cloud VMs, bare metal servers, or VPS
environments in just minutes.&lt;/p&gt;
&lt;p&gt;With a single command (&lt;code&gt;kamal setup&lt;/code&gt;), you can set up a production-ready Rails
environment on a standard Linux box, making deployment both easy and
cost-effective.&lt;/p&gt;
&lt;p&gt;Kamal 2 also integrates with &lt;a href=&quot;https://github.com/basecamp/thruster&quot;&gt;Thruster&lt;/a&gt;, a custom proxy built specifically for
Rails that enables zero-downtime deployments, HTTP/2 support, automated SSL with
Let&amp;#39;s Encrypt, Gzip compression, and easy hosting of multiple apps on a single
server — all without complex setup.&lt;/p&gt;
&lt;p&gt;With Kamal 2 and Thruster, Rails 8 makes deploying apps easier than ever. And if
you prefer a different deployment setup, you can opt out using the
&lt;code&gt;--skip-kamal&lt;/code&gt; flag to maintain your existing workflows.&lt;/p&gt;
&lt;h2&gt;Leaner Rails Deployments with Solid Adapters&lt;/h2&gt;
&lt;p&gt;One of the big improvements in Rails 8 is simpler deployments by reducing
the number of additional services needed to implement common web application
requirements.&lt;/p&gt;
&lt;p&gt;Traditionally, if you required features like job queues, caching, and pub/sub
messaging, you&amp;#39;d use a combination of a database like PostgreSQL with Redis for
auxiliary functions.&lt;/p&gt;
&lt;p&gt;With Rails 8, you can handle all these with just SQLite, thanks to three new
database-backed adapters: &lt;a href=&quot;https://github.com/rails/solid_cable&quot;&gt;Solid Cable&lt;/a&gt;,
&lt;a href=&quot;https://github.com/rails/solid_cache&quot;&gt;Solid Cache&lt;/a&gt;, and
&lt;a href=&quot;https://github.com/rails/solid_queue&quot;&gt;Solid Queue&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Solid Cable&lt;/strong&gt; is Rails&amp;#39; new default Action Cable adapter in production and
means you can drop the common dependency on Redis. It acts as the
pub/sub server, relaying messages between the app and connected clients using
fast polling through SQLite. Despite polling, Solid Cable&amp;#39;s performance
is comparable to Redis in most situations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Solid Cache&lt;/strong&gt; replaces the need for Redis by using disk storage instead of
RAM for caching. This approach allows for much larger, more cost-effective
caches that persist longer and handle more requests without sacrificing
performance. It also supports encrypted storage and retention policies to
meet privacy requirements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Solid Queue&lt;/strong&gt; replaces Redis for
&lt;a href=&quot;https://edgeguides.rubyonrails.org/active_job_basics.html&quot;&gt;Active Job&lt;/a&gt;
background processing, using the &lt;code&gt;FOR UPDATE SKIP LOCKED&lt;/code&gt; mechanism for
efficient job handling (compatible with PostgreSQL, MySQL, or SQLite). It
includes essential features like concurrency control, retries, and recurring
jobs, and has proven itself at &lt;a href=&quot;https://www.hey.com/&quot;&gt;HEY&lt;/a&gt;, where it now
manages 20 million jobs per day.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These three adapters are designed around a simple idea: modern SSDs and NVMe
drives are fast enough to handle many tasks that previously required in-memory
solutions. By tapping into these speedy drives, Rails cuts out the need for
separate RAM-based tools like Redis.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;SQLite is Ready for Production&lt;/h2&gt;
&lt;p&gt;Rails 8 takes SQLite from a lightweight development tool to a reliable choice
for production use, thanks to extensive work on the SQLite adapter and Ruby
driver.&lt;/p&gt;
&lt;p&gt;With the introduction of the solid adapters discussed above, SQLite now has the
capability to power Action Cable, Rails.cache, and Active Job effectively,
expanding its role beyond just prototyping or testing environments.&lt;/p&gt;
&lt;p&gt;Here are some key improvements to the SQLite integration in Rails 8:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Full-text search and virtual tables are now supported using
&lt;code&gt;create_virtual_table&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The adapter now allows bulk insert fixtures for enhanced data seeding
performance.&lt;/li&gt;
&lt;li&gt;Transactions default to &lt;code&gt;IMMEDIATE&lt;/code&gt; mode to improve concurrency.&lt;/li&gt;
&lt;li&gt;Enhanced error handling by translating &lt;code&gt;SQLite3::BusyException&lt;/code&gt; into
&lt;code&gt;ActiveRecord::StatementTimeout&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A New Era for the Asset Pipeline with Propshaft&lt;/h2&gt;
&lt;p&gt;Rails 8 also introduces &lt;a href=&quot;https://github.com/rails/propshaft&quot;&gt;Propshaft&lt;/a&gt; as the
new asset pipeline default, replacing the long-standing
&lt;a href=&quot;https://github.com/rails/sprockets&quot;&gt;Sprockets&lt;/a&gt; system. Sprockets served Rails
developers well for over a decade, but it was designed in a different era — before
the explosion of JavaScript build tools and modern browser improvements.&lt;/p&gt;
&lt;p&gt;Propshaft reflects a simpler, modern approach to managing assets, built around
the core needs of today&amp;#39;s developers. Its purpose is straightforward: to provide a
clear path for assets and apply digest stamps for caching.&lt;/p&gt;
&lt;p&gt;Unlike Sprockets, which took on numerous additional tasks, Propshaft focuses
only on what&amp;#39;s essential, fitting naturally with the new Rails philosophy of
keeping asset pipelines lean (while complex JavaScript handling is left to
specialized tools like Esbuild or Vite).&lt;/p&gt;
&lt;h2&gt;Built-In Authentication Made Simple&lt;/h2&gt;
&lt;p&gt;Rails has been building the key components of authentication over the years,
from &lt;code&gt;has_secure_password&lt;/code&gt; in Rails 5 to &lt;code&gt;normalizes&lt;/code&gt;, &lt;code&gt;generates_token_for&lt;/code&gt; and
&lt;code&gt;authenticate_by&lt;/code&gt; in
&lt;a href=&quot;https://blog.appsignal.com/2023/02/15/whats-new-in-rails-7-1.html&quot;&gt;Rails 7.1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With Rails 8, all these components come together to give you a straightforward
starting point for building a secure, session-based authentication system.&lt;/p&gt;
&lt;p&gt;By running a single command, you can set up all the essentials for an
authentication system with database-backed sessions and password reset
functionality:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bin/rails generate authentication
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command generates key files, including models, controllers, mailers, and
views:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;app/models/current.rb
app/models/user.rb
app/models/session.rb
app/controllers/sessions_controller.rb
app/controllers/passwords_controller.rb
app/mailers/passwords_mailer.rb
app/views/sessions/new.html.erb
app/views/passwords/new.html.erb
app/views/passwords/edit.html.erb
app/views/passwords_mailer/reset.html.erb
app/views/passwords_mailer/reset.text.erb
db/migrate/xxxxxxx_create_users.rb
db/migrate/xxxxxxx_create_sessions.rb
test/mailers/previews/passwords_mailer_preview.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This effectively puts you on the fast track to secure, production-ready
authentication. All that&amp;#39;s left is to integrate a user sign-up flow
customized to your application&amp;#39;s needs.&lt;/p&gt;
&lt;h2&gt;New Script Folder and Generator&lt;/h2&gt;
&lt;p&gt;Rails 8 introduces a new &lt;code&gt;script&lt;/code&gt; folder dedicated to holding one-off or
general-purpose scripts, such as data migrations, cleanup tasks, or other
utility operations. This addition helps organize these scripts neatly, keeping
them separate from your main application logic.&lt;/p&gt;
&lt;p&gt;To make script creation easier, a new script generator is available. You can
generate scripts with a simple command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bin/rails generate script my_script
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These commands create the corresponding script files, which you can then execute
with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec ruby script/my_script.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This streamlined approach keeps your application organized and makes handling
custom scripts more convenient and maintainable.&lt;/p&gt;
&lt;h2&gt;A Slew of Active Record Improvements&lt;/h2&gt;
&lt;p&gt;Active Record has also seen major enhancements in Rails 8 to improve
performance, simplify migrations, improve troubleshooting, and provide better
support for complex database use cases.&lt;/p&gt;
&lt;p&gt;Below are some of the key changes introduced in this latest version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rails 8 now distinguishes between &lt;code&gt;float4&lt;/code&gt; and &lt;code&gt;float8&lt;/code&gt; in PostgreSQL.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;drop_table&lt;/code&gt; now supports dropping multiple tables at once.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rails/rails/pull/50475&quot;&gt;Support for advanced options when creating a table with PostgreSQL&lt;/a&gt;, including inheritance and partitioning.&lt;/li&gt;
&lt;li&gt;Bulk inserts of fixtures are now supported to improve data seeding performance.&lt;/li&gt;
&lt;li&gt;Migrating a fresh database now starts by loading the database schema before
running the migrations.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create_schema&lt;/code&gt; and &lt;code&gt;drop_schema&lt;/code&gt; operations are now reversible.&lt;/li&gt;
&lt;li&gt;Rails 8 now requires MySQL 5.6.4 or later due to advancements like datetime
with precision.&lt;/li&gt;
&lt;li&gt;Query log tags are enabled by default in development environments to trace SQL
statements back to the application code and identify which database is in use.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Rails 8 introduces a range of impactful updates, from easier deployments with
Kamal and a modern asset pipeline to significant ActiveRecord enhancements and
improved production capabilities for SQLite.&lt;/p&gt;
&lt;p&gt;These advancements not only boost developer productivity but also align with
modern best practices, allowing you to focus on building your application
instead of dealing with infrastructure complexities.&lt;/p&gt;
&lt;p&gt;For a detailed list of all the new features, optimizations, and changes, check
out the
&lt;a href=&quot;https://edgeguides.rubyonrails.org/8_0_release_notes.html&quot;&gt;official Rails 8 release notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to get involved with contributing to Rails, visit the
&lt;a href=&quot;https://github.com/rails/rails/&quot;&gt;Rails GitHub repository&lt;/a&gt; to explore open
issues and review the contribution guidelines.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Measuring the Impact of Feature Flags in Ruby on Rails with AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/10/02/measuring-the-impact-of-feature-flags-in-ruby-on-rails-with-appsignal.html"/>
    <id>https://blog.appsignal.com/2024/10/02/measuring-the-impact-of-feature-flags-in-ruby-on-rails-with-appsignal.html</id>
    <published>2024-10-02T00:00:00+00:00</published>
    <updated>2024-10-02T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll set up feature flags in a Solidus storefront using Flipper and AppSignal&#039;s custom metrics.</summary>
    <content type="html">&lt;p&gt;Feature flags are a powerful tool in software development, allowing developers to control the behavior of an application at runtime without deploying new code. They enable teams to test new features, perform A/B testing, and roll out changes gradually.&lt;/p&gt;
&lt;p&gt;In Ruby on Rails, feature flags can be managed using diverse tools, the most popular being the Flipper gem. This article will explore implementing and measuring the impact of feature flags in a Solidus storefront using Flipper and AppSignal&amp;#39;s custom metrics.&lt;/p&gt;
&lt;h2&gt;What Are Feature Flags in Rails, Again?&lt;/h2&gt;
&lt;p&gt;If you are looking for an introduction to the subject, &lt;a href=&quot;https://blog.appsignal.com/2022/06/08/add-feature-flags-in-ruby-on-rails-with-flipper.html&quot;&gt;check out the post Add Feature Flags in Ruby on Rails with Flipper&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In a nutshell, though, feature flags are a way to influence how your application behaves &lt;strong&gt;at runtime&lt;/strong&gt;, without having to deploy new code. The simplest type of feature flags are environment variables. Every Ruby on Rails application uses them out of the box. One example is the configuration of application server concurrency using &lt;code&gt;ENV[&amp;#39;WEB_CONCURRENCY&amp;#39;]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, there are other ways to manage feature flags, such as using a persistence layer like ActiveRecord or Redis. A comprehensive way to do this is offered by the &lt;a href=&quot;https://www.flippercloud.io/docs&quot;&gt;Flipper gem&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The following snippet exemplifies how the &lt;code&gt;performance_improvement&lt;/code&gt; feature flag is evaluated for a given user:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@categories = if Flipper.enabled?(:performance_improvement, user)
  Category.all.includes(:products)
else
  Category.all
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we will set up a &lt;a href=&quot;https://github.com/solidusio/solidus&quot;&gt;Solidus&lt;/a&gt; storefront to start experimenting with feature flags.&lt;/p&gt;
&lt;h2&gt;Our Example App: A Solidus Storefront&lt;/h2&gt;
&lt;p&gt;To measure the impact of feature flags in a somewhat realistic scenario, let&amp;#39;s quickly bootstrap a Solidus store:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails new coder_swag_store &amp;amp;&amp;amp; cd coder_swag_store
bundle add solidus
bin/rails g solidus:install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This generator will guide you through the process and ask you a few setup questions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Choose the &lt;em&gt;starter&lt;/em&gt; frontend when queried for the frontend type.&lt;/li&gt;
&lt;li&gt;Skip setting up a payment method.&lt;/li&gt;
&lt;li&gt;Choose to mount your Solidus application at &lt;code&gt;/&lt;/code&gt;, since we are using it as a standalone app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Afterward, run &lt;code&gt;bin/dev&lt;/code&gt; from your terminal and you should be good to go. When you go to &lt;code&gt;http://localhost:3000&lt;/code&gt;, you&amp;#39;ll see this screen:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-10/solidus-sample-store.png&quot; alt=&quot;Solidus sample store&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Implement Feature Flags with Flipper&lt;/h2&gt;
&lt;p&gt;Now let&amp;#39;s implement two exemplary use cases for feature flags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A performance improvement.&lt;/li&gt;
&lt;li&gt;An attempt at conversion rate optimization.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First of all, though, we have to add the &lt;code&gt;flipper&lt;/code&gt; gem along with its &lt;code&gt;active_record&lt;/code&gt; storage adapter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bundle add flipper
bundle add flipper-active_record
bin/rails g flipper:setup
bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will set up the required database tables to look up Flipper &amp;quot;gates&amp;quot;, i.e., concrete conditionals to evaluate when checking a feature flag.&lt;/p&gt;
&lt;h3&gt;Testing a Performance Improvement&lt;/h3&gt;
&lt;p&gt;To assess this scenario, we will simulate a slow request in the storefront by adding a &lt;code&gt;sleep 1&lt;/code&gt; call for the unoptimized case:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/products_controller.rb

def index
  @searcher = build_searcher(params.merge(include_images: true))
  @products = @searcher.retrieve_products

  if Flipper.enabled?(:performance_improvement)
    # 🚅
  else
    sleep 1
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we will use a &lt;a href=&quot;https://www.flippercloud.io/docs/features/percentage-of-time&quot;&gt;&amp;quot;percentage of time&amp;quot; strategy&lt;/a&gt; to roll out the optimization across a random set of requests. Open a Rails console and key in the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Flipper.enable_percentage_of_time(:performance_improvement, 50)
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Using the &lt;a href=&quot;https://github.com/hatoo/oha/&quot;&gt;oha&lt;/a&gt; load testing tool, we can confirm that indeed half of the requests take one second longer than the others:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ oha http://localhost:3000/products -z 30s -q 2
Summary:
  Success rate: 100.00%
  Total:        30.0037 secs
  Slowest:      1.2662 secs
  Fastest:      0.1049 secs
  Average:      0.6260 secs
  Requests/sec: 2.0664

  Total data:   2.59 MiB
  Size/request: 44.24 KiB
  Size/sec:     88.47 KiB

Response time histogram:
  0.105 [1]  |■
  0.221 [24] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.337 [8]  |■■■■■■■■■■
  0.453 [0]  |
  0.569 [0]  |
  0.686 [0]  |
  0.802 [0]  |
  0.918 [0]  |
  1.034 [0]  |
  1.150 [6]  |■■■■■■■■
  1.266 [21] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Testing Conversion Rate Optimization&lt;/h3&gt;
&lt;p&gt;When dealing with user-facing features, for example, changes in the UI, it&amp;#39;s often advisable to use a &lt;a href=&quot;https://www.flippercloud.io/docs/features/percentage-of-actors&quot;&gt;&amp;quot;percentage of actors&amp;quot; strategy&lt;/a&gt; to roll out flags. This way, every user is consistently offered the same experience.&lt;/p&gt;
&lt;p&gt;So to start, we&amp;#39;ll create two users for our e-commerce application. Fire up a Rails console and issue the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Spree::User.create(email: &amp;quot;test1@example.com&amp;quot;, login: &amp;quot;test1@example.com&amp;quot;, password: &amp;quot;super_safe_password&amp;quot;, password_confirmation: &amp;quot;super_safe_password&amp;quot;)
Spree::User.create(email: &amp;quot;test2@example.com&amp;quot;, login: &amp;quot;test2@example.com&amp;quot;, password: &amp;quot;super_safe_password&amp;quot;, password_confirmation: &amp;quot;super_safe_password&amp;quot;)
Flipper.enable_percentage_of_actors(:conversion_rate_optimization, 50)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates two sample users and ensures that the feature flag is &lt;strong&gt;consistently&lt;/strong&gt; enabled for one of them.&lt;/p&gt;
&lt;p&gt;To simulate a feature attempting to drive conversion rates up, we&amp;#39;ll make the checkout button pulsate:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/carts/_cart_footer.html.erb --&amp;gt;
&amp;lt;% order = order_form.object %&amp;gt;

&amp;lt;footer class=&amp;quot;cart-footer&amp;quot;&amp;gt;
  &amp;lt;%= render &amp;#39;carts/cart_adjustments&amp;#39; %&amp;gt;
  &amp;lt;p class=&amp;quot;cart-footer__total flex justify-between mb-3 text-body-20 p-2&amp;quot;&amp;gt;
    &amp;lt;%= t(&amp;#39;spree.total&amp;#39;) %&amp;gt;: &amp;lt;span class=&amp;quot;font-sans-md&amp;quot;&amp;gt;&amp;lt;%= order.display_total %&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;div class=&amp;quot;cart-footer__primary-action&amp;quot;&amp;gt;
    &amp;lt;%= order_form.button(
      I18n.t(&amp;#39;spree.checkout&amp;#39;),
      class: &amp;quot;button-primary w-full #{&amp;#39;animate-pulse&amp;#39; if Flipper.enabled?(:conversion_rate_optimization, spree_current_user)}&amp;quot;,
      id: &amp;#39;checkout-link&amp;#39;,
      name: :checkout
    ) %&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/footer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we log in with both users and arrange the browser windows side by side, we can observe that indeed the effect is active for one (the left) user:&lt;/p&gt;
&lt;Video fileName=&quot;/images/blog/2024-10/cro&quot; /&gt;

&lt;h2&gt;Use AppSignal Custom Metrics to Measure the Impact of Feature Flags&lt;/h2&gt;
&lt;p&gt;The best feature flag system is useless if there&amp;#39;s no way to evaluate its impact. In our example scenario, we simply want to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Has the performance improvement led to a significant latency reduction?&lt;/li&gt;
&lt;li&gt;Has our pulsating checkout button led to a significantly higher conversion rate?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will use &lt;a href=&quot;https://docs.appsignal.com/metrics/custom.html&quot;&gt;AppSignal&amp;#39;s custom metrics&lt;/a&gt; to measure the pay-off of these optimizations.&lt;/p&gt;
&lt;p&gt;First of all, create a new application in your AppSignal organization and connect it to your app by following the instructions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bundle add appsignal
bundle exec appsignal install YOUR_APPSIGNAL_UUID
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Measuring Latency with a Measurement Metric&lt;/h3&gt;
&lt;p&gt;We have verified how effective our improvement is with the &lt;code&gt;oha&lt;/code&gt; CLI above, but to make valid judgments we&amp;#39;ll install server-side telemetry that reports latency to AppSignal. A &lt;a href=&quot;https://docs.appsignal.com/metrics/custom.html#measurement&quot;&gt;measurement metric&lt;/a&gt; allows for exactly that: we will send over response times in milliseconds, and add a &lt;a href=&quot;https://docs.appsignal.com/metrics/custom.html#metric-tags&quot;&gt;metric tag&lt;/a&gt; indicating whether our performance optimization was active for a specific request.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s a small gotcha here: because we&amp;#39;re employing the &amp;quot;Percentage of Time&amp;quot; metric, we have to capture the flag&amp;#39;s state in an instance variable so that the same value is used for execution and for reporting:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/products_controller.rb

class ProductsController &amp;lt; StoreController
  around_action :measure_response_time, only: :index

  # ...

  def index
    @searcher = build_searcher(params.merge(include_images: true))
    @products = @searcher.retrieve_products

    @performance_improvement_enabled = Flipper.enabled?(:performance_improvement)

    if @performance_improvement_enabled
      # 🚅
    else
      sleep 1
    end
  end

  # ...

  private

  # ...

  def measure_response_time
    response_time = Benchmark.realtime { yield }
    Appsignal.add_distribution_value(&amp;quot;products_response_time&amp;quot;, response_time * 1000, performance_improvement_enabled: @performance_improvement_enabled)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&amp;#39;s repeat the local load testing from above:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ oha http://localhost:3000/products -z 30s -q 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ll look at charting and evaluating this metric in a bit. Before that, let&amp;#39;s turn to our second feature flag.&lt;/p&gt;
&lt;h3&gt;Tallying Conversions with a Count Metric&lt;/h3&gt;
&lt;p&gt;We&amp;#39;ll use a &lt;a href=&quot;https://docs.appsignal.com/metrics/custom.html#counter&quot;&gt;counter metric&lt;/a&gt; to count conversions. This is a great choice if all you want to do is just keep a tally of an event.&lt;/p&gt;
&lt;p&gt;To do this, we&amp;#39;ll have to open &lt;code&gt;CartsController&lt;/code&gt;, and, for demonstration purposes, add an &lt;code&gt;increment_counter&lt;/code&gt; call if the checkout button is clicked:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/carts_controller.rb

def update
  authorize! :update, @order, cookies.signed[:guest_token]
  if @order.contents.update_cart(order_params)
    # ...

    Appsignal.increment_counter(&amp;quot;checkout_count&amp;quot;, 1, optimization_active: Flipper.enabled?(:conversion_rate_optimization, spree_current_user))

    # ...
  else
    render action: :show
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&amp;#39;s test this by manually opening respective browser windows and clicking the &amp;quot;Checkout&amp;quot; button 3 times, and in another case only once. In this way, we can see if the optimization flag is active.&lt;/p&gt;
&lt;h2&gt;Set Up Custom Dashboards in AppSignal&lt;/h2&gt;
&lt;p&gt;Our final step is to create informative graphics to make data-informed business decisions. We&amp;#39;ll use &lt;a href=&quot;https://docs.appsignal.com/metrics/dashboards.html&quot;&gt;AppSignal&amp;#39;s dashboards&lt;/a&gt; to achieve this. Let&amp;#39;s go through this step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the left sidebar, click &amp;quot;Add dashboard&amp;quot; and name it &amp;quot;Feature Flag Evaluation&amp;quot;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-10/add-dashboard.png&quot; alt=&quot;Add dashboard&quot;/&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Click &amp;quot;Add Graph&amp;quot; and the &lt;code&gt;products_response_time&lt;/code&gt; metric. Select &amp;quot;mean&amp;quot; to display only averages and apply the &lt;code&gt;performance_improvement_enabled&lt;/code&gt; tag.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-10/add-graph-mean.png&quot; alt=&quot;Add graph — mean&quot;/&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Click &amp;quot;Add new Graph&amp;quot; to add a chart for the checkout counts. Again, apply the &lt;code&gt;optimization_active&lt;/code&gt; tag.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-10/add-graph.png&quot; alt=&quot;Add new graph&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now your custom dashboard is ready. In the line graph on the left, you can assert that your performance improvement was effective. On the right, observe how the higher count of checkouts in the optimized case was recorded.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-10/custom-dashboard.png&quot; alt=&quot;Custom dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;And that&amp;#39;s it!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve seen how feature flags offer a flexible and efficient way to manage and deploy new features in a Ruby on Rails application. By using tools like the Flipper gem and AppSignal&amp;#39;s custom metrics, developers can not only control feature rollouts, but also measure their impact on performance and user behavior.&lt;/p&gt;
&lt;p&gt;This approach ensures that new features are thoroughly tested and optimized before being fully deployed, ultimately leading to a more stable and user-friendly application. Finally, it can lead to more informed business decisions when gauging the effectiveness of alternative approaches.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby’s hidden gems: Sorbet</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/09/18/rubys-hidden-gems-sorbet.html"/>
    <id>https://blog.appsignal.com/2024/09/18/rubys-hidden-gems-sorbet.html</id>
    <published>2024-09-18T00:00:00+00:00</published>
    <updated>2024-09-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s see how Sorbet addresses the challenges of dynamic typing in Ruby.</summary>
    <content type="html">&lt;p&gt;The debate between static and dynamically typed languages has long been a subject of contention among developers. Each approach offers its own set of advantages and disadvantages, significantly influencing the software development process.&lt;/p&gt;
&lt;p&gt;Dynamically typed languages like Ruby provide flexibility by allowing variables to be declared without corresponding types. This approach fosters rapid development and promotes an agile process.&lt;/p&gt;
&lt;p&gt;Yet, the absence of strict typing can lead to challenges, such as runtime errors that may be harder to debug and maintain in larger codebases. For example, in a dynamically typed language like Ruby, attempting to divide an array by a string only results in an error when the code is executed, making it potentially harder to identify and fix such issues.&lt;/p&gt;
&lt;p&gt;In this article, we explore Sorbet, a type checker for Ruby, which addresses the challenges of dynamic typing in Ruby, enhancing code reliability and maintainability without sacrificing the language&amp;#39;s flexibility and expressiveness.&lt;/p&gt;
&lt;h2&gt;What is Sorbet for Ruby?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sorbet/sorbet&quot;&gt;Sorbet&lt;/a&gt;, implemented in C++, is a Ruby gem designed to harmonize the dynamism of Ruby with the reliability and predictability of static typing. As Ruby projects scale in size and complexity, maintaining code quality and preventing errors becomes increasingly challenging. A primary culprit is the absence of static typing, which often necessitates heavy reliance on extensive testing and runtime checks to ensure code correctness, resulting in more frequent bugs slipping into production.&lt;/p&gt;
&lt;p&gt;Developed by Stripe, Sorbet seeks to tackle these challenges by introducing static typing to Ruby. It functions as a type checker and gradual type system for Ruby, enabling the annotation of code with type information and the detection of errors at compile time (rather than runtime).&lt;/p&gt;
&lt;p&gt;Key features and benefits of Sorbet include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Type Declaration:&lt;/strong&gt; Sorbet allows for the declaration of types for variables, method parameters, and return values. This facilitates early error detection and enhances code readability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gradual Typing:&lt;/strong&gt; Unlike statically typed languages where typing is mandatory, Sorbet permits the incremental introduction of type annotations. This means existing Ruby codebases can transition gradually to a statically typed workflow without needing a complete rewrite.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instantaneous Feedback:&lt;/strong&gt; During development, Sorbet provides instant insights into method definitions and usage. Clicking on a method reveals its definition, while hovering over it displays information about its input and return types. This real-time feedback accelerates development and enhances code navigation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type Inference:&lt;/strong&gt; Sorbet employs a straightforward type inference algorithm to deduce types where explicit annotations are absent. This minimizes the need for manual type annotations and facilitates the adoption of static typing in Ruby projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling Integration:&lt;/strong&gt; Sorbet seamlessly integrates with popular Ruby development tools, including editors, IDEs, and build systems. This ensures a seamless developer experience and promotes the adoption of static typing practices within Ruby development workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started with Sorbet&lt;/h2&gt;
&lt;p&gt;Before diving into Sorbet&amp;#39;s integration into your codebase, it&amp;#39;s helpful to explore a Sorbet playground, which provides a sandbox environment to experiment with Sorbet&amp;#39;s features. &lt;a href=&quot;https://sorbet.run/#%23%20typed%3A%20true%0Arequire%20&#039;sorbet-runtime&#039;%0A%0Aclass%20A%0A%20%20extend%20T%3A%3ASig%0A%0A%20%20sig%20%7Bparams%28x%3A%20Integer%29.returns%28String%29%7D%0A%20%20def%20bar%28x%29%0A%20%20%20%20x.to_s%0A%20%20end%0Aend%0A%0Adef%20main%0A%20%20A.new.barr%2891%29%20%20%20%23%20error%3A%20Typo!%0A%20%20A.new.bar%28%2291%22%29%20%20%23%20error%3A%20Type%20mismatch!%0Aend&quot;&gt;Here&amp;#39;s a Sorbet playground&lt;/a&gt; that allows you to tweak code and see how Sorbet responds to various changes.&lt;/p&gt;
&lt;p&gt;To understand Sorbet and its functionality, before applying it to an existing codebase, let&amp;#39;s start a new Ruby project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory:
Begin by creating a new directory named &lt;code&gt;sorbet-test&lt;/code&gt; where we&amp;#39;ll set up our Sorbet testing environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;mkdir sorbet-test
cd sorbet-test
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Set up the Gemfile:
If you don&amp;#39;t already have a Gemfile in your sorbet-test directory, create one using the following command:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;touch Gemfile
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add the Sorbet gem to your Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;source &amp;#39;https://rubygems.org&amp;#39;

gem &amp;#39;sorbet&amp;#39;, group: :development
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Install Sorbet:
Install the Sorbet gem using Bundler:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Verify the installation:
Check that Sorbet is installed correctly by running:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec srb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should give an output that indicates that no &lt;code&gt;sorbet/&lt;/code&gt; directory was found and prompts us to initialize our directory with &lt;code&gt;&amp;#39;srb init&amp;#39;&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a Ruby file:
Now, let&amp;#39;s create a new Ruby file named &lt;code&gt;person.rb&lt;/code&gt; in our &lt;code&gt;sorbet-test&lt;/code&gt; directory, and add the following code:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Person
 attr_accessor :name

  def initialize(name)
    @name = name
  end

  def check_name
    if nam == &amp;#39;John&amp;#39;
      puts &amp;#39;This person is John&amp;#39;
    else
      puts &amp;#39;This person is not John&amp;#39;
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Initialize Sorbet:
Initialize your directory with Sorbet by running:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;srb init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a sorbet folder with an &lt;code&gt;rbi&lt;/code&gt; subfolder in our directory. You&amp;#39;ll also find the &lt;code&gt;# typed: false&lt;/code&gt; sigil appended to the &lt;code&gt;person.rb&lt;/code&gt; file.
This sigil indicates to Sorbet what errors to report and which to silence (&lt;code&gt;# typed: ignore&lt;/code&gt; being the least, as it causes Sorbet to ignore the file, and &lt;code&gt;# typed: strong&lt;/code&gt; being the strictest, as all errors in this file are reported). &lt;a href=&quot;https://sorbet.org/docs/static&quot;&gt;Read more detailed information about these sigils&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update typed sigil:
Update the &lt;code&gt;# typed&lt;/code&gt; sigil in the &lt;code&gt;person.rb&lt;/code&gt; file to &lt;code&gt;true&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# typed: true
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Run Sorbet:
Finally, run Sorbet with &lt;code&gt;bundle exec srb&lt;/code&gt; to check your Ruby file for type errors.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following error should ensue:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;person.rb:10: Method nam does not exist on Person https://srb.help/7003
    10 |    if nam == &amp;#39;John&amp;#39;
               ^^^
  Did you mean name? Use -a to autocorrect
    person.rb:10: Replace with name
    10 |    if nam == &amp;#39;John&amp;#39;
               ^^^
    person.rb:3: Defined here
     3 | attr_accessor :name
         ^^^^^^^^^^^^^^^^^^^
Errors: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Correcting this typo to &lt;code&gt;name&lt;/code&gt; and rerunning the command should output the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;No errors! Great job.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Type Mismatches in Sorbet&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s see another example of how Sorbet detects a type mismatch.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add this new method to &lt;code&gt;person.rb&lt;/code&gt; file:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def name_length
  puts name.length
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Within the file, call this method as such:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Person.new(8).name_length
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Run Sorbet with &lt;code&gt;bundle exec srb&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We do not get any errors. However, when we run this code, we get the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;/.../person.rb:18:in `name_length&amp;#39;: undefined method `length&amp;#39; for 8:Integer (NoMethodError)

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

&lt;h3&gt;Adding Type Signatures&lt;/h3&gt;
&lt;p&gt;We can enable type signatures in our code by extending the &lt;code&gt;T::Sig&lt;/code&gt; module and adding signatures to methods. Let&amp;#39;s add a signature to the initialize method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Person
  extend T::Sig

  sig {params(name: String).void}
  def initialize(name)
    @name = name
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This signature is basically telling Sorbet that this method takes a parameter &lt;code&gt;name&lt;/code&gt; of type &lt;code&gt;String&lt;/code&gt; and returns nothing.&lt;/p&gt;
&lt;p&gt;Now, running &lt;code&gt;bundle exec srb&lt;/code&gt; outputs the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;person.rb:24: Expected String but found Integer(8) for argument name https://srb.help/7002
    24 |Person.new(8).name_length
                   ^
  Expected String for argument name of method Person#initialize:
    person.rb:6:
     6 |  sig {params(name: String).void}
                      ^^^^
  Got Integer(8) originating from:
    person.rb:24:
    24 |Person.new(8).name_length
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sorbet successfully detects the error due to the addition of type signatures. Updating the sigil to &lt;code&gt;# typed: strict&lt;/code&gt;, requires all methods to possess a type signature.&lt;/p&gt;
&lt;h2&gt;Sorbet Runtime Support&lt;/h2&gt;
&lt;p&gt;Although Sorbet is able to carry out its checks successfully, we&amp;#39;re not able to run this piece of code. We get the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;/.../person.rb:3:in `&amp;lt;class:Person&amp;gt;&amp;#39;: uninitialized constant Person::T (NameError)

  extend T::Sig
          ^^^^^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This error indicates that Sorbet&amp;#39;s type annotations, represented by the &lt;code&gt;T&lt;/code&gt; module, were not found. Our piece of code requires access to the necessary runtime support for Sorbet&amp;#39;s type annotations, which allows it to run correctly alongside Sorbet&amp;#39;s static type checking.&lt;/p&gt;
&lt;p&gt;To resolve this, we need to add the &lt;code&gt;sorbet-runtime&lt;/code&gt; gem to our project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;sorbet-runtime&amp;#39;, group: :development
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running &lt;code&gt;bundle install&lt;/code&gt;, require &lt;code&gt;sorbet-runtime&lt;/code&gt; in our file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# person.rb
require &amp;#39;sorbet-runtime&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;sorbet-runtime&lt;/code&gt; loaded, our code runs correctly, and Sorbet effectively enforces type safety.&lt;/p&gt;
&lt;h2&gt;Sorbet IDE Integration&lt;/h2&gt;
&lt;p&gt;Running &lt;code&gt;bundle exec srb tc&lt;/code&gt; frequently to typecheck code can be tedious and time-consuming. To streamline the development process, Sorbet offers a VSCode extension that provides various features to enhance your coding experience. These features include autocomplete, jump to definition, type information and documentation on hover, sig suggestion, quick fixes, autocorrection, static error displays, and more. &lt;a href=&quot;https://sorbet.org/docs/vscode&quot;&gt;Find out more about how to install and use the VSCode extension&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For developers using editors other than VS Code, Sorbet does not have official integrations or extensions. However, some editors support the Language Server Protocol (LSP), allowing language servers like Sorbet to integrate with them. JetBrains IDEs, including IntelliJ IDEA, PyCharm, and RubyMine, have built-in support for the LSP. Additionally, there are plugins available for Sublime Text and Atom that enable LSP support. While these solutions may not offer the same level of integration and features as the dedicated Sorbet extension for VSCode, they can still provide basic functionality, such as syntax highlighting, autocompletion, and error checking.&lt;/p&gt;
&lt;h2&gt;Exploring Tapioca&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve gone through the process of adding types to a new project. However, what about projects already in existence? Typically, these projects come with pre-installed gems, and these third-party services include methods invoked within our project. How do we guarantee that we&amp;#39;re passing the correct types of parameters to these methods, or that they&amp;#39;re being invoked on appropriate types? The answer to this is by using &lt;a href=&quot;https://github.com/Shopify/tapioca&quot;&gt;Tapioca&lt;/a&gt;. To understand what Tapioca does, it&amp;#39;s important to first understand what Ruby Interface (RBI) files are.&lt;/p&gt;
&lt;h3&gt;What Are Ruby Interface (RBI) Files?&lt;/h3&gt;
&lt;p&gt;RBI means Ruby Interface, and RBI files serve as interface files that provide documentation on type information for Ruby code. They contain declarations of types, method signatures, and other type-related metadata. They can either be created manually or autogenerated.&lt;/p&gt;
&lt;p&gt;While Sorbet is capable of inferring types to some extent, there are scenarios for which Sorbet lacks sufficient information. For example, when dealing with complex inheritance hierarchies, method overrides, external dependencies, or dynamic method calls, Sorbet may struggle to infer types accurately without explicit type annotations provided by RBI files.&lt;/p&gt;
&lt;h3&gt;What Does Tapioca Do?&lt;/h3&gt;
&lt;p&gt;Tapioca, a Ruby gem developed by Shopify, streamlines the creation of RBI files, which are essential for implementing gradual typing in Ruby projects. These RBI files can cover not only the gems used within the application but also the methods available within Rails, along with other DSLs and metaprogramming paradigms.&lt;/p&gt;
&lt;p&gt;To integrate Tapioca into an existing Rails project for use with Sorbet, follow these steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add the Tapioca gem to your Gemfile, alongside existing gems such as &lt;code&gt;sorbet&lt;/code&gt; and &lt;code&gt;sorbet-runtime&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;tapioca&amp;#39;, require: false, group: [:development, :test]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Initialize the project for use with Sorbet by running:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec tapioca init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command generates a Sorbet folder structure within your project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;├── config             # Default options to be passed to Sorbet on every run
└── rbi/
  ├── annotations/     # Type definitions pulled from the rbi-central repository
  ├── gems/            # Autogenerated type definitions for your gems
  └── todo.rbi         # Constants which were still missing after RBI generation
└── tapioca/
  ├── config.yml       # Default options to be passed to Tapioca
  └── require.rb       # A file where you can make requires from gems that might be needed for gem RBI generation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The terminal output provides valuable guidance on generating type definitions for DSLs in your application, performing type checking, and upgrading files from &lt;code&gt;# typed: false&lt;/code&gt; to &lt;code&gt;# typed: true&lt;/code&gt; using tools like &lt;a href=&quot;https://github.com/Shopify/spoom&quot;&gt;Spoom&lt;/a&gt;. Take some time to review this information.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s illustrate with a simple example involving a &lt;code&gt;Person&lt;/code&gt; model in your project. After adding &lt;code&gt;# typed: true&lt;/code&gt; to a file, attempting to call &lt;code&gt;Person.all&lt;/code&gt; triggers an error:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;Method `all` does not exist on `T.class_of(Person)`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the Sorbet extension in your IDE, &lt;code&gt;Person.all&lt;/code&gt; is highlighted in red, indicating an error. This occurs because Sorbet lacks awareness of &lt;code&gt;Person&lt;/code&gt; as a model or the available Rails methods for the &lt;code&gt;Person&lt;/code&gt; class. To resolve this, we need to generate an RBI file for the &lt;code&gt;Person&lt;/code&gt; model. RBI files can be generated using the command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bin/tapioca dsl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Executing this command generates several RBI files, including &lt;code&gt;person.rbi&lt;/code&gt;, which defines methods available to the &lt;code&gt;Person&lt;/code&gt; class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# typed: true

# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `Book`.
# Please instead update this file by running `bin/tapioca dsl Book`.

class Person
  include GeneratedAttributeMethods
  extend CommonRelationMethods
  extend GeneratedRelationMethods

  # Other methods related to Person class are stated here...

  module GeneratedAssociationRelationMethods
    sig { returns(PrivateAssociationRelation) }
    def all; end

    # Additional methods related to associations are stated here...
  end

  # More methods related to Person class are stated here...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the RBI file in place, the error disappears, as Sorbet now recognizes all methods available to &lt;code&gt;Person&lt;/code&gt;. Any additions or changes to methods in &lt;code&gt;Person&lt;/code&gt; can be reflected in the RBI file by rerunning the &lt;code&gt;bin/tapioca dsl&lt;/code&gt; command.&lt;/p&gt;
&lt;h2&gt;Challenges with Sorbet&lt;/h2&gt;
&lt;p&gt;Gradually adding types to a codebase often leads to a mixed scenario where some parts are typed, and others aren&amp;#39;t. Sorbet helps by detecting errors during runtime with its &lt;code&gt;sorbet-runtime&lt;/code&gt; component. This is particularly valuable in identifying situations where types might be incorrect, such as when passing different types from an untyped section of code while expecting a specific type in a typed portion.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# typed: true
require &amp;#39;sorbet-runtime&amp;#39;

class Person
  extend T::Sig

  def self.happy?
    true
  end

  sig {params(statement: String).returns(T::Boolean)}
  def self.the_truth?(statement)
    statement.length &amp;gt; 9 ? true : false
  end
end

Person.the_truth?(Person.happy?)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;bundle exec srb&lt;/code&gt; doesn&amp;#39;t raise an error here because it&amp;#39;s unaware of the &lt;code&gt;happy?&lt;/code&gt; return type, which prevents it from accurately assessing whether the passed value is not a string. However, Sorbet runtime detects this issue and raises an error during runtime without the execution of the &lt;code&gt;the_truth?&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;Parameter &amp;#39;statement&amp;#39;: Expected type String, got type TrueClass (TypeError)
Caller: person.rb:37
Definition: person.rb:31
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This runtime feedback is invaluable for correcting our code and making necessary adjustments. Yet, it&amp;#39;s essential to recognize that Sorbet runtime incurs a performance overhead, which may not be suitable for all production environments. According to the &lt;a href=&quot;https://sorbet.org/docs/runtime&quot;&gt;Sorbet documentation on runtime&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[...]in some cases, especially when calling certain methods in tight loops or other latency-sensitive paths, the overhead of even doing
the checks (regardless of what happens on failure) is prohibitively expensive. To handle these cases, Sorbet offers &lt;code&gt;.checked(...)&lt;/code&gt; which
declares in what environments a sig should be checked.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sorbet provides various mechanisms to disable these runtime checks when needed. One such mechanism can be configured in &lt;code&gt;application.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;T::Configuration.default_checked_level = :tests # where :tests is the environment, it can also be set to :never
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For further details on Sorbet runtime checks, refer to the &lt;a href=&quot;https://sorbet.org/docs/runtime&quot;&gt;Sorbet runtime documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ve looked at Sorbet, exploring its fundamental workings, benefits, and some considerations regarding performance. Sorbet stands as a robust tool for type checking in Ruby, offering the flexibility of gradual adoption. Its vibrant ecosystem and ease of implementation make it an invaluable asset for Ruby developers seeking to enhance code reliability and maintainability.&lt;/p&gt;
&lt;p&gt;For further exploration, consider diving into the resources provided below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stripe.com/blog/sorbet-stripes-type-checker-for-ruby&quot;&gt;Sorbet: Stripe’s type checker for Ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learnxinyminutes.com/docs/sorbet/&quot;&gt;Learn Sorbet in Y minutes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Gdx6by6tcvw&quot;&gt;Gradual typing for Ruby at Scale with Sorbet (video)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sorbet.org/docs&quot;&gt;Sorbet documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby on Rails 7.1: Partial Strict Locals and Their Gotchas</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/09/11/ruby-on-rails-7-1-partial-strict-locals-and-their-gotchas.html"/>
    <id>https://blog.appsignal.com/2024/09/11/ruby-on-rails-7-1-partial-strict-locals-and-their-gotchas.html</id>
    <published>2024-09-11T00:00:00+00:00</published>
    <updated>2024-09-11T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Rails partials have been around for years, but now strict locals have entered the scene to make things easier for us.</summary>
    <content type="html">&lt;p&gt;Rails partials have been around for years, but they can be clunky since they&amp;#39;re just ERB snippets without a backing object structure.&lt;/p&gt;
&lt;p&gt;Recently, libraries like ViewComponent and Phlex have tried to improve the view layer by adding more semantic structure to the templates. These are great libraries and I personally reach for &lt;code&gt;ViewComponent&lt;/code&gt; on almost every project I work on. That said, I still feel the humble Rails partial still works great for many use cases.&lt;/p&gt;
&lt;p&gt;The Rails team is always trying to improve its offering, so let&amp;#39;s look at a new feature introduced in Rails 7.1: strict locals!&lt;/p&gt;
&lt;h2&gt;Rails Partials and Local Variables&lt;/h2&gt;
&lt;p&gt;We can pass in arbitrary local variables into Rails partials and they&amp;#39;re magically available for use.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/application/_badge.html.erb %&amp;gt;

&amp;lt;ui-badge&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;%= title %&amp;gt;
    &amp;lt;%= tag.i class: icon %&amp;gt;
  &amp;lt;/p&amp;gt;
&amp;lt;/ui-badge&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This isn&amp;#39;t ideal, because it&amp;#39;s tricky to ascertain which variables a partial accepts if there&amp;#39;s a large block of markup. Also, since variables are dynamically created, a missing variable won&amp;#39;t return &lt;code&gt;nil&lt;/code&gt; but will cause an error.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say the &lt;code&gt;icon&lt;/code&gt; was optional in the above partial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/application/_badge.html.erb %&amp;gt;

&amp;lt;ui-badge&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;%= title %&amp;gt;
    &amp;lt;%= tag.i class: icon if icon.present? %&amp;gt;
  &amp;lt;/p&amp;gt;
&amp;lt;/ui-badge&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We render it as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= render &amp;quot;application/badge&amp;quot;, title: &amp;quot;New&amp;quot; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That will raise an &lt;code&gt;ActionView::Template::Error&lt;/code&gt;, with the message &lt;code&gt;undefined local variable or method &amp;#39;icon&amp;#39; for #&amp;lt;ActionView::Base:0x00000000023078&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The fact that &lt;code&gt;icon&lt;/code&gt; is optional is not obvious to a reader either. Until Rails 7.1, the solution was to inspect the &lt;code&gt;local_assigns&lt;/code&gt; hash which contains all injected local variables.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/application/_badge.html.erb %&amp;gt;

&amp;lt;% icon ||= local_assigns[:icon] %&amp;gt;
&amp;lt;ui-badge&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;%= title %&amp;gt;
    &amp;lt;%= tag.i class: icon if icon.present? %&amp;gt;
  &amp;lt;/p&amp;gt;
&amp;lt;/ui-badge&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This fixes the above error because the &lt;code&gt;icon&lt;/code&gt; variable is now always set.&lt;/p&gt;
&lt;p&gt;Rails 7.1 attempts to address the shortcomings of local variables in partials with the concept of &lt;em&gt;&lt;strong&gt;strict locals&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;What Are Strict Locals in Rails?&lt;/h2&gt;
&lt;p&gt;Starting in Rails 7.1, we can &lt;a href=&quot;https://guides.rubyonrails.org/action_view_overview.html#strict-locals&quot;&gt;write a magic comment&lt;/a&gt; at the top of partials, which defines the local variables it accepts and any default values for them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/application/_badge.html.erb %&amp;gt;

&amp;lt;%# locals: (title:, icon: nil) %&amp;gt;

&amp;lt;ui-badge&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;%= title %&amp;gt;
    &amp;lt;%= tag.i class: icon if icon.present? %&amp;gt;
  &amp;lt;/p&amp;gt;
&amp;lt;/ui-badge&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails parses this comment and sets the defined variables in the partial. Now it&amp;#39;s plainly obvious to a reader which variables the partial accepts and any default values.&lt;/p&gt;
&lt;p&gt;The partial should still render as shown above. Try excluding the &lt;code&gt;title&lt;/code&gt; variable from the call site and see what happens:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= render &amp;quot;application/badge&amp;quot; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You get an &lt;code&gt;ArgumentError&lt;/code&gt; with the message &lt;code&gt;missing local: :title&lt;/code&gt;: a more descriptive error message than before!&lt;/p&gt;
&lt;h2&gt;When to Use Strict Locals&lt;/h2&gt;
&lt;p&gt;Strict locals can be fiddly, so it&amp;#39;s not always a good idea to use them, especially in legacy apps.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take a use case that&amp;#39;s perfect for strict locals. Say we have a partial showing a user&amp;#39;s details in a &lt;em&gt;card&lt;/em&gt;, but only admins can see when they last signed in:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;ui-card&amp;gt;
  &amp;lt;dl&amp;gt;
    &amp;lt;dt&amp;gt;Name&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= name %&amp;gt;&amp;lt;/dd&amp;gt;

    &amp;lt;dt&amp;gt;Email&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= email %&amp;gt;&amp;lt;/dd&amp;gt;

    &amp;lt;% if last_signed_in %&amp;gt;
      &amp;lt;dt&amp;gt;Last signed in at&amp;lt;/dt&amp;gt;
      &amp;lt;dd&amp;gt;&amp;lt;%= last_signed_in %&amp;gt;&amp;lt;/dd&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/dl&amp;gt;
&amp;lt;/ui-card&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This partial takes several variables and has an optional variable, making it a perfect case for strict locals. Adding the following magic comment to the file makes it significantly easier to skim:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# locals: (name:, email:, last_signed_in: nil) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the case of a model&amp;#39;s associated partial (e.g. &lt;code&gt;posts/_post.html.erb&lt;/code&gt;), it&amp;#39;s fairly obvious that such a partial accepts a single &lt;code&gt;post&lt;/code&gt; variable. I don&amp;#39;t think strict locals are necessarily a good idea for these cases.&lt;/p&gt;
&lt;h2&gt;Strict Locals Gotchas&lt;/h2&gt;
&lt;p&gt;There are a couple of gotchas to be wary of, which is why I don&amp;#39;t recommend using strict locals across the board.&lt;/p&gt;
&lt;h3&gt;Implicitly Rendering a Collection&lt;/h3&gt;
&lt;p&gt;When implicitly rendering a collection:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= render @posts %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails injects the &lt;code&gt;_post&lt;/code&gt; partial with two variables in addition to the &lt;code&gt;post&lt;/code&gt; variable. Before Rails 7.1.2, these needed to be manually defined to prevent an error:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/posts/_post.html.erb %&amp;gt;

&amp;lt;%# locals: (post:, post_counter:, post_iteration:) %&amp;gt;
&amp;lt;%# ... %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This problem was &lt;a href=&quot;https://github.com/rails/rails/pull/49782&quot;&gt;fixed in Rails 7.1.2&lt;/a&gt; and these variables are now optional (but it&amp;#39;s good to know they exist if needed)!&lt;/p&gt;
&lt;h3&gt;Broadcasting Over ActionCable&lt;/h3&gt;
&lt;p&gt;When broadcasting a partial over ActionCable from an ActiveRecord callback:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Comment &amp;lt; ApplicationRecord
  after_create_commit -&amp;gt; {
    broadcast_append_later_to(
      post,
      target: dom_id(post, :comments)
    )
  }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You need to specify a &lt;code&gt;request_id&lt;/code&gt; local variable defaulting to &lt;code&gt;nil&lt;/code&gt;, as this is used under the hood by Rails.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/comments/_comment.html.erb %&amp;gt;

&amp;lt;%# locals: (comment:, request_id:) %&amp;gt;
&amp;lt;%# ... %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not doing so results in an error when the callback is triggered.&lt;/p&gt;
&lt;p&gt;And that&amp;#39;s our whistle-stop tour done!&lt;/p&gt;
&lt;h2&gt;A Few Things to Keep in Mind&lt;/h2&gt;
&lt;p&gt;Adding strict locals to existing partials is an &lt;em&gt;all-or-nothing&lt;/em&gt; proposition for a given file. We can&amp;#39;t incrementally add variables to a magic comment as we find them. As shown in the above gotchas, it can be tricky to ascertain all the variables that need to be declared in the magic comment.&lt;/p&gt;
&lt;p&gt;Given how much easier it is to reason about code with strict locals, I think they&amp;#39;re worth using extensively in new apps (and progressively adding retroactively to legacy apps).&lt;/p&gt;
&lt;p&gt;It&amp;#39;s important to ensure your test suite in legacy apps covers the partials to which you add strict locals. An omission will cause an exception, so you need to be sure you&amp;#39;ve tested all use cases before deploying changes.&lt;/p&gt;
&lt;p&gt;While I&amp;#39;m not a huge fan of the &lt;em&gt;magic comment&lt;/em&gt; approach (as I believe comments should never have functionality), I think strict locals are a great addition to Rails. It&amp;#39;s always been fiddly to assign locals to a partial and these do make it easier. Just beware of the gotchas!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we took a quick look at strict locals in Rails 7.1. We saw how partials and local variables work before exploring when to use strict locals and some gotchas.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Squash Your Ruby and Rails Bugs Faster</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/08/21/squash-your-ruby-and-rails-bugs-faster.html"/>
    <id>https://blog.appsignal.com/2024/08/21/squash-your-ruby-and-rails-bugs-faster.html</id>
    <published>2024-08-21T00:00:00+00:00</published>
    <updated>2024-08-21T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s look at a few readily available techniques and tools to gather and investigate issues in your Ruby or Rails app.</summary>
    <content type="html">&lt;p&gt;A bug in software can be disruptive, elusive, maddening, and
invasive. Indeed, a developer often needs the tenacity of Edison to find and
fix an issue.&lt;/p&gt;
&lt;p&gt;But grit isn&amp;#39;t the only asset a developer requires. One also needs
information to debug code: What are the symptoms and effects of the issue?
What is its frequency? Pervasiveness? Provenance? The evidence and artifacts
of a bug — a core dump, stack trace, log, or test case — are invaluable.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s explore a few techniques and tools readily available to Ruby and Rails
developers for gathering and investigating evidence of an issue. Data can&amp;#39;t
supplant tenacity, but it can help to ease your efforts (and spare you your sleep).&lt;/p&gt;
&lt;h2&gt;Use Environment Variables to Tune Your Tools in Rails&lt;/h2&gt;
&lt;p&gt;Rails provides some excellent tools to peer into a running application, including a configurable
&lt;a href=&quot;https://guides.rubyonrails.org/debugging_rails_applications.html#the-logger&quot;&gt;Logger&lt;/a&gt;
to capture diagnostics of its own and any that you&amp;#39;d like to add. Rails also
provides a verbose query log to pinpoint the origin of database queries and
a verbose enqueue log to illuminate where background jobs are queued.
The latter two logs are enabled by default in the &lt;code&gt;development&lt;/code&gt; environment;
you can enable both in other environments with two statements.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;##
# In any initializer... enable the verbose query log to see the file
# name and line number of the code initiating each query
ActiveRecord.verbose_query_logs = true

##
# In config/application.rb or any environment initializer, enable the
# queueing log to find where each job is enqueued
config.active_job.verbose_enqueue_logs = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A lesser-known log option available since Rails 7 annotates each SQL query
with comments. If you add the following to &lt;code&gt;config/application.rb&lt;/code&gt; or any environment file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;##
# Add to config/application.rb or any environment initializer file
config.active_record.query_log_tags_enabled = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your query log is automatically amended with the application name, controller
name, action name, or background job name. The following sample output
comes directly from &lt;a href=&quot;https://guides.rubyonrails.org/debugging_rails_applications.html&quot;&gt;Rails&amp;#39;s debugging guide&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Article Load (0.2ms)  SELECT &amp;quot;articles&amp;quot;.* FROM &amp;quot;articles&amp;quot;
/*application=&amp;#39;Blog&amp;#39;,controller=&amp;#39;articles&amp;#39;,action=&amp;#39;index&amp;#39;*/

Article Update (0.3ms)  UPDATE &amp;quot;articles&amp;quot; SET &amp;quot;title&amp;quot; = ?, &amp;quot;updated_at&amp;quot; = ? WHERE &amp;quot;posts&amp;quot;.&amp;quot;id&amp;quot; = ?
/*application=&amp;#39;Blog&amp;#39;,job=&amp;#39;ImproveTitleJob&amp;#39;*/
[[&amp;quot;title&amp;quot;, &amp;quot;Improved Rails debugging guide&amp;quot;], [&amp;quot;updated_at&amp;quot;, &amp;quot;2022-10-16 20:25:40.091371&amp;quot;], [&amp;quot;id&amp;quot;, 1]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You likely do not want to enable all these features in production. Log
generation can be costly in terms of memory and time (finite resources for an
application under load).&lt;/p&gt;
&lt;p&gt;However, there are exceptions. You may want to briefly
enable an on-demand feature, especially when debugging in
development and test modes. Rather than alter code (and perhaps redeploy) to
enable or disable a diagnostic, use environment variables to control state. In
some environments, such as Heroku, changing the setting of an environment
variable does not mandate a new deployment.&lt;/p&gt;
&lt;p&gt;For example, you can define a set of variables to control logging and verbosity.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable Name&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMENTED_QUERY_LOG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable comments in query log&lt;/td&gt;
&lt;td&gt;Non-blank value enables feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LOG_ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable all logging features&lt;/td&gt;
&lt;td&gt;Non-blank value enables feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LOG_LEVEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Control the threshold for log messages&lt;/td&gt;
&lt;td&gt;&lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;fatal&lt;/code&gt;, &lt;code&gt;unknown&lt;/code&gt; (from most verbose to least verbose order)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VERBOSE_ENQUEUE_LOG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Display where jobs are queued&lt;/td&gt;
&lt;td&gt;Non-blank value enables feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VERBOSE_QUERY_LOG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the origin of each Active Record query&lt;/td&gt;
&lt;td&gt;Non-blank value enables feature&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;For convenience, create a small class to query configuration settings.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;##
# Put this in a file such as config/initializers/env_configuration.rb
class EnvironmentConfiguration
  def self.enabled?(envvar)
    Rails.env.development? ||
      ENV.fetch(&amp;#39;LOG_ALL&amp;#39;, nil).present? ||
      ENV.fetch(envvar.to_s, nil).present?
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now use the variables and the code to set logging features in &lt;code&gt;config/application.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;##
# Centralize configuration of all logging features in config/application.rb
#
module MyApplication
  class Application &amp;lt; Rails::Application
    # . . .
    ActiveRecord.verbose_query_logs             = EnvironmentConfiguration.enabled?(&amp;#39;VERBOSE_QUERY_LOG&amp;#39;)
    config.active_job.verbose_enqueue_logs      = EnvironmentConfiguration.enabled?(&amp;#39;VERBOSE_ENQUEUE_LOG&amp;#39;)
    config.active_record.query_log_tags_enabled = EnvironmentConfiguration.enabled?(&amp;#39;COMMENTED_QUERY_LOG&amp;#39;)
    config.log_level = (ENV.fetch(&amp;#39;LOG_LEVEL&amp;#39;, nil).presence || &amp;#39;info&amp;#39;).to_sym
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use your shell, a dotenv file, your continuous integration, or hosting and
deployment platform to set each option. You can also use a feature on demand.
Simply start the application with the environment variable defined on the
command line.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;LOG_ALL=on LOG_LEVEL=debug bundle exec rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tune Puma for Development&lt;/h3&gt;
&lt;p&gt;Building on the previous section, let&amp;#39;s look at how to use environment
variables to tailor your Puma configuration for debugging in the development
environment.&lt;/p&gt;
&lt;p&gt;Typically, Puma is configured to maximize throughput in production, running
multiple workers and many threads per worker. In development, you want
the opposite: one worker, one thread, and a very long timeout
to allow for interactive debugging. Each of those is a parameter you can tune.&lt;/p&gt;
&lt;p&gt;Alter &lt;code&gt;config/puma.rb&lt;/code&gt; to reflect the following code.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;##
# Control thread count with environment variables.
# Each environment setting, if set, is always a string. Use `Integer` to
# convert the string to an integer value.
#
max_threads_count = Integer ENV.fetch(&amp;#39;PUMA_MAX_THREADS&amp;#39;, 5)
min_threads_count = Integer ENV.fetch(&amp;#39;PUMA_MIN_THREADS, max_threads_count)
threads min_threads_count, max_threads_count

##
# Specify the duration Puma waits before terminating a worker.
# The default is 30 seconds.
#
worker_timeout Integer ENV.fetch(&amp;#39;PUMA_WORKER_TIMEOUT&amp;#39;, 30)

##
# Set the number of workers
worker_count = Integer ENV.fetch(&amp;#39;PUMA_WEB_CONCURRENCY, 2)
workers worker_count
preload_app! if worker_count.positive?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now configure the three environment variables to control
Puma in each environment. In development, set the values to optimize
for interactive debugging.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export PUMA_MAX_THREADS=1
export PUMA_WORKERS=0
export PUMA_WEB_CONCURRENCY=0
export PUMA_WORKER_TIMEOUT=3600
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to validate your Puma settings, set the environment variable
&lt;code&gt;PUMA_LOG_CONFIG=true&lt;/code&gt; and start your application. Puma emits its
active configuration on startup.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;=&amp;gt; Booting Puma
=&amp;gt; Rails 6.1.7.7 application starting in development
=&amp;gt; Run `bin/rails server --help` for more startup options
Configuration:
. . .
- environment: development
- max_threads: 5
- min_threads: 5
- worker_timeout: 3600
- workers: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Coincidentally, the &lt;a href=&quot;https://github.com/rails/rails/blob/main/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt&quot;&gt;default Puma configuration&lt;/a&gt; in the most recent Rails release
is similar to what&amp;#39;s shown here (thanks to Nate Matykiewicz for the tip).&lt;/p&gt;
&lt;h3&gt;Run Background Jobs Inline&lt;/h3&gt;
&lt;p&gt;A Rails application of any complexity typically leverages background jobs to
run compute-intensive and (relatively) long-running tasks. Background jobs
run asynchronously, disconnected from the request/response cycle. Ideal candidates
for &amp;quot;out of band&amp;quot; processing include generating
reports, sending emails, and interacting with third-party APIs. But the asynchronous nature of jobs also complicates
debugging.&lt;/p&gt;
&lt;p&gt;To simplify troubleshooting, run jobs &lt;em&gt;immediately&lt;/em&gt; in your local development and test
environments. In immediate mode, jobs are not queued, but instead execute instantaneously.
Running in the &amp;quot;foreground&amp;quot;, you can set breakpoints and inspect state interactively.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at an example using &lt;a href=&quot;https://github.com/collectiveidea/delayed_job&quot;&gt;Delayed Job&lt;/a&gt;,
a popular and easy-to-manage queueing backend for
&lt;a href=&quot;https://guides.rubyonrails.org/active_job_basics.html#&quot;&gt;Active Job&lt;/a&gt;. Delayed Job
provides a setting to enable queueing. By default, the setting is &lt;code&gt;true&lt;/code&gt; and jobs
are queued as per usual. However, if set to &lt;code&gt;false&lt;/code&gt;, jobs run immediately.&lt;/p&gt;
&lt;p&gt;Add the following code to your application in &lt;code&gt;config/initializers/delayed_job.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Delayed::Worker.delay_jobs = ENV.fetch(&amp;#39;DELAYED_JOBS_DISABLE_JOB_QUEUES&amp;#39;, false).blank?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES&lt;/code&gt; is set to any value, queueing is disabled. If the environment
variable is blank or not defined, queueing is enabled.&lt;/p&gt;
&lt;p&gt;Next, in your shell, on the command line, or in your dot files, set
&lt;code&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES&lt;/code&gt; as needed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Set the variable in the current shell
export DELAYED_JOBS_DISABLE_JOB_QUEUES=true
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Set the variable only for the current command
DELAYED_JOBS_DISABLE_JOB_QUEUES=true rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# In .env.development, .env.local, and/or .env.test, add the line...
DELAYED_JOBS_DISABLE_JOB_QUEUES=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Set the environment variable to nothing or delete the environment variable to restore queueing.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Set the variable to blank
export DELAYED_JOBS_DISABLE_JOB_QUEUES=

# Remove the variable from the environment
unset DELAYED_JOBS_DISABLE_JOB_QUEUES
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are no rules for naming environment variables. Choose names meaningful to you.
One helpful convention to categorize variables is to add a package name
as a prefix, such as &lt;code&gt;PUMA_&lt;/code&gt; and &lt;code&gt;DELAYED_JOB_&lt;/code&gt;. The former indicates variables
affecting Puma; the latter connotes variables used by Delayed Job.&lt;/p&gt;
&lt;h3&gt;Peer Into Network Calls&lt;/h3&gt;
&lt;p&gt;Like background jobs, Rails applications can also consume application
programming interfaces (APIs). APIs provide access to external services,
such as GraphQL databases, e-commerce transactions, and public data sources.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s another example to conditionally emit HTTP requests and responses
from the &lt;code&gt;Net::HTTP&lt;/code&gt; library. If the environment variable &lt;code&gt;DEBUG_HTTP&lt;/code&gt; is set
to any non-blank value, the outgoing request and inbound responses are
printed to &lt;code&gt;STDOUT&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def construct_uri(hostname:, path:, port:, params: {}, protocol: &amp;#39;https&amp;#39;)
  target =
    Addressable::URI.new.tap do |uri|
      uri.hostname     = hostname
      uri.path         = path
      uri.port         = port
      uri.query_values = params
      uri.scheme       = protocol
    end

  URI.parse target
end

def debug?
  ENV.fetch[&amp;#39;DEBUG_HTTP&amp;#39;].present?
end

def request
  uri = construct_uri(...)

  Net::HTTP.new(uri.host, uri.port).tap do |http|
    http.set_debug_output($stdout) if debug?
    http.use_ssl = uri.scheme == &amp;quot;https&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see the network activity, simply start the application with
the environment variable defined on the command line.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;DEBUG_HTTP=on bundle exec rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;debug?&lt;/code&gt; method provides some abstraction from the actual
flag implementation. It, and the rest of this code, can form
a &lt;a href=&quot;https://gist.github.com/martinstreicher/334e24943a51495dbddd67901ed29b51&quot;&gt;base class for all your network requests, as shown in this gist taken from production code&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Add Ruby Gems to Your Toolbox&lt;/h2&gt;
&lt;p&gt;The universe of Ruby gems is vast: it&amp;#39;s impossible to cover all the great gems
available for debugging. Instead, let&amp;#39;s look at a few tools you can add today
for an instant boost. You&amp;#39;ll likely add each of these to all your projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/awesome-print/awesome_print&quot;&gt;&lt;code&gt;awesome_print&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;http://tableprintgem.com&quot;&gt;&lt;code&gt;table_print&lt;/code&gt;&lt;/a&gt; create rich, readable inspections of
Ruby data structures, including Active Record models. You can use either gem
in code or from the console.&lt;/p&gt;
&lt;p&gt;Here is an example of a model emitted by &lt;code&gt;awesome_print&lt;/code&gt; in the console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;gt; ap User.find_by(email: &amp;#39;george@bigbrother.gov&amp;#39;)
#&amp;lt;User:0x00000001163482f0&amp;gt; {
       :account_balance =&amp;gt; 0.0,
          :confirmed_at =&amp;gt; Mon, 11 Mar 2024 12:29:30.000000000 EDT -04:00,
            :created_at =&amp;gt; Mon, 11 Mar 2024 12:29:30.000000000 EDT -04:00,
                 :email =&amp;gt; &amp;quot;george@bigbrother.gov&amp;quot;,
    :encrypted_password =&amp;gt; &amp;quot;992c45d528f9b445de3d6653e14d3e6165171944&amp;quot;,
            :first_name =&amp;gt; &amp;quot;George&amp;quot;,
             :last_name =&amp;gt; &amp;quot;Orwell&amp;quot;,
         :sign_in_count =&amp;gt; 0,
                 :state =&amp;gt; &amp;quot;active&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of &lt;code&gt;p&lt;/code&gt;, use &lt;code&gt;ap&lt;/code&gt; to show the enhanced output. &lt;em&gt;awesome_print&lt;/em&gt; lists
attributes in alphabetical order, one attribute per line, among other niceties.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/BetterErrors/better_errors&quot;&gt;&lt;code&gt;better_errors&lt;/code&gt;&lt;/a&gt; replaces the
standard Rails error page with an enhanced stack backtrace, parameter list, and an
interactive console where you can probe the stack frame and variables at
the scene of the exception. You can also tie source code links to your favorite
editor. Here is code to tie &lt;code&gt;better_errors&lt;/code&gt; to Visual Studio Code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if Rails.env.development?
BetterErrors.editor =
  if (ENV[&amp;#39;BETTER_ERRORS_EDITOR&amp;#39;].presence || ENV[&amp;#39;EDITOR&amp;#39;]) =~ /vscode/i
    proc { |file, line| &amp;quot;vscode://file#{file}:#{line}&amp;quot; }
  else
    :macvim
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;export BETTER_ERRORS_EDITOR=vscode&lt;/code&gt; in your shell and restart the Rails server.
Now links to files open automatically in Visual Studio.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep the &lt;code&gt;faker&lt;/code&gt; and &lt;code&gt;factory_bot&lt;/code&gt; gems (or their alternatives) in
both the testing and development groups in &lt;em&gt;Gemfile&lt;/em&gt;. Both are invaluable if you
need to generate data quickly in the console.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;group :development, :test do
  # other gems
  # ...
  gem &amp;#39;factory_bot&amp;#39;
  gem &amp;#39;faker&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One more idea: If you use AppSignal as your application monitoring tool,
it has features to interpret your logs and pinpoint the source of errors.
See &lt;a href=&quot;https://blog.appsignal.com/2022/09/21/debugging-in-ruby-with-appsignal.html&quot;&gt;Debugging in Ruby with AppSignal&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2&gt;Debugging is a Skill&lt;/h2&gt;
&lt;p&gt;Debugging code is something of an art, and like any creative endeavor, the
more practice you have, the better you become.&lt;/p&gt;
&lt;p&gt;Debug while pairing with another developer,
especially an experienced developer. If your partner is willing, think out loud together
and exchange hunches and insights.&lt;/p&gt;
&lt;p&gt;Go forth and hack!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to HTTP Caching in Ruby On Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/08/14/an-introduction-to-http-caching-in-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2024/08/14/an-introduction-to-http-caching-in-ruby-on-rails.html</id>
    <published>2024-08-14T00:00:00+00:00</published>
    <updated>2024-08-14T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll explore the basics of HTTP caching, cache layers, configuration, and how to cache in Rails.</summary>
    <content type="html">&lt;p&gt;It&amp;#39;s 2024, and the HyperText Transfer Protocol (HTTP) is 35 years old. The fact that the vast majority of web traffic still relies on this simple, stateless form of communication is a marvel in itself.&lt;/p&gt;
&lt;p&gt;A first set of content retrieval optimizations were added to the protocol when v1.0 was &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2616#section-9.3&quot;&gt;published in 1996&lt;/a&gt;. These include the infamous caching instructions (aka headers) that the client and server use to negotiate whether content needs refreshing.&lt;/p&gt;
&lt;p&gt;28 years later, many web developers still avoid using it like the plague. A great deal of traffic and server load (and thus, latency) can be avoided, though, by using the built-in caching mechanism of the web. Not only does this result in a greatly improved user experience, it can also be a powerful lever to trim down your server bill. In this post, we&amp;#39;ll take a look at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The basic concepts around HTTP caching.&lt;/li&gt;
&lt;li&gt;What cache layers we can leverage.&lt;/li&gt;
&lt;li&gt;How web caching is configured and controlled.&lt;/li&gt;
&lt;li&gt;How simple it is to cache in Ruby on Rails.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Concepts&lt;/h2&gt;
&lt;p&gt;Before we dive deep into the mechanics of web caching, let&amp;#39;s get some definitions out of the way.&lt;/p&gt;
&lt;h3&gt;Fresh Vs. Stale&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s assume we have a shared cache server in place that stores responses from our app server for reuse. Later, a client issues a request to our app server. The stored response in our cache is now in one of two states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fresh:&lt;/strong&gt; The response is still valid (we&amp;#39;ll see what that means in a second) and will be used to fulfill the request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stale:&lt;/strong&gt; The response isn&amp;#39;t valid anymore. A new response has to be calculated and served by the upstream server.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;HTTP Cache Layers&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://httpwg.org/specs/rfc9111.html&quot;&gt;HTTP Caching spec&lt;/a&gt; defines two types of cache — &lt;em&gt;shared&lt;/em&gt; and &lt;em&gt;private&lt;/em&gt;.&lt;/p&gt;
&lt;h4&gt;Shared Caches&lt;/h4&gt;
&lt;p&gt;Shared caches store responses that are reusable among a group of users. Typically, shared caches are implemented at &lt;em&gt;intermediaries&lt;/em&gt;, e.g., Nginx, Caddy, or other reverse proxies. Of course, commercial edge caches like Cloudflare or Fastly also implement shared caches.&lt;/p&gt;
&lt;p&gt;Another flavor of shared caches can be implemented in service workers using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Cache&quot;&gt;Cache API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can control which responses are eligible for caching with the &lt;code&gt;Cache-Control&lt;/code&gt; header.&lt;/p&gt;
&lt;h4&gt;Private Caches&lt;/h4&gt;
&lt;p&gt;Private caches are allotted to a single user. You are most likely to encounter a private cache as a &lt;em&gt;browser&lt;/em&gt; component. The main feature here is that the stored responses are not shared with other clients. Any content that the server renders containing personalized data must &lt;em&gt;only&lt;/em&gt; be stored in a private cache. In general, this will be the case for any authenticated route in your Rails app, but there might be exceptions.&lt;/p&gt;
&lt;p&gt;You can force a response to be cached privately using the &lt;code&gt;Cache-Control: private&lt;/code&gt; header.&lt;/p&gt;
&lt;h2&gt;Controlling Caching Via the &lt;code&gt;Cache-Control&lt;/code&gt; Header&lt;/h2&gt;
&lt;p&gt;Although, historically, other headers have been in use (like &lt;code&gt;Expires&lt;/code&gt; or &lt;code&gt;Pragma&lt;/code&gt;), we will focus on the &lt;code&gt;Cache-Control&lt;/code&gt; header here. The &lt;a href=&quot;https://httpwg.org/specs/rfc9111.html#field.cache-control&quot;&gt;specification&lt;/a&gt; has an exhaustive description, but we&amp;#39;ll focus on the salient parts.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Cache-Control&lt;/code&gt; header sets one or several comma-separated &lt;em&gt;directives&lt;/em&gt; that can be further divided into &lt;em&gt;request&lt;/em&gt; and &lt;em&gt;response&lt;/em&gt; directives.&lt;/p&gt;
&lt;h3&gt;Request Directives&lt;/h3&gt;
&lt;p&gt;When requesting a resource from the server, the client (browser) can specify one of the following directives to negotiate how caching should behave:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;no-cache&lt;/code&gt; - Advises the cache to revalidate the response against the origin server. Typically this happens when you force reload a page or disable caching in the browser&amp;#39;s developer tools.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no-store&lt;/code&gt; - Indicates that no cache must store any part of this request or any response.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max-age&lt;/code&gt; - In seconds, indicates the timespan for which a response from the server is considered fresh. For example, &lt;code&gt;Cache-Control: max-age=3600&lt;/code&gt; would tell the server that any response older than an hour cannot be reused.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Response Directives&lt;/h3&gt;
&lt;p&gt;Essentially, response directives consist of what&amp;#39;s above, with different semantics, plus a few more. A cache must obey these directives coming from the origin server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;no-cache&lt;/code&gt; - A bit counterintuitively, this directive &lt;em&gt;does not&lt;/em&gt; imply that the response cannot be cached. Rather, each cache &lt;em&gt;must revalidate&lt;/em&gt; the response with the origin server for each reuse. This is the &lt;a href=&quot;https://github.com/rails/rails/blob/967fc62a9432aaf2f6ed5a92bf1ed1c9c7310651/actionpack/lib/action_controller/metal/live.rb#L179&quot;&gt;normal case&lt;/a&gt; for a response from a Rails application.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no-store&lt;/code&gt; - No cache of any type (shared or private) must store this response.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max-age&lt;/code&gt; - Indicates the period into the future for which the response should count as &lt;em&gt;fresh&lt;/em&gt;. So, &lt;code&gt;Cache-Control: max-age=3600&lt;/code&gt; would specify a period of one hour from the moment of generation on the origin server. Afterwards, a cache cannot reuse it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;private&lt;/code&gt; - This directive specifies that the response can only be stored in a &lt;em&gt;private&lt;/em&gt; cache (i.e., the browser). This should be set for any user-personalized content, especially when a session cookie is required to access a resource.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public&lt;/code&gt; - Indicates that a response can be stored in a shared cache.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;must-revalidate&lt;/code&gt; - An edge case worth noting: HTTP allows the reuse of stale responses when caches are disconnected from the origin server. This directive prevents that and forces a fresh response or a 504 gateway timeout error.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;(In)validation&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s say a response has become &lt;em&gt;stale&lt;/em&gt; for some reason (it&amp;#39;s expired, for example). Even so, there&amp;#39;s a chance that it is still &lt;em&gt;valid&lt;/em&gt;, but we have to ask the origin server if this is the case. This process is called &lt;strong&gt;validation&lt;/strong&gt; and is performed using a &lt;strong&gt;conditional request&lt;/strong&gt; in one of two ways: by expiration or based on the response content.&lt;/p&gt;
&lt;h3&gt;By Expiration&lt;/h3&gt;
&lt;p&gt;The client sends an &lt;code&gt;If-Modified-Since&lt;/code&gt; header in the request, and the cache uses this header to determine if the respective response has to be revalidated. Let&amp;#39;s consider this response.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK
...
Date: Fri, 29 Mar 2024 10:30:00 GMT
Last-Modified: Fri, 29 Mar 2024 10:00:00 GMT
Cache-Control: max-age=3600
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It was retrieved at 10:30 and is stored on the client. The combination of &lt;code&gt;Last-Modified&lt;/code&gt; and &lt;code&gt;max-age&lt;/code&gt; tells us that this response becomes stale at 11:30. If the client sends a request at 11:31, it includes an &lt;code&gt;If-Modified-Since&lt;/code&gt; header:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-Modified-Since: Fri, 29 Mar 2024 10:00:00 GMT
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The server now calculates the content of the response and, if it hasn&amp;#39;t changed, sends a &lt;code&gt;304 Not Modified&lt;/code&gt; response:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 304 Not Modified
...
Date: Fri, 29 Mar 2024 11:31:00 GMT
Last-Modified: Fri, 29 Mar 2024 10:00:00 GMT
Cache-Control: max-age=3600
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because it&amp;#39;s a redirect, this response has no payload. It merely tells the client that the stale response has been &lt;strong&gt;revalidated&lt;/strong&gt; and can revert to a &lt;em&gt;fresh&lt;/em&gt; state again. Its new expiry time is now 12:31.&lt;/p&gt;
&lt;h3&gt;Based On the Response Content&lt;/h3&gt;
&lt;p&gt;Timing is a problematic matter: servers and clients can drift out of sync or file system timestamps may not be appropriate. This is solved by using an &lt;code&gt;ETag&lt;/code&gt; header. This can be an arbitrary value, but most frequently, it is a digest of the response body. Picking up the example from above, we swap &lt;code&gt;Last-Modified&lt;/code&gt; for an &lt;code&gt;ETag&lt;/code&gt; header:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK
...
Date: Fri, 29 Mar 2024 10:30:00 GMT
ETag: &amp;quot;12345678&amp;quot;
Cache-Control: max-age=3600
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this response is stored in a private cache and becomes stale, the client now uses the last known &lt;code&gt;ETag&lt;/code&gt; value and asks the server to revalidate it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-None-Match: &amp;quot;12345678&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, the server will return a &lt;code&gt;304 Not Modified&lt;/code&gt; response if the values of a freshly computed &lt;code&gt;ETag&lt;/code&gt; and the requested &lt;code&gt;If-None-Match&lt;/code&gt; header match. Otherwise, it will respond with &lt;code&gt;200 OK&lt;/code&gt; and a new version of the content.&lt;/p&gt;
&lt;p&gt;I will not go into the details here, but &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#strong-v-s-weak-etags&quot;&gt;the Rails docs explain weak vs. strong ETags&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Implementation In Rails&lt;/h2&gt;
&lt;p&gt;We&amp;#39;re now ready to implement this knowledge in a Rails app. The relevant module is wired into &lt;code&gt;ActionController&lt;/code&gt; and is called &lt;a href=&quot;https://api.rubyonrails.org/classes/ActionController/ConditionalGet.html&quot;&gt;&lt;code&gt;ActionController::ConditionalGet&lt;/code&gt;&lt;/a&gt;. Let&amp;#39;s examine the interface it uses to emit the &lt;code&gt;Cache-Control&lt;/code&gt; directives discussed above.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;expires_in&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This method sets the &lt;code&gt;max-age&lt;/code&gt; directive, overwriting all others. You can pass it &lt;code&gt;ActiveSupport::Duration&lt;/code&gt; and &lt;a href=&quot;https://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-expires_in&quot;&gt;options such as &lt;code&gt;public&lt;/code&gt; and &lt;code&gt;must_revalidate&lt;/code&gt;&lt;/a&gt;, which will set the respective directives.&lt;/p&gt;
&lt;p&gt;When would you want to use this? Typically, when you need to balance cache effectiveness and freshness. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ProductsController &amp;lt; ApplicationController
  def index
    # Set the response to expire in 15 minutes
    expires_in 15.minutes, public: true

    @products = Product.all
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This exemplifies the compromise you might make between the probability that a new product is added, changed, or removed in the course of 15 minutes and somebody seeing a stale response. Every application will have its own limitations here, but it&amp;#39;s a good idea to have application monitoring like &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;AppSignal for Ruby&lt;/a&gt; built into your production environment. This will enable you to query how often an endpoint is accessed by the same user vs the data manipulation frequency.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;expires_now&lt;/code&gt; and &lt;code&gt;no_store&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This will set &lt;code&gt;Cache-Control&lt;/code&gt; to &lt;code&gt;no-cache&lt;/code&gt; and &lt;code&gt;no-store&lt;/code&gt;, respectively. Look above for the implications.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;http_cache_forever&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;http_cache_forever&lt;/code&gt; sets a &lt;code&gt;max-age&lt;/code&gt; of 100 years internally and caches the response. It will consult &lt;code&gt;stale?&lt;/code&gt;, though (see below), to determine if a fresh response should be rendered. You call it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class StaticController &amp;lt; ApplicationController
  def about
    http_cache_forever(public: true) do
      # Your logic to render the about page goes here
      render :about
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This renders the &lt;code&gt;about&lt;/code&gt; view and allows intermediary shared caches to store it (because &lt;code&gt;public&lt;/code&gt; is set in &lt;code&gt;Cache-Control&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;fresh_when&lt;/code&gt; and &lt;code&gt;stale?&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;These methods are siblings and are both concerned with setting appropriate &lt;code&gt;ETag&lt;/code&gt; and &lt;code&gt;Last-Modified&lt;/code&gt; headers. Thus, they form the heart of Rails&amp;#39; conditional GET implementation. Let&amp;#39;s look at each of them now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ArticlesController &amp;lt; ApplicationController
  def show
    @article = Article.find(params[:id])
    fresh_when(@article)
  end

  def index
    @articles = Articles.all
    fresh_when(@articles)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;show&lt;/code&gt; action exhibits the simplest way to enable conditional GET requests on an endpoint. If it&amp;#39;s passed an ActiveRecord instance, it will extract the &lt;code&gt;update_at&lt;/code&gt; timestamp, reuse it as &lt;code&gt;Last-Modified&lt;/code&gt;, and &lt;code&gt;ETag&lt;/code&gt; will be computed from the record as a &lt;a href=&quot;https://github.com/rails/rails/blob/30e37387249632cce382d7b7b7adc619855dc7d9/actionpack/lib/action_dispatch/http/cache.rb#L137-L138&quot;&gt;hex digest&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When working with relations, like in the &lt;code&gt;index&lt;/code&gt; action, &lt;code&gt;fresh_when&lt;/code&gt; will check for the most recent &lt;code&gt;updated_at&lt;/code&gt; in the collection using the &lt;code&gt;maximum&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;If desired, you can override this behavior using &lt;a href=&quot;https://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-fresh_when&quot;&gt;explicit options&lt;/a&gt;. These match the directives discussed above (the only outlier being the &lt;code&gt;template&lt;/code&gt; option, which allows you to specify the template used to calculate the &lt;code&gt;ETag&lt;/code&gt; digest). This is useful when your controller action uses a different template than the default.&lt;/p&gt;
&lt;p&gt;In either case, before rendering a response, &lt;code&gt;fresh_when&lt;/code&gt; will &lt;a href=&quot;https://github.com/rails/rails/blob/30e37387249632cce382d7b7b7adc619855dc7d9/actionpack/lib/action_controller/metal/conditional_get.rb#L149-L150&quot;&gt;check if a stored response is still fresh&lt;/a&gt; and return a 304 in this case.&lt;/p&gt;
&lt;p&gt;On the other hand, &lt;code&gt;stale?&lt;/code&gt; is a predicate method that falls back to &lt;code&gt;fresh_when&lt;/code&gt; internally and returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; based on its evaluation. You can use it to guard against expensive method calls, analogously to the above examples:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ArticlesController &amp;lt; ApplicationController
  def show
    @article = Article.find(params[:id])

    expires_in 15.minutes

    if stale?(@article)
      @article.refresh_counters!
      render @article
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, since it uses the same internal semantics as &lt;code&gt;fresh_when&lt;/code&gt;, it would automatically send a &lt;code&gt;304 Not Modified&lt;/code&gt; response unless the article is stale. If it is stale, though (i.e., if 15 minutes have passed since it was generated), the counters are refreshed and return a fresh response. Updating low-priority data in your responses only infrequently, based on a &amp;quot;timeout&amp;quot;, is a typical use case.&lt;/p&gt;
&lt;h3&gt;The &lt;code&gt;etag&lt;/code&gt; Class Method&lt;/h3&gt;
&lt;p&gt;How can caches, even private ones, deal with personalized information? The answer is that data identifying the session in some way has to be included in the &lt;code&gt;ETag&lt;/code&gt;. This is exactly what the &lt;code&gt;etag&lt;/code&gt; controller class method does: it provides the necessary information to differentiate between private responses to individual users.&lt;/p&gt;
&lt;p&gt;Continuing with the example above, imagine that some users are administrators who are served &amp;quot;edit&amp;quot; buttons for the articles in the UI. You don&amp;#39;t want that information to spill over to an anonymous user, so you can prevent that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ArticlesController &amp;lt; ApplicationController
  etag { current_user&amp;amp;.id }

  def show
    @article = Article.find(params[:id])
    fresh_when(@article)
  end

  def index
    @articles = Articles.all
    fresh_when(@articles)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming that &lt;code&gt;current_user&lt;/code&gt; is a method returning the currently logged-in user, his/her id is now added to the &lt;code&gt;ETag&lt;/code&gt; before digesting, preventing the leak of sensitive data to unauthorized visitors.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Given all we have learned above, do you feel confident adding HTTP caching to your controllers? I would cautiously assume that you still have some leftover anxiety regarding the leakage of sensitive data. But all in all, using Turbo Frames and personalizing a UI can provide some valuable insights.&lt;/p&gt;
&lt;h3&gt;Rigorously Decompose into Turbo Frames&lt;/h3&gt;
&lt;p&gt;If you are using Turbo in your frontend, you can leverage eager or lazy-loaded &lt;a href=&quot;https://turbo.hotwired.dev/reference/frames#eager-loaded-frame&quot;&gt;Turbo Frames&lt;/a&gt;. A lot of applications comprise personalized sections of the UI, while others do not. By detaching the non-personalized parts into separate endpoints that employ HTTP caching individually, you not only gain performance benefits but also a clearer application architecture.&lt;/p&gt;
&lt;h3&gt;Apply Personalized Bits of the UI After the Fact&lt;/h3&gt;
&lt;p&gt;Another way to deal with user-dependent data is to apply JavaScript after a page has loaded. This is only viable if the amount of personalized data on a page is small. In essence, you&amp;#39;d query an endpoint for a current user&amp;#39;s data in JSON format, then use a Stimulus controller to apply it to the relevant bits of the UI.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we demystified HTTP caching. We looked at some basic concepts, cache layers, and configuration, including how to use the &lt;code&gt;Cache-Control&lt;/code&gt; header and validation. We also examined the elegant solution that Rails provides in the form of the &lt;code&gt;ActionController::ConditionalGet&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;Until next time, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Nix for Ruby Developers</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/08/07/an-introduction-to-nix-for-ruby-developers.html"/>
    <id>https://blog.appsignal.com/2024/08/07/an-introduction-to-nix-for-ruby-developers.html</id>
    <published>2024-08-07T00:00:00+00:00</published>
    <updated>2024-08-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn how to build and share reproducible Rails development environments.</summary>
    <content type="html">&lt;p&gt;A predictable, stable environment (in terms of your operating system, system libraries, build
tools, and programming libraries) is essential to each
development step: from onboarding, to collaboration, continuous integration, quality
assurance, and deployment. Deviation can cause one-off, intermittent, and even
catastrophic failures.&lt;/p&gt;
&lt;p&gt;However, consistency can be elusive, even with the best intentions, best
practices, and tools in place, because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Developer machines are easily polluted with clashing libraries
and binaries.&lt;/li&gt;
&lt;li&gt;Programming libraries — Ruby&amp;#39;s gems or Python&amp;#39;s eggs, for example — are
regularly updated on unadvertised and unpredictable schedules.&lt;/li&gt;
&lt;li&gt;Docker images can be removed from public repositories without notice.&lt;/li&gt;
&lt;li&gt;Continuous integration providers change internals without forewarning or
ex post facto notice.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nix aims to solve some of these issues. Specifically,
it is designed to reproduce packages and environments &lt;em&gt;verbatim&lt;/em&gt;. Given
a set of inputs, it generates the same output every time.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s explore how Nix can reproduce an environment for Ruby and Rails
development, including a version of Ruby, a collection of gems, a PostgreSQL
database, and a Redis cache.&lt;/p&gt;
&lt;h2&gt;An Overview of Nix&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nixos.org&quot;&gt;Nix&lt;/a&gt; is an entire universe of software. It runs on Linux and macOS, on both
Apple Silicon and Intel processors, and has several components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://nixos.org/manual/nix/stable/language/index.html&quot;&gt;Nix Programming Language&lt;/a&gt;
is a declarative, domain-specific, functional language dedicated to system
composition. Portions of the language manipulate Nix directly, while
others provide typical programming constructs like flow control and variables.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/NixOS/nixpkgs&quot;&gt;Nix Packages&lt;/a&gt; is an expansive repository
of software, from &lt;code&gt;awk&lt;/code&gt; to &lt;code&gt;zsh&lt;/code&gt;. At the time of writing, the repository contains
more than 100,000 entries, each usable with Nix. Each package in the Nix archive
explicitly documents all its dependencies down to a specific commit.
Further, the nixpkgs archive is itself versioned, providing even greater control
over package provenance.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nixos.org/manual/nix/stable/command-ref/nix-shell.html&quot;&gt;&lt;code&gt;nix-shell&lt;/code&gt;&lt;/a&gt;
provides a virtual, sacrosanct environment contained in a shell. Given a Nix
description of a system — a &lt;em&gt;derivation&lt;/em&gt; in Nix parlance — you can compose
and boot a system in a walled garden isolated from all other virtual and
physical systems. For example, you can download, install, build, and run an
independent instance of Ruby with &lt;code&gt;nix-shell&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://nixos.org/manual/nix/stable/store/types/&quot;&gt;Nix Store&lt;/a&gt; persists
immutable data (such as software packages) and all dependencies. On some
systems, the Nix Store is the local file system; however, it
can also be retained on Amazon S3 and even a remote SSH server.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nixos.org/manual/nixos/stable/#sec-installation&quot;&gt;NixOS&lt;/a&gt; is an entire
Linux distribution configured entirely by the Nix language and composed from
versions in Nixpkgs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nix is expansive and each component warrants deep exploration. This
introduction focuses on the &lt;code&gt;nix-shell&lt;/code&gt; and Nix Packages and presents
just enough of the Nix programming language to boot a Rails application.
Tony Finn&amp;#39;s &lt;a href=&quot;https://tonyfinn.com/blog/nix-from-first-principles-flake-edition/&quot;&gt;Nix from First Principles&lt;/a&gt;
provides another take on Nix.&lt;/p&gt;
&lt;h2&gt;Nix Versus Docker&lt;/h2&gt;
&lt;p&gt;Docker and Nix are both capable of building environments. However, Nix provides
consistency everywhere, including build tools and source packages.&lt;/p&gt;
&lt;p&gt;Docker is not a package manager. While you can connect and coordinate
containers using Docker Compose, you cannot use Docker to combine containers
into a new container.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s trivial with Nix to combine disparate Ruby and PostgreSQL
derivations into a new environment.&lt;/p&gt;
&lt;p&gt;If a Docker image changes without notice, the output of &lt;code&gt;docker build&lt;/code&gt;
changes too. Pinning a version obviously helps, but if a
Docker image is deleted or an image&amp;#39;s repository goes offline, a
Docker build fails without recourse.&lt;/p&gt;
&lt;p&gt;Each package and version stored by Nix can have a unique ID akin to a git SHA.
You can think of a complete Nix derivation as a manifest of tens or hundreds of
SHAs. A change in any one SHA is detectable and two derivations will not be the
same if a single entity has changed.&lt;/p&gt;
&lt;h2&gt;Get Started with Nix&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s dive into Nix.&lt;/p&gt;
&lt;p&gt;To install Nix natively on Apple MacOS, open the &lt;em&gt;Terminal&lt;/em&gt; application
in &lt;em&gt;Applications/Utilities&lt;/em&gt; and run the following command at the shell prompt:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# Shell command to download and install Nix on your system
curl --proto &amp;#39;=https&amp;#39; --tlsv1.2 -sSf \
  -L https://install.determinate.systems/nix \
  | sh -s -- install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter the password for the &lt;code&gt;root&lt;/code&gt; user of your system when prompted.
(If you are a Mac administrator, you can enter your password too.)
You need privileged access as the install creates a new storage volume specifically
for Nix and tweaks several system settings, such as excluding the Nix store
from Time Machine backups.&lt;/p&gt;
&lt;p&gt;The installer summarizes all the modifications it plans to perform and
prompts you to proceed. Enter &lt;code&gt;Yes&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# Nix output
Nix install plan (v0.18.0)
Planner: macos (with default settings)

Planned actions:
* Create an encrypted APFS volume `Nix Store` for Nix on `disk1` and add it to `/etc/fstab` mounting on `/nix`
* Fetch `https://releases.nixos.org/nix/nix-2.21.2/nix-2.21.2-x86_64-darwin.tar.xz` to `/nix/temp-install-dir`
* Create a directory tree in `/nix`
* Move the downloaded Nix into `/nix`
* Create build users (UID 301-332) and group (GID 30000)
* Configure Time Machine exclusions
* Setup the default Nix profile
* Place the Nix configuration in `/etc/nix/nix.conf`
* Configure the shell profiles
* Configuring zsh to support using Nix in non-interactive shells
* Create a `launchctl` plist to put Nix into your PATH
* Configure Nix daemon related settings with launchctl
* Remove directory `/nix/temp-install-dir`

Proceed? ([Y]es/[n]o/[e]xplain): Y

 INFO Step: Create an encrypted APFS volume `Nix Store` for Nix on `disk1` and add it to `/etc/fstab` mounting on `/nix`
 INFO Step: Provision Nix
 INFO Step: Create build users (UID 301-332) and group (GID 30000)
 INFO Step: Configure Time Machine exclusions
 INFO Step: Configure Nix
 INFO Step: Configuring zsh to support using Nix in non-interactive shells
 INFO Step: Create a `launchctl` plist to put Nix into your PATH
 INFO Step: Configure Nix daemon related settings with launchctl
 INFO Step: Remove directory `/nix/temp-install-dir`

Nix was installed successfully!
To get started using Nix, open a new shell or
run `. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The installation takes only a moment or two to finish.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;You can easily test it when it&amp;#39;s done by running the following sequence of
commands in your current shell.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# Load Nix into the current shell environment
source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# Display the free space found on the new volume at /nix
df /nix

# Filesystem   512-blocks      Used Available Capacity iused      ifree %iused  Mounted on
# /dev/disk1s7  976490576    799416 228759728     1%   73715 1143798640    0%   /nix
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# Use Nix to install and run the `hello` command
nix run &amp;#39;nixpkgs#hello&amp;#39;
# Produces `Hello, World!&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;nix run&lt;/code&gt; command is something akin to magic. To run the &lt;a href=&quot;https://manpages.org/hello&quot;&gt;&lt;code&gt;hello&lt;/code&gt;&lt;/a&gt; utility, Nix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Downloads all the packages required to build &lt;code&gt;hello&lt;/code&gt;, including tools such as
&lt;code&gt;make&lt;/code&gt;, &lt;code&gt;gcc&lt;/code&gt;, and &lt;code&gt;tar&lt;/code&gt;. (On the machine used to develop this article, an &lt;em&gt;i9&lt;/em&gt;
iMac running MacOS Sonoma, Nix downloaded more than 400 prerequisite packages
into &lt;em&gt;/nix/store&lt;/em&gt;.)&lt;/li&gt;
&lt;li&gt;Downloads the most recent version of &lt;code&gt;hello&lt;/code&gt; (as no version number was specified).&lt;/li&gt;
&lt;li&gt;Builds and executes the utility, producing &lt;code&gt;Hello, World!&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Terminates and returns to the original shell.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you run the same command — &lt;code&gt;nix run &amp;#39;nixpkgs#hello&amp;#39;&lt;/code&gt; — again, Nix executes &lt;code&gt;hello&lt;/code&gt;
immediately, as the assets required for the utility are cached and readily accessible.&lt;/p&gt;
&lt;p&gt;You can use Nix to run almost any utility on-demand. Here is another example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# Launch the `fish` shell (see https://fishshell.com)
nix run `nixpkgs#fish`
martin@iMac ~/p/o/a/martin (ruby-on-nix)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The penultimate line of the output above is the default &lt;code&gt;fish&lt;/code&gt; prompt, showing a user
name, machine name, an abbreviated current working directory name, and the current
git branch, if any. Press Control-D to exit the shell.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re following along, try this: Launch &lt;code&gt;fish&lt;/code&gt; via Nix and enter the command
&lt;code&gt;which hello&lt;/code&gt;. The result should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;strike@iMac ~/p/o/a/martin (ruby-on-nix)&amp;gt; which hello
strike@iMac ~/p/o/a/martin (ruby-on-nix) [1]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;which hello&lt;/code&gt; fails in the current environment. (&lt;code&gt;[1]&lt;/code&gt; is the return value
of the previous command, not the output of the previous command. Shell
utilities return non-zero values on failure.) Where is the &lt;code&gt;hello&lt;/code&gt; from the
previous install? It&amp;#39;s in its own encapsulated derivation, completely isolated
from the one created for &lt;code&gt;fish&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A one-off Nix derivation such as the one above has its uses, but a Nix derivation
is not limited to a sole utility. A derivation may contain any number of
packages. Better yet, a derivation can be declared, shared, and re-instantiated in
identical form on any machine.&lt;/p&gt;
&lt;h2&gt;Nix for Ruby Development&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s create a Nix derivation for Rails development. The derivation must have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A version of Ruby, such as 3.2.0 (the latest version of Ruby available
via Nix at the time of writing). Each Ruby package includes &lt;code&gt;gem&lt;/code&gt; and &lt;code&gt;irb&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A database service to persist information. Here, let&amp;#39;s use PostgreSQL Version 15.&lt;/li&gt;
&lt;li&gt;A key store for caching data, partials, and views. Redis is quite capable.&lt;/li&gt;
&lt;li&gt;Tools for JavaScript and asset compilation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, a plenary Rails environment also needs the &lt;code&gt;rails&lt;/code&gt; gem and many
others. Ideally, a Nix derivation for Rails development also automatically
bundles necessary gems, producing a standalone environment ready for
development.&lt;/p&gt;
&lt;h3&gt;A Nix Derivation for Ruby Development&lt;/h3&gt;
&lt;p&gt;The code below is a nascent, but working, Nix derivation for Rails development.
A Nix derivation is like a &lt;em&gt;Dockerfile&lt;/em&gt; or a blueprint to
build a working environment from scratch.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nix&quot;&gt;let
  nixpkgs =
    import (builtins.fetchTarball {
      url = https://github.com/NixOS/nixpkgs/archive/ee4a6e0f566fe5ec79968c57a9c2c3c25f2cf41d.tar.gz;
    }) { };

  targetRuby = nixpkgs.ruby_3_2;

  myBundler = nixpkgs.bundler.override {
    ruby = targetRuby;
  };

  gems =
    nixpkgs.bundlerEnv {
      inherit (nixpkgs) ruby_3_2;

      name     = &amp;quot;rails-gems&amp;quot;;
      bundler  = myBundler;
      gemfile  = ./Gemfile;
      lockfile = ./Gemfile.lock;
      gemset   = ./gemset.nix;
    };

in
  nixpkgs.mkShell {
    buildInputs = [
      targetRuby
      gems
      gems.wrappedRuby
      nixpkgs.bundler
      nixpkgs.bundix
      nixpkgs.nodejs
      nixpkgs.yarn
      nixpkgs.postgresql
    ];
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sample derivation has two sections. The &lt;code&gt;let&lt;/code&gt; section defines settings,
while the &lt;code&gt;in&lt;/code&gt; portion controls the build and enumerates the packages
to construct and install in the derivation.&lt;/p&gt;
&lt;p&gt;Before discussing how to use the derivation, some commentary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The setting &lt;code&gt;nixpkgs&lt;/code&gt; requires a specific version of the Nix archive.
Like a &lt;code&gt;git&lt;/code&gt; repository, each iteration of the Nix packages archive is
represented by a unique SHA reflecting the state of its contents. If the
contents of the archive change, such as by incorporating a new version
of Ruby, the SHA changes in tandem. Similar to referencing specific commits
in &lt;code&gt;git&lt;/code&gt;, you can lock your derivation to a specific iteration of the
archive. The particular archive referred to here, &lt;code&gt;ee4a6e0&lt;/code&gt;, contained a bug
fix required to build the example derivation.&lt;/li&gt;
&lt;li&gt;The setting &lt;code&gt;targetRuby&lt;/code&gt; specifies a Ruby version. Here, the derivation uses
the latest version of Ruby 3.2 available in the archive.&lt;/li&gt;
&lt;li&gt;The setting &lt;code&gt;myBundler&lt;/code&gt; customizes the Nix build to use the same version
of Ruby specified for the shell environment.&lt;/li&gt;
&lt;li&gt;Lastly, the setting named &lt;code&gt;gems&lt;/code&gt; configures the Nix version of &lt;code&gt;bundler&lt;/code&gt;.
As with native &lt;code&gt;bundler&lt;/code&gt;, the Nix bundler requires a &lt;em&gt;Gemfile&lt;/em&gt; and a
&lt;em&gt;Gemfile.lock&lt;/em&gt;. &lt;em&gt;gemset.nix&lt;/em&gt; is original to Nix and is a translation
of &lt;em&gt;Gemfile.lock&lt;/em&gt; to the Nix syntax.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to know what versions of Ruby are available, point your browser
to the &lt;a href=&quot;https://search.nixos.org/packages&quot;&gt;Nix Packages Index&lt;/a&gt; and search for
&lt;code&gt;ruby&lt;/code&gt;. The search results include all the Ruby iterations available, such as
&lt;code&gt;ruby&lt;/code&gt;, &lt;code&gt;ruby_3_2&lt;/code&gt;, and &lt;code&gt;ruby_3_3&lt;/code&gt;, for Ruby 3.1, 3.2, and 3.3, respectively.
Each package in the search result specifies the exact version number, such as
&lt;code&gt;3.1.4&lt;/code&gt; for the &lt;code&gt;ruby&lt;/code&gt; package.&lt;/p&gt;
&lt;p&gt;If you want to use a specific version of Ruby that is not named in the archive, try
&lt;a href=&quot;https://github.com/bobvanderlinden/nixpkgs-ruby&quot;&gt;nixpkgs-ruby&lt;/a&gt;. For instance, to use Ruby 3.2.2, replace the line
&lt;code&gt;targetRuby = nixpkgs.ruby_3_2;&lt;/code&gt; in &lt;em&gt;shell.nix&lt;/em&gt; with this code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nix&quot;&gt;nixpkgs-ruby =
  import (builtins.fetchTarball {
    url = &amp;quot;https://github.com/bobvanderlinden/nixpkgs-ruby/archive/c1ba161adf31119cfdbb24489766a7bcd4dbe881.tar.gz&amp;quot;;
  });

targetRuby = nixpkgs-ruby.packages.x86_64-linux.&amp;quot;ruby-3.2.2&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Running the Nix Shell&lt;/h3&gt;
&lt;p&gt;Using the Nix derivation for Rails development proceeds much like development
in any shell. The big exception: There is no need to install Ruby or attendant
services beforehand. If you have Nix installed, you&amp;#39;re ready to go.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install Nix if necessary.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clone a Rails project into a new directory or choose an existing codebase.
&lt;code&gt;cd&lt;/code&gt; to the directory for the project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy and paste the code for the sample derivation to a new file in the project
directory named &lt;em&gt;shell.nix&lt;/em&gt;. (&lt;em&gt;shell.nix&lt;/em&gt; is the customary name for a derivation,
but the name is otherwise arbitrary.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate &lt;em&gt;gemset.nix&lt;/em&gt; to catalog the gems used within the project.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;nix \
  --extra-experimental-features nix-command \
  --extra-experimental-features flakes \
  run &amp;#39;nixpkgs/nixos-unstable#bundix&amp;#39; -- --lock
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command runs &lt;code&gt;bundix&lt;/code&gt;, a Nix program, to convert &lt;em&gt;Gemfile.lock&lt;/em&gt;
into &lt;em&gt;gemset.nix&lt;/em&gt;. Both are gem manifests, albeit the latter expressed natively
in the Nix language. For comparison, here is an entry for &lt;code&gt;actioncable&lt;/code&gt;
from &lt;em&gt;gemset.nix&lt;/em&gt; and from &lt;em&gt;Gemfile.lock&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nix&quot;&gt;# From gemset.nix
#
actioncable = {
  dependencies = [&amp;quot;actionpack&amp;quot; &amp;quot;activesupport&amp;quot; &amp;quot;nio4r&amp;quot; &amp;quot;websocket-driver&amp;quot; &amp;quot;zeitwerk&amp;quot;];
  groups = [&amp;quot;default&amp;quot;];
  platforms = [];
  source = {
    remotes = [&amp;quot;https://rubygems.org&amp;quot;];
    sha256 = &amp;quot;0ifiz4nd6a34z2n8lpdgvlgwziy2g364b0xzghiqd3inji0cwqp1&amp;quot;;
    type = &amp;quot;gem&amp;quot;;
  };
  version = &amp;quot;7.1.3.2&amp;quot;;
};

## From Gemfile.lock
#
actioncable (7.1.3.2)
  actionpack (= 7.1.3.2)
  activesupport (= 7.1.3.2)
  nio4r (~&amp;gt; 2.0)
  websocket-driver (&amp;gt;= 0.6.1)
  zeitwerk (~&amp;gt; 2.6)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate and launch the derivation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;nix-shell --verbose
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;--verbose&lt;/code&gt; option is not required, but provides good and numerous insights
into how &lt;code&gt;nix-shell&lt;/code&gt; assembles the environment.&lt;/p&gt;
&lt;h2&gt;The Downsides: Nix has Some Flaws&lt;/h2&gt;
&lt;p&gt;Nix is a rich, robust, and burgeoning option for building, reproducing, and
sharing environments. Nix is also an emergent tool: its current users are early adopters.&lt;/p&gt;
&lt;p&gt;Nix is already a workable solution for many problems, but Ruby support seems to be a
work in progress. For example, at the time of writing, no official archive is
available for newer versions of Ruby 3.3, and there is no guarantee the latest
Nix archive contains every gem in your Gemfile. Documentation for Ruby on Nix
is scattered between various GitHub repos, the official Nix website, and user
forums. I am grateful to a number of intrepid Nix developers who helped me craft
the sample &lt;em&gt;shell.nix&lt;/em&gt;, including Evan Travers, Bob van der Linden, and Norbert
Melzer.&lt;/p&gt;
&lt;p&gt;Nix currently is akin to
&lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain&quot;&gt;git&amp;#39;s &amp;quot;porcelain&amp;quot;&lt;/a&gt;:
powerful but esoteric. However, much like &lt;code&gt;git&lt;/code&gt; evolved into exoteric,
user-friendly tools such as
&lt;a href=&quot;https://dev.to/amirfakour/git-flow-easy-to-understand-tutorial-27jb&quot;&gt;git-flow&lt;/a&gt;,
&lt;a href=&quot;https://desktop.github.com&quot;&gt;GitHub Desktop&lt;/a&gt;, and
&lt;a href=&quot;https://www.git-tower.com&quot;&gt;Tower&lt;/a&gt; to become user-friendly, many developers are
building abstractions, wrappers, and utilities to simplify Nix usage. Let&amp;#39;s briefly look at a few of these tools now.&lt;/p&gt;
&lt;h3&gt;Tools to Help with Nix: &lt;code&gt;devbox&lt;/code&gt;, &lt;code&gt;devenv.sh&lt;/code&gt;, and &lt;code&gt;fleek&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;A tool to experiment with is &lt;a href=&quot;https://www.jetify.com/devbox&quot;&gt;&lt;code&gt;devbox&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;devbox&lt;/code&gt;
turned a 10-year-old Macbook Pro laptop running macOS Big Sur into a development
machine for a modern Rails application in a few minutes. The setup did not
include automatic bundling of gems, but it did install Ruby (including &lt;code&gt;bundler&lt;/code&gt;
and &lt;code&gt;gem&lt;/code&gt;), Redis, and PostgreSQL in an isolated and pristine shell environment.
&lt;code&gt;devbox&lt;/code&gt; eschews a bespoke programming language for configuration and uses JSON.
The environment on the MacBook Pro booted from this minimal file named
&lt;em&gt;devbox.json&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;$schema&amp;quot;: &amp;quot;https://raw.githubusercontent.com/jetify-com/devbox/0.10.6/.schema/devbox.schema.json&amp;quot;,
  &amp;quot;packages&amp;quot;: [
    &amp;quot;postgresql@latest&amp;quot;,
    &amp;quot;redis@latest&amp;quot;,
    &amp;quot;ruby@3.2.2&amp;quot;,
    &amp;quot;libsass@latest&amp;quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://devenv.sh&quot;&gt;&lt;code&gt;devenv.sh&lt;/code&gt;&lt;/a&gt; merits exploration too. It is something of a
hybrid, with a JSON-like programming language, YAML configuration, and
Docker-like composition of services.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://getfleek.dev&quot;&gt;&lt;code&gt;fleek&lt;/code&gt;&lt;/a&gt; is another
Nix-based tool to craft and share configurations for development machines. It
also avoids the Nix programming language and instead provides Homebrew-like
commands to build an environment.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ve seen how Nix can help reproduce a stable environment for Ruby and Rails applications.&lt;/p&gt;
&lt;p&gt;While it has some flaws, Nix and the myriad offshoots in development are worth watching.&lt;/p&gt;
&lt;p&gt;Go forth and hack!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Setting Up Custom Metrics with Effective Alerts for a Ruby App in AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/07/31/setting-up-custom-metrics-with-effective-alerts-for-a-ruby-app-in-appsignal.html"/>
    <id>https://blog.appsignal.com/2024/07/31/setting-up-custom-metrics-with-effective-alerts-for-a-ruby-app-in-appsignal.html</id>
    <published>2024-07-31T00:00:00+00:00</published>
    <updated>2024-07-31T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s learn how to use custom metrics in AppSignal for deep insights into our Ruby app&#039;s performance.</summary>
    <content type="html">&lt;p&gt;Most of the time, the default application monitoring metrics, graphs, and visualizations provided by AppSignal will do for your Ruby app. However, you might be the kind of user who likes a bit of control over what is measured, how it’s displayed, and how critical information about your app should be relayed.&lt;/p&gt;
&lt;p&gt;AppSignal allows you to customize app metrics and dashboards as you wish. In this guide, we’ll learn all about AppSignal&amp;#39;s custom metrics, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What custom metrics are&lt;/li&gt;
&lt;li&gt;The different types of custom metrics you can set up&lt;/li&gt;
&lt;li&gt;How to customize graph visualizations&lt;/li&gt;
&lt;li&gt;How to set up effective alerts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more!&lt;/p&gt;
&lt;p&gt;But before we dive in, you&amp;#39;ll need a couple of things to follow along.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;An AppSignal account:&lt;/strong&gt; If you don&amp;#39;t have one, &lt;a href=&quot;https://appsignal.com/users/sign_up&quot;&gt;sign up for a 30-day free trial&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Ruby application:&lt;/strong&gt; This app can be based on any supported Ruby framework, like Rails, Sinatra, or just plain Ruby. Additionally, it can be a production or development app. If you don&amp;#39;t want to spin up your own app, &lt;a href=&quot;https://github.com/iamaestimo/sinatra_api_backend_app&quot;&gt;clone the code for the example Sinatra app we&amp;#39;ll be using in this tutorial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note: If you&amp;#39;re using your own app to follow along with this tutorial, make sure your app is configured to use the latest AppSignal Ruby gem, as the examples used in this tutorial assume this is the case.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;What Are Custom Metrics?&lt;/h2&gt;
&lt;p&gt;Other than measuring your app&amp;#39;s error rates, throughput, and performance, you might be interested in measuring custom data specifically tailored for your own app. For example, you could be interested in how many visitors signed up to your app in a particular time period, how your app&amp;#39;s websocket layer is performing, and so forth.&lt;/p&gt;
&lt;p&gt;For such customized cases, you might be hard-pressed to find a standard measuring tool within AppSignal. Instead, you&amp;#39;ll need to use a &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/index.html&quot;&gt;custom metric&lt;/a&gt;. Custom metrics are additional metrics that you define alongside the default ones for deeper context on how your app is running.&lt;/p&gt;
&lt;p&gt;Next up, let&amp;#39;s learn how to set up our first custom metric.&lt;/p&gt;
&lt;h2&gt;Setting Up Custom Metrics&lt;/h2&gt;
&lt;p&gt;You can set up a custom metric for almost any use case within your application. Let&amp;#39;s start with a simple example to help you understand how everything fits together.&lt;/p&gt;
&lt;p&gt;The first step is to define a custom metric that will be tracked on AppSignal. You can define a custom metric by using the different metric types available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gauges&lt;/li&gt;
&lt;li&gt;Counters&lt;/li&gt;
&lt;li&gt;Distributions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The Gauge Custom Metric&lt;/h3&gt;
&lt;p&gt;In AppSignal, a &lt;em&gt;gauge&lt;/em&gt; custom metric is useful for measuring metrics that increase and decrease over time.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s set up a simple gauge custom metric to measure the total number of posts in our example Sinatra app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app.rb

require &amp;#39;sinatra/base&amp;#39;
require &amp;#39;dotenv/load&amp;#39;
require &amp;#39;sinatra/reloader&amp;#39;
require &amp;#39;sinatra/activerecord&amp;#39;
require &amp;#39;./models/post.rb&amp;#39;
require &amp;#39;appsignal/integrations/sinatra&amp;#39;

class App &amp;lt; Sinatra::Base
  ...

  get &amp;#39;/&amp;#39; do
    posts = Post.all

    # set a gauge custom metric
    posts_count = posts.count
    Appsignal.set_gauge(&amp;quot;all_posts&amp;quot;, posts_count)

    posts.to_json
  end
...
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;In the code shown above, we use the &lt;code&gt;Appsignal::Helpers::Metrics&lt;/code&gt; module and call the &lt;code&gt;set_gauge&lt;/code&gt; method, which accepts three arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;key&lt;/code&gt;: the name of the custom metric. In the example, this would be &lt;em&gt;all_posts&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt; - The metric or &amp;quot;thing&amp;quot; to be measured. In the example shown above, this is simply the total post count.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tags&lt;/code&gt; - Additional and optional metadata that can be added to a custom metric and are useful for labeling the data being measured however you want. For example, we could easily tag the &lt;code&gt;posts_count&lt;/code&gt; metric to account for the environment, as shown below:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app.rb

...

get &amp;#39;/&amp;#39; do
  posts = Post.all
  posts_count = posts.count
  Appsignal.set_gauge(&amp;quot;all_posts&amp;quot;, posts_count, environment: &amp;quot;development&amp;quot;)

  posts.to_json
end

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great, we&amp;#39;ve just added our first custom gauge metric! But if you were to go back to AppSignal, your new custom metric will not be visible. Instead, you&amp;#39;re likely to see the default dashboard, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/default-appsignal-dashboard.png&quot; alt=&quot;Default Appsignal dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;So what do you need to do to make the custom metric appear? You need to add a dashboard. Start with creating a new dashboard:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/create-new-dashboard.png&quot; alt=&quot;Create new dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Then name your new dashboard with a descriptive title and description:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/naming-custom-dashboard.png&quot; alt=&quot;Name the new dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;With the custom dashboard added, you&amp;#39;ll now need to add a graph for the custom metric:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/adding-graph.png&quot; alt=&quot;Adding a graph to the custom dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Then define the new graph:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/defining-new-graph.png&quot; alt=&quot;Define the new graph&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s a breakdown of the fields to set up the new graph:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;a. Title&lt;/strong&gt; - Enter a descriptive title for the new graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;b. Description&lt;/strong&gt; - This is optional, but you can enter a description for the new graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;c. Metrics&lt;/strong&gt; - Here is where you define the metric that will be measured and displayed by the new graph. This is the name of the custom metric, or the first argument defined in the &lt;code&gt;set_gauge&lt;/code&gt; method: &lt;code&gt;all_posts&lt;/code&gt;. In this section, you can also define tags (for example, the tag &lt;em&gt;environment&lt;/em&gt; is also included as shown).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;d. Graph display&lt;/strong&gt; - This is where you choose the type of graph display for your new graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;e. Legend label&lt;/strong&gt; - You can customize the label for the chart legend here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;f. Data format&lt;/strong&gt; - Define the data type used for the graph display. You can choose from a number of formats, including number, percentage, throughput (in requests/minute or hour), duration (in milliseconds), or file size (in bytes).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you&amp;#39;ve properly defined the new graph&amp;#39;s properties, you should get a graph for the custom metric. This is similar to what is shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/finished-custom-graph.png&quot; alt=&quot;Finished custom graph&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Moving forward, let&amp;#39;s look at the next custom metric type: the counter.&lt;/p&gt;
&lt;h3&gt;The Counter Custom Metric&lt;/h3&gt;
&lt;p&gt;A counter custom metric is great for measuring how many times an event occurs. Using the example application, we can apply a counter metric to measure every time the home (root) page is visited.&lt;/p&gt;
&lt;p&gt;To begin with, edit the root method to include the code shown below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app.rb
...
get &amp;#39;/&amp;#39; do
  ...
  Appsignal.increment_counter(&amp;quot;visits_count&amp;quot;, 1)
end
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we use AppSignal&amp;#39;s &lt;code&gt;increment_counter&lt;/code&gt; method and pass it &lt;code&gt;visits_count&lt;/code&gt; as the first argument. The increment step is the integer 1, passed as the second argument. You could also add a &lt;code&gt;tags&lt;/code&gt; hash as the third argument, but we&amp;#39;ll leave it as is (since this was covered in the previous section).&lt;/p&gt;
&lt;p&gt;Now go ahead and follow the steps as outlined for the gauge metric type. Add a custom graph for this counter metric to give you a similar graph to the one shown:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/custom-counter-metric.png&quot; alt=&quot;Custom graph for counter metric&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s switch gears to the distribution custom metric.&lt;/p&gt;
&lt;h3&gt;The Distribution Custom Metric&lt;/h3&gt;
&lt;p&gt;The AppSignal distribution custom metric is useful for measuring something per unit of time: for example, how many seconds it takes to generate a PDF report, or how long a background job takes to execute.&lt;/p&gt;
&lt;p&gt;Using the example application, let&amp;#39;s modify the main file to include a call to an open API endpoint. Then, we&amp;#39;ll use a custom distribution to measure how long the API call takes in milliseconds.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app.rb
...

require &amp;#39;httparty&amp;#39;

class App &amp;lt; Sinatra::Base
  ...

  Thread.new do
    while true do
      start_time = Time.now
      url = &amp;#39;https://openlibrary.org/search/authors.json?q=clavell&amp;#39;
      $response = HTTParty.get(url)
      duration = (Time.now - start_time) * 1000
      Appsignal.add_distribution_value(&amp;quot;fetch_books_duration&amp;quot;, duration)
    end
  end

  get &amp;#39;/&amp;#39; do
    ...
    $response.to_json
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if we go back to AppSignal, we can view the custom distribution as a graph.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Tip: You can follow the steps outlined in the gauge section to set up the custom graph visualization.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/distribution-metric.png&quot; alt=&quot;Distribution metric&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now that you&amp;#39;ve learned how to create custom metrics and the accompanying graph visualizations, you might have noticed that it&amp;#39;s not very convenient to keep going back to the AppSignal dashboards to see what&amp;#39;s going on with your app. Instead, it would be very handy if you could get a notification for your custom metrics, right?&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s learn how to set up notifications for your custom metrics next.&lt;/p&gt;
&lt;h2&gt;Notification Alerts&lt;/h2&gt;
&lt;p&gt;By default, whenever an error or performance event occurs, AppSignal will open an incident for that event and place it in the relevant section. For example, if it&amp;#39;s an error, you&amp;#39;ll find it in the errors list, while performance incidents will be in the performance list.&lt;/p&gt;
&lt;p&gt;Additionally, &lt;a href=&quot;https://www.appsignal.com/tour/anomaly-detection&quot;&gt;AppSignal sends out incident notifications&lt;/a&gt; via email (the default notification channel). You can also set up other notification channels, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Discord&lt;/li&gt;
&lt;li&gt;Google Hangouts&lt;/li&gt;
&lt;li&gt;Intercom&lt;/li&gt;
&lt;li&gt;Microsoft Teams&lt;/li&gt;
&lt;li&gt;Slack&lt;/li&gt;
&lt;li&gt;Webhook&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more.&lt;/p&gt;
&lt;p&gt;But before we set up a notification trigger for one of our custom metrics, it&amp;#39;s important to be aware of the various notification options available to you.&lt;/p&gt;
&lt;p&gt;To begin with, you can set up a notification for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Every time&lt;/strong&gt; - Here, a notification will be sent out every time an incident occurs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First deploy&lt;/strong&gt; - This indicates that a notification will be sent the first time an incident occurs after a deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First after close&lt;/strong&gt; - Here, a notification is sent out whenever an incident re-occurs after the previous one was closed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never notify&lt;/strong&gt; - As the name suggests, in this case, a notification will never be sent, but the error or performance incident will still be tracked on AppSignal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every nth hour or day&lt;/strong&gt; - With this option, you can specify how many alerts will be sent to you within an hour or a day. This option is perfect for keeping a balance between getting notified of important events and having too many notifications (which could easily overwhelm you or your team).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I highly suggest you dig into &lt;a href=&quot;https://docs.appsignal.com/application/notification-settings.html#notification-options&quot;&gt;AppSignal&amp;#39;s notification settings documentation&lt;/a&gt; to get more information on these options.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see how to set up a notification for one of the custom metrics we made earlier. This will be a trivial example, but it will illustrate the steps you need to go through for your own use case.&lt;/p&gt;
&lt;h3&gt;Setting Up Notification Alerts for Your Custom Metrics&lt;/h3&gt;
&lt;p&gt;For this example, we&amp;#39;ll use the distribution metric that measured the API call&amp;#39;s duration earlier in this post. Let&amp;#39;s say we&amp;#39;d like to get an email alert whenever the mean duration exceeds a certain number (in milliseconds).&lt;/p&gt;
&lt;p&gt;The steps for setting this up are shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/setting-up-notifications-step-1.png&quot; alt=&quot;Setting up notifications for a custom metric - 1&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Firstly, begin by hitting the &lt;em&gt;Triggers&lt;/em&gt; link in the left-hand menu.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/setting-up-notifications-step-2.png&quot; alt=&quot;Setting up notifications for a custom metric - 2&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Give your trigger a relevant name, then select the measurement for which this notification is intended. In this example, we are using the &lt;code&gt;fetch_books_duration&lt;/code&gt; distribution custom metric. You can also add tags if you wish to.&lt;/p&gt;
&lt;p&gt;Next, define the comparison operator and the value to check for. For example, let&amp;#39;s say we want to receive an alert whenever the duration exceeds 1600 milliseconds. For this, we would select the comparison operator &lt;em&gt;more than&lt;/em&gt;, then, a value of 1600.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/setting-up-notifications-step-3.png&quot; alt=&quot;Setting up notification for a custom metric - 3&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Finally, you&amp;#39;ll need to define the &lt;a href=&quot;https://docs.appsignal.com/anomaly-detection.html#warm-up&quot;&gt;alert warm-up and cooldown settings&lt;/a&gt;. Provide a description for the alert, a link to the dashboard to be included in the alert message (if needed), and, finally, the notification method (with email being the default).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-07/setting-up-notifications-step-4.png&quot; alt=&quot;Setting up notification for a custom metric - 4&quot;/&gt;&lt;/p&gt;
&lt;p&gt;With that done, you&amp;#39;ll receive a notification whenever an incident occurs matching the settings you input here.&lt;/p&gt;
&lt;p&gt;And that&amp;#39;s it!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, we learned how to set up custom metrics for a Ruby application with matching dashboards and graph visualizations on AppSignal.&lt;/p&gt;
&lt;p&gt;The custom metrics functionality that AppSignal offers can be fine-tuned for very powerful applications. Take a deep dive into &lt;a href=&quot;https://docs.appsignal.com/metrics/custom.html&quot;&gt;AppSignal&amp;#39;s custom metrics documentation&lt;/a&gt; to discover more possibilities.&lt;/p&gt;
&lt;p&gt;Until next time, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>What&#039;s Coming in Ruby on Rails 7.2: Database Features in Active Record</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/07/24/whats-coming-in-ruby-on-rails-7-2-database-features-in-active-record.html"/>
    <id>https://blog.appsignal.com/2024/07/24/whats-coming-in-ruby-on-rails-7-2-database-features-in-active-record.html</id>
    <published>2024-07-24T00:00:00+00:00</published>
    <updated>2024-07-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s take a look at some noteworthy changes in Ruby on Rails 7.2, particularly in Active Record.</summary>
    <content type="html">&lt;p&gt;Ruby on Rails is currently in major version 7.1 and rolling towards Rails 8, the next comprehensive new release.&lt;/p&gt;
&lt;p&gt;Before Rails 8, though, there’s a significant version that will help bridge the gap: Ruby on Rails 7.2.&lt;/p&gt;
&lt;p&gt;In this post, we’ll dive into several noteworthy changes in Ruby on Rails 7.2, focusing on the support for database changes in Active Record.&lt;/p&gt;
&lt;p&gt;You&amp;#39;ll come away with hands-on opportunities to work with these features.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Active Record Database-Related Features in Ruby on Rails 7.2&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://guides.rubyonrails.org/active_record_basics.html&quot;&gt;Active Record&lt;/a&gt; object-relational mapper (ORM) supports three open-source relational database management systems: MySQL, PostgreSQL, and SQLite. Active Record is most commonly thought of for its ORM facilities, mapping between database operations and database types to Ruby methods and types. However, besides the ORM, Active Record has mechanisms to evolve your database schema definition, capturing incremental changes and a representation of the entire schema in Ruby files or SQL files.&lt;/p&gt;
&lt;p&gt;Active Record can connect a single Rails app to multiple databases, and those databases can have distinct roles. The databases might have writer or reader roles or even be “shards” that leverage the &lt;a href=&quot;https://rubyonrails.org/2020/12/9/Rails-6-1-0-release&quot;&gt;Horizontal Sharding feature&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recently, Beta releases of 7.2 have shipped, allowing developers to preview what’s changed.&lt;/p&gt;
&lt;p&gt;As we roll towards Rails 8, we must first pass through 7.2. Let’s examine what&amp;#39;s in this release.&lt;/p&gt;
&lt;h2&gt;What’s New with Composite Primary Keys (CPKs)&lt;/h2&gt;
&lt;p&gt;Rails 7.1 brought us the first release of composite primary keys (CPKs). What are CPKs? First, let’s talk about unique identifiers in databases more generally. Uniquely identifying rows can take the shape of “natural keys” or “surrogate keys”. Since most databases use surrogate keys, let’s focus on those. Surrogate keys are when “id” integer values are used to uniquely identify a row. This is a surrogate in that it’s not directly related to the data row.&lt;/p&gt;
&lt;p&gt;In Rails, the surrogate primary key is usually a column called “id” with an integer type, populated by the database. In PostgreSQL, a sequence object usually provides the value.&lt;/p&gt;
&lt;p&gt;What does this have to do with indexes? When we define our &lt;code&gt;id&lt;/code&gt; column as the primary key column, this creates a primary key constraint on the table, and constraints have a supporting index. Primary key constraints enforce unique values, and the index helps the lookups performed to enforce that rule to run quickly.&lt;/p&gt;
&lt;p&gt;CPKs, or multi-column primary keys, are primary keys where multiple columns uniquely identify a table row. This is a generic mechanism, so it’s up to you as to how you describe your CPK. You may choose to incorporate a surrogate value like the &lt;code&gt;id&lt;/code&gt; integer, and combine that with a unique identifier for one of your customers, for example, a &lt;code&gt;customer_id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You may choose a CPK as a natural key composed of two foreign key columns that point to other tables. This structure is most common with &lt;em&gt;join tables&lt;/em&gt;.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;With that context in mind, let&amp;#39;s arrive at what’s changing in Rails 7.2.&lt;/p&gt;
&lt;p&gt;Currently, for the CPK feature, we use the &lt;code&gt;query_constraints&lt;/code&gt; keyword on models that relate to other models, and supply a list of foreign key columns that refer to the foreign table primary key columns.&lt;/p&gt;
&lt;p&gt;In Rails 7.2, this option changes from &lt;code&gt;query_constraints&lt;/code&gt; to &lt;code&gt;foreign_keys&lt;/code&gt;. Besides the parameter name change, some interesting discussions are underway about plans to &lt;a href=&quot;https://github.com/rails/rails/pull/51571&quot;&gt;repurpose the original &lt;code&gt;query_constraints&lt;/code&gt; name&lt;/a&gt; by freeing it up.&lt;/p&gt;
&lt;p&gt;If you’d like to play around with single-column surrogate keys, and compare them with composite or multi-column primary keys as a type of natural key, take a look at &lt;a href=&quot;https://github.com/andyatkinson/bookshop&quot;&gt;this Bookshop repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here, we’ve got the models listed in the Rails API documentation for CPKs implemented as a single file Rails app. You can modify the code and database schema design and try out different alternatives.&lt;/p&gt;
&lt;p&gt;To try out Rails 7.2 changes, edit the Gemfile portion within the &lt;code&gt;bookshop.rb&lt;/code&gt; Ruby file. Use a 7.2 beta version by putting this change into the file, then running &lt;code&gt;bundle install&lt;/code&gt; from your terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;gem &amp;#39;rails&amp;#39;, &amp;#39;~&amp;gt; 7.2.0.beta1&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Development Containers and Databases in Rails 7.2&lt;/h2&gt;
&lt;p&gt;Ruby on Rails has always been a productivity-centric framework. One challenge that impacts the developer experience and productivity is creating and maintaining a development environment.&lt;/p&gt;
&lt;p&gt;Dev containers intend to fix that. They&amp;#39;re used to create reproducible development environments that anyone on your team can run.&lt;/p&gt;
&lt;p&gt;Besides reproducibility, another benefit is the isolation of environment dependencies. Some operating system dependencies you use can be particularly challenging to install, or multiple versions might coexist. By isolating dependencies into a container, you can avoid those pitfalls.&lt;/p&gt;
&lt;p&gt;Dev containers use Docker, but add a “devcontainer.json” file to the mix. This can describe the text editor configuration and be shared on a team that uses the same editor and configuration.&lt;/p&gt;
&lt;p&gt;Let’s generate a new app with a dev container within the bookshop repository. Make sure that Rails 7.2 is the version used by the rails executable, installing the gem if needed.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll add &lt;code&gt;--database=postgresql&lt;/code&gt; which will configure a Postgres instance in Docker, and install the &lt;code&gt;pg&lt;/code&gt; gem for the generated Rails app.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;gem install rails -v 7.2.0.beta1 # if needed

rails new myapp --database=postgresql --devcontainer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &amp;quot;myapp&amp;quot; in VS Code from the interface or by running &lt;code&gt;code myapp&lt;/code&gt; when the &lt;code&gt;code&lt;/code&gt; executable is available.&lt;/p&gt;
&lt;p&gt;Once in VS Code, if not auto-detected, run &amp;quot;Command-shift-P&amp;quot; and choose “Dev Container: Rebuild in Container”. You&amp;#39;ll see “Starting Dev Container”, which starts up Docker as needed.&lt;/p&gt;
&lt;p&gt;Click “Show Log” to expand the terminal area, showing log progress. Here, you can tell whether containers are being downloaded and which stage they’re in.&lt;/p&gt;
&lt;p&gt;The containers can be viewed and administered using Docker Desktop.&lt;/p&gt;
&lt;p&gt;The Rails app, Postgres, and all its gem dependencies should now be running inside the container.&lt;/p&gt;
&lt;p&gt;Open a terminal in VS Code and run &lt;code&gt;bin/rails server&lt;/code&gt;. Navigate to &lt;code&gt;localhost:3000&lt;/code&gt; in your browser. Since there’s a local port mapping from 3000 into the container, you should see the generated Rails 7.2 app welcome page!&lt;/p&gt;
&lt;p&gt;If you run into issues, visit the &lt;a href=&quot;https://github.com/andyatkinson/bookshop&quot;&gt;bookshop repo&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2&gt;Database Transaction Enhancements, Solid Queue, and Active Job&lt;/h2&gt;
&lt;p&gt;Transactions are one of the fundamental concepts of relational databases. Databases are designed to support high concurrent access to shared resources, like table row data. For example, multiple clients might try to read and write to the same table row at once. The database consistently processes operations by using transactions with an isolated view of the data.&lt;/p&gt;
&lt;p&gt;One of the problems in Rails apps comes when working with relational database transactions and then with another data store like Redis (via background processing with Sidekiq or other Active Job backends).&lt;/p&gt;
&lt;p&gt;The issue is one of timing: ensuring data is committed to the relational database before any background work starts.&lt;/p&gt;
&lt;p&gt;With Solid Queue — a database-backed queue management system — coming in Rails 8, ensuring transactionally consistent data and operational order is even more important. Transactional consistency errors will become more visible when there&amp;#39;s a first-party database-backed queue system.&lt;/p&gt;
&lt;p&gt;To prepare for that, what&amp;#39;s changed in Rails 7.2? Active Job will now defer enqueuing background jobs until &lt;em&gt;after&lt;/em&gt; a database transaction has been committed. This small change ensures no background job processing starts until the database transaction is committed.&lt;/p&gt;
&lt;p&gt;Another change in 7.2 is that callbacks will be registered on database transactions.&lt;/p&gt;
&lt;p&gt;For example, &lt;a href=&quot;https://edgeguides.rubyonrails.org/7_2_release_notes.html#per-transaction-commit-and-rollback-callbacks&quot;&gt;&lt;code&gt;after_commit&lt;/code&gt;&lt;/a&gt; can be added within a transaction block to ensure that it runs after the transaction is committed.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the example used in the release notes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Article.transaction do |transaction|
  article.update(published: true)

  transaction.after_commit do
    # Do work after commit, like send a notification email
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach ensures the code in the &lt;code&gt;after_commit&lt;/code&gt; block runs after the article is updated.&lt;/p&gt;
&lt;h2&gt;New Active Support Instrumentation for Transactions&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/rails/rails/releases/tag/v7.2.0.beta3&quot;&gt;Beta 3 version of Rails 7.2&lt;/a&gt; was recently released, and it included new Active Support Instrumentation events we can configure for our applications.&lt;/p&gt;
&lt;p&gt;One of these new events is called &lt;code&gt;start_transaction.active_record&lt;/code&gt;, and it is triggered when database transactions or savepoints within transactions are started.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Check out the blog post &lt;a href=&quot;https://andyatkinson.com/blog/2024/07/22/postgresql-savepoints&quot;&gt;You make a good point! — PostgreSQL Savepoints&lt;/a&gt; for more information on savepoints.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When database transactions finish, the event &lt;code&gt;transaction.active_record&lt;/code&gt; event is emitted.&lt;/p&gt;
&lt;p&gt;What can we do with these events? By creating an &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/Subscriber.html&quot;&gt;Active Support Subscriber&lt;/a&gt; that inspects these events and their payloads, we can gain a better understanding of database transaction and savepoint activity within Active Record.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rails/rails/commit/6a05a391999dd8201706516b440ef8b9b8527161&quot;&gt;Check out the commit code changes and documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we looked at a few database-related features coming in Ruby on Rails 7.2. We covered the basics of CPKs and an important option name that&amp;#39;s changing in 7.2. We also examined how we&amp;#39;ll be able to run all of our application dependencies (including our databases) in a dev container.&lt;/p&gt;
&lt;p&gt;Finally, we covered how database transactions and database-backed background job systems will tackle the challenge of write operations occurring in the expected order.&lt;/p&gt;
&lt;p&gt;Besides those items, there&amp;#39;s plenty more to read and learn about Rails 7.2. For example, Ruby 3.1 will become the new default minimum version, there will be support for jemalloc, RuboCop rules, a GitHub CI workflow, and support for progressive web apps (PWAs). To learn more, check out the &lt;a href=&quot;https://edgeguides.rubyonrails.org/7_2_release_notes.html&quot;&gt;Rails 7.2 release notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Auth0 for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/07/17/an-introduction-to-auth0-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2024/07/17/an-introduction-to-auth0-for-ruby-on-rails.html</id>
    <published>2024-07-17T00:00:00+00:00</published>
    <updated>2024-07-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">This article will cover the setup and use of Auth0 in a Ruby on Rails application.</summary>
    <content type="html">&lt;p&gt;From custom-made to plug-and-play forms of authentication, Ruby developers have plenty to choose from these days. Yet, as you may know, building your own solution can be costly and dangerous. If Devise is the de facto standard for most teams, an alternative might simplify the lives of most.&lt;/p&gt;
&lt;p&gt;This article will cover the setup and use of Auth0 in a Ruby on Rails application, including everything you need to get going properly, from handling roles to relying on multiple providers to authenticate users.&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Here&amp;#39;s what we need to get started:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An &lt;a href=&quot;https://auth0.com&quot;&gt;Auth0&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;A Ruby on Rails application (version 7.x onwards)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Auth0 is a third-party authentication service with a free tier that lets you handle up to 7,000 users. That is plenty to get you started, and its pricing is reasonable if you need more advanced features.&lt;/p&gt;
&lt;h2&gt;Configuring Our Ruby App in Auth0&lt;/h2&gt;
&lt;p&gt;Since your application will rely on Auth0 to authenticate users through redirects and calls, we must ensure it stays secure.&lt;/p&gt;
&lt;p&gt;In our Auth0 account, let&amp;#39;s create an application within a tenant. You can create multiple tenants in an account to separate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Domain names.&lt;/li&gt;
&lt;li&gt;Different environments (development, production, etc).&lt;/li&gt;
&lt;li&gt;The country or region within which data will be stored.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you have created your first tenant, you can build an application. That&amp;#39;s where we&amp;#39;ll start.&lt;/p&gt;
&lt;p&gt;Head to the &amp;quot;Settings&amp;quot; tab in the application panel. You need to copy and paste the following and save it to a safe place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The domain name: &lt;code&gt;app-name.[eu,us,..].auth0.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The client&amp;#39;s ID&lt;/li&gt;
&lt;li&gt;The client&amp;#39;s Secret&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We must also fill in the following Application URIs. We&amp;#39;ll use these values for our local development setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allowed Callback URLs: &lt;code&gt;http://localhost:3000/auth/auth0/callback&lt;/code&gt; (the URL Auth0 will redirect to after authentication).&lt;/li&gt;
&lt;li&gt;Allowed Logout URLs: &lt;code&gt;http://localhost:3000&lt;/code&gt; (the URL Auth0 will redirect to after someone logs out).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s go ahead and configure our app.&lt;/p&gt;
&lt;h2&gt;Preparing Our Ruby on Rails Application&lt;/h2&gt;
&lt;p&gt;You can start with a vanilla Ruby on Rails application using the &lt;code&gt;rails new&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s generate one that relies on SQLite3 for the database and Tailwind for the CSS library. Skip the installation of mini-test and name the application &amp;quot;Auth0 article&amp;quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;cd ~/my_projects
rails new -d sqlite3 -c tailwind -T auth0_article
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a User model with just a few attributes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;cd auth0_article
bin/rails db:create
bin/rails generate model User email name
bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This builds a simple but efficient base for our application.&lt;/p&gt;
&lt;h2&gt;Adding the Auth0 Gem to the App&lt;/h2&gt;
&lt;p&gt;You&amp;#39;ll need &lt;code&gt;omniauth-auth0&lt;/code&gt; and &lt;code&gt;omniauth-rails_csrf_protection&lt;/code&gt; to use Auth0 in your Ruby on Rails application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;omniauth-auth0&amp;#39;, &amp;#39;~&amp;gt; 3.0&amp;#39;
gem &amp;#39;omniauth-rails_csrf_protection&amp;#39;, &amp;#39;~&amp;gt; 1.0&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuring Auth0&lt;/h3&gt;
&lt;p&gt;We need to create a tiny configuration file (&lt;code&gt;config/auth0.yml&lt;/code&gt;) to store credentials in our development environment.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;development:
  auth0_domain: &amp;lt;YOUR AUTH0 APPLICATION DOMAIN NAME&amp;gt;
  auth0_client_id: &amp;lt;YOUR AUTH0 APPLICATION CLIENT ID&amp;gt;
  auth0_client_secret: &amp;lt;YOUR AUTH0 APPLICATION CLIENT SECRET&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also rely on environment variables here by using some erb:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;development:
  auth0_domain: &amp;lt;%= ENV[&amp;#39;AUTH0_APPLICATION_DOMAIN&amp;#39;] %&amp;gt;
  auth0_client_id: &amp;lt;%= ENV[&amp;#39;AUTH0_APPLICATION_CLIENT_ID&amp;#39;] %&amp;gt;
  auth0_client_secret: &amp;lt;%= ENV[&amp;#39;AUTH0_APPLICATION_CLIENT_SECRET&amp;#39;] %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, relying on Ruby on Rails credentials ensures that things stay more up-to-date.&lt;/p&gt;
&lt;p&gt;This file will be used in the Auth0 initializer (&lt;code&gt;config/initializers/auth0.rb&lt;/code&gt;), which we will now create:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;AUTH0_CONFIG = Rails.application.config_for(:auth0)

Rails.application.config.middleware.use OmniAuth::Builder do
  provider(
    :auth0,
    AUTH0_CONFIG[&amp;#39;auth0_client_id&amp;#39;],
    AUTH0_CONFIG[&amp;#39;auth0_client_secret&amp;#39;],
    AUTH0_CONFIG[&amp;#39;auth0_domain&amp;#39;],
    callback_path: &amp;#39;/auth/auth0/callback&amp;#39;,
    authorize_params: {
      scope: &amp;#39;openid profile&amp;#39;
    }
  )
end
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;As you can see here, we are using &lt;a href=&quot;https://api.rubyonrails.org/classes/Rails/Application.html#method-i-config_for&quot;&gt;&lt;code&gt;Rails.application.config_for&lt;/code&gt;&lt;/a&gt; to load up the content of a YAML file as a convenient hash. We could replace this with any secret handling library, including Rails encrypted credentials storage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The provider name (&lt;code&gt;:auth0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The three items from the YAML file, read and used through the &lt;code&gt;AUTH0_CONFIG&lt;/code&gt; hash.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;callback_path&lt;/code&gt; key and value, matching the one we configured in the Auth0 interface.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;authorize_params&lt;/code&gt; and the &lt;code&gt;scope&lt;/code&gt; key inside it; we will come back to that later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We need to create the interface and routing elements to allow for authentication.&lt;/p&gt;
&lt;h2&gt;Setting Up Routes and UI with Tailwind&lt;/h2&gt;
&lt;p&gt;In this example, we will work with a Ruby on Rails application using version 7.1 of the framework with TailwindCSS.&lt;/p&gt;
&lt;p&gt;Use the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails new -d sqlite3 -c tailwind -T myApp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then add two controllers with the index action, preparing to test the authentication process:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;bin/rails g controller public index
bin/rails g controller private index
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With those two commands, the following are created:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Both &lt;code&gt;public_controller.rb&lt;/code&gt; and &lt;code&gt;private_controller.rb&lt;/code&gt; controllers, with the index action ready to use&lt;/li&gt;
&lt;li&gt;Both related routes&lt;/li&gt;
&lt;li&gt;The related views&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s add the Auth0 controller to handle the callbacks and failures. Create the controller file (&lt;code&gt;bin/rails g controller auth0&lt;/code&gt;) and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# ./app/controllers/auth0_controller.rb
class Auth0Controller &amp;lt; ApplicationController

  # this will happen in case of success
  def callback
    auth_info = request.env[&amp;#39;omniauth.auth&amp;#39;]
    session[:userinfo] = auth_info[&amp;#39;extra&amp;#39;][&amp;#39;raw_info&amp;#39;]

    redirect_to &amp;#39;/private/index&amp;#39;
  end

  # this will happen in case of failure
  def failure
    @error_msg = request.params[&amp;#39;message&amp;#39;]
    redirect_to &amp;#39;/public_index&amp;#39;
  end

  # logout route
  def logout
    reset_session
    redirect_to logout_url, allow_other_host: true
  end

  private

  def logout_url
    request_params = {
      returnTo: root_url,
      client_id: AUTH0_CONFIG[&amp;#39;auth0_client_id&amp;#39;]
    }

    URI::HTTPS.build(host: AUTH0_CONFIG[&amp;#39;auth0_domain&amp;#39;], path: &amp;#39;/v2/logout&amp;#39;, query: request_params.to_query).to_s
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then update the routes to use those three actions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;./config/routes.rb
Rails.application.routes.draw do
  # ..
  get &amp;#39;/auth/auth0/callback&amp;#39; =&amp;gt; &amp;#39;auth0#callback&amp;#39;
  get &amp;#39;/auth/failure&amp;#39; =&amp;gt; &amp;#39;auth0#failure&amp;#39;
  get &amp;#39;/auth/logout&amp;#39; =&amp;gt; &amp;#39;auth0#logout&amp;#39;

  root &amp;quot;public#index&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we can add &lt;code&gt;Login&lt;/code&gt; and &lt;code&gt;Logout&lt;/code&gt; buttons in the public and private views, respectively:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/public/index.html.erb
&amp;lt;div&amp;gt;
  &amp;lt;h1 class=&amp;quot;font-bold text-4xl&amp;quot;&amp;gt;Public#index&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;Find me in app/views/public/index.html.erb&amp;lt;/p&amp;gt;
  &amp;lt;%= button_to &amp;#39;Login&amp;#39;, &amp;#39;/auth/auth0&amp;#39;, method: :post, data: { turbo: false }, class: &amp;quot;rounded bg-sky-500 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-sky-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500&amp;quot; %&amp;gt;
&amp;lt;/div&amp;gt;

# app/views/private/index.html.erb
&amp;lt;div&amp;gt;
  &amp;lt;h1 class=&amp;quot;font-bold text-4xl&amp;quot;&amp;gt;Private#index&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;Find me in app/views/private/index.html.erb&amp;lt;/p&amp;gt;
  &amp;lt;%= button_to &amp;#39;Logout&amp;#39;, &amp;#39;/auth0/logout&amp;#39;, method: :get, data: { turbo: false }, class: &amp;quot;rounded bg-sky-500 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-sky-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500&amp;quot; %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now go to &amp;#39;&lt;a href=&quot;http://localhost:3000&quot;&gt;http://localhost:3000&lt;/a&gt;&amp;#39;. Use the &lt;code&gt;Login&lt;/code&gt; button and you&amp;#39;ll be redirected to the &lt;em&gt;private&lt;/em&gt; page. There, you will find a &lt;code&gt;Logout&lt;/code&gt; button.&lt;/p&gt;
&lt;p&gt;A couple of things are missing, though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data from a user&amp;#39;s profile&lt;/li&gt;
&lt;li&gt;Identifying a user and handling authorization&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Working with Scopes and Data&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s take a few steps back and bring back the initializer (&lt;code&gt;config/initializers/auth0.rb&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;AUTH0_CONFIG = Rails.application.config_for(:auth0)

Rails.application.config.middleware.use OmniAuth::Builder do
  provider(
    :auth0,
    AUTH0_CONFIG[&amp;#39;auth0_client_id&amp;#39;],
    AUTH0_CONFIG[&amp;#39;auth0_client_secret&amp;#39;],
    AUTH0_CONFIG[&amp;#39;auth0_domain&amp;#39;],
    callback_path: &amp;#39;/auth/auth0/callback&amp;#39;,
    authorize_params: {
      scope: &amp;#39;openid profile&amp;#39;
    }
  )
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The critical piece here is the scope: &lt;code&gt;openid profile&lt;/code&gt;. This tells Auth0 we are interested in a few pieces of information, namely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The provider name (Auth0)&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;uid&lt;/code&gt;: A unique identifier to match a user&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;info&lt;/code&gt; hash: Containing a name, URL to a profile picture, and an empty &amp;#39;email&amp;#39; key&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;extra_info&lt;/code&gt; hash: Again, with a name and profile picture, but also a pair of given and family names&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Read more on the topic of &lt;a href=&quot;https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes&quot;&gt;scopes in Auth0&amp;#39;s documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The above information is valuable, and you should ensure that, at least upon first login, you copy that data into a table in your application.&lt;/p&gt;
&lt;p&gt;To do so, we need to use the content of the request-response in the &lt;code&gt;auth0_controller&lt;/code&gt; and, specifically, in the &lt;code&gt;callback&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def callback
  # all the data sent by auth0
  auth_info = request.env[&amp;#39;omniauth.auth&amp;#39;]

  # the data that really identifies the user
  session[:userinfo] = auth_info[&amp;#39;extra&amp;#39;][&amp;#39;raw_info&amp;#39;]

  redirect_to &amp;#39;/private/index&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use a breakpoint before the redirect to dig into the hash and experiment.&lt;/p&gt;
&lt;p&gt;We usually want the email address too. To get it, you can update the scope:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scope: &amp;#39;openid profile email&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After reloading the application server and going through the login steps, you will need to give the application access to additional information.&lt;/p&gt;
&lt;p&gt;We can now do something like this in the &lt;code&gt;callback&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def callback
  # all the data sent by auth0
  auth_info = request.env[&amp;#39;omniauth.auth&amp;#39;]

  # the data that really identify the user
  session[:userinfo] = auth_info[&amp;#39;extra&amp;#39;][&amp;#39;raw_info&amp;#39;]

  user = User.find_by(email: session[:userinfo][&amp;#39;email&amp;#39;] || User.new
  if user.new_record?
    user.name = session[:userinfo][&amp;#39;name&amp;#39;]
    user.email = session[:userinfo][&amp;#39;email&amp;#39;]
  end
  user.save

  redirect_to &amp;#39;/private/index&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will ensure we have a local, up-to-date profile for the user.&lt;/p&gt;
&lt;h2&gt;A Word On Sessions&lt;/h2&gt;
&lt;p&gt;A session is a special hash in a Ruby on Rails application. It has limited storage that is accessible from the controllers and views and unique to each visitor.&lt;/p&gt;
&lt;p&gt;We can &amp;quot;open&amp;quot; and &amp;quot;close&amp;quot; sessions. If we need a specific dataset, we can decide that a session is open. If it has no data, then it&amp;#39;s closed.&lt;/p&gt;
&lt;p&gt;Remember the following lines in our Auth0 controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# in the callback method
session[:userinfo] = auth_info[&amp;#39;extra&amp;#39;][&amp;#39;raw_info&amp;#39;]

# in the logout method
reset_session
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first one writes the value in the &lt;code&gt;raw_info&lt;/code&gt; key of the hash to the session hash, while the second one just clears up the session hash.&lt;/p&gt;
&lt;p&gt;We can then define the following helper method in the &lt;code&gt;ApplicationHelper&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# get the user
def current_user
  User.find_by(email: session[:userinfo][&amp;#39;email&amp;#39;]) if session[:userinfo]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A Security Concern&lt;/h2&gt;
&lt;p&gt;Now let&amp;#39;s use this as a concern for our controllers. The idea is to define a controller that requires users to be logged in to access it, such as our &lt;code&gt;private_controller&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can write the following concern file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# ./app/controllers/concerns/secured.rb
module Secured
  extend ActiveSupport::Concern

  included do
    before_action :logged_in?
  end

  def logged_in?
    redirect_to &amp;#39;/&amp;#39; unless session[:userinfo].present?
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then use it in our controllers like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class PrivateController &amp;lt; ApplicationController
  include Secured

  def index
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a visitor isn&amp;#39;t logged in, but then tries to open up the &lt;code&gt;/private/index&lt;/code&gt; page, they will automatically be redirected to the site&amp;#39;s root.&lt;/p&gt;
&lt;h2&gt;Integrating with Other Providers&lt;/h2&gt;
&lt;p&gt;You can rely on multiple providers through Auth0. However, Google is defined as the default. For many companies, that&amp;#39;s enough.&lt;/p&gt;
&lt;p&gt;If you need more providers, head to the &lt;em&gt;Authentication&lt;/em&gt; menu for the related tenant in Auth0, and then the &lt;em&gt;Social&lt;/em&gt; submenu, where you can set up additional providers.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s also worth noting that you can configure multi-factor authentication (MFA) with Auth0 too.&lt;/p&gt;
&lt;p&gt;The process will remain the same except for those configuration steps. Our application will only return visitor data to prove that a visitor has been authenticated.&lt;/p&gt;
&lt;h2&gt;Opening Up Towards Authorization&lt;/h2&gt;
&lt;p&gt;Now we can authenticate users and send their information to our application through a callback. We have already seen how to get a user&amp;#39;s email address out of a hash and find the relevant user in our database.&lt;/p&gt;
&lt;p&gt;From there, we can define authorization policies.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/varvet/pundit&quot;&gt;Pundit&lt;/a&gt; is a great choice to define and use authorization policies, yet relies on checking a user&amp;#39;s role.&lt;/p&gt;
&lt;p&gt;Using a &lt;code&gt;role&lt;/code&gt; attribute is, in fact, the easiest method. You can fill it in when creating a user in the Rails console or your application&amp;#39;s back-end interface, before a user&amp;#39;s first login attempt.&lt;/p&gt;
&lt;p&gt;Or you can get a list of admins&amp;#39; emails and match a user&amp;#39;s email against the list when creating the user.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ADMINS = [&amp;#39;johndoe@example.com&amp;#39;]

def find_or_create_user(email:, name:)
  user = User.find_by(email: session[:userinfo][&amp;#39;email&amp;#39;] || User.new
  if user.new_record?
    user.name = session[:userinfo][&amp;#39;name&amp;#39;]
    user.email = session[:userinfo][&amp;#39;email&amp;#39;]
  end
  if ADMINS.include?(user.email)
    user.role = &amp;#39;admin&amp;#39;
  else
    user.role = &amp;#39;normal&amp;#39;
  end

  user.save
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&amp;#39;s it!&lt;/p&gt;
&lt;h2&gt;What We&amp;#39;ve Covered&lt;/h2&gt;
&lt;p&gt;In this article, we set up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A tenant and application in Auth0&lt;/li&gt;
&lt;li&gt;Auth0-related gems in a Ruby on Rails application&lt;/li&gt;
&lt;li&gt;Routes and views in the application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We then:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reviewed the concept of a session and saw how to use it&lt;/li&gt;
&lt;li&gt;Created base tooling to check if a user is logged in&lt;/li&gt;
&lt;li&gt;Added a controller concern to secure controllers behind a login requirement&lt;/li&gt;
&lt;li&gt;Saw how to match users to their roles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Auth0 and other authentication providers can help you integrate state-of-the-art authentication in your Ruby on Rails application without writing much code. It&amp;#39;s often a much better option than relying on your own implementation of the authentication layer or even using gems like Devise.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Debugging in Ruby with Debug</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/07/03/debugging-in-ruby-with-debug.html"/>
    <id>https://blog.appsignal.com/2024/07/03/debugging-in-ruby-with-debug.html</id>
    <published>2024-07-03T00:00:00+00:00</published>
    <updated>2024-07-03T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll introduce the basics of the debug gem before diving into its more advanced features.</summary>
    <content type="html">&lt;p&gt;Debugging is a valuable skill for any software engineer to have. Unfortunately, most software engineers are not trained in it. And that&amp;#39;s not just specific to developers going through boot camps; even in universities, we are not often taught and trained to use a debugger.&lt;/p&gt;
&lt;p&gt;My teachers and mentors were more interested in getting me to write programs rather than debugging them. If we are fortunate, debugging comes at the end of the semester, in a short, last session.&lt;/p&gt;
&lt;p&gt;Luckily, we have tools that can help us with debugging. Since Ruby 3.1, Ruby ships with the debug gem, a powerful debugger.&lt;/p&gt;
&lt;p&gt;In this article, we will go through a quick overview of the gem. We&amp;#39;ll see how to use it for simple and more advanced cases.&lt;/p&gt;
&lt;h2&gt;Debugging Without A Debugger: What&amp;#39;s the Issue?&lt;/h2&gt;
&lt;p&gt;Many of us rely on what you might call &lt;em&gt;&amp;quot;printf debugging&amp;quot;&lt;/em&gt;: we add &lt;code&gt;puts&lt;/code&gt; (or its equivalent in the language we&amp;#39;re using) to the standard output (STDOUT). We include the current state of an object, variable, or just a string so we know if our program is going into specific branches of its logic tree.&lt;/p&gt;
&lt;p&gt;While helpful, this isn&amp;#39;t the most optimal way to debug a program. It often leads to many back-and-forth trips between your logs and the code, as you forget to add a &lt;code&gt;puts&lt;/code&gt; here and there, or leave in some debugging code.&lt;/p&gt;
&lt;p&gt;That method also relies on your own preconceptions about how the code is running and what is going on that&amp;#39;s different from what you might expect.&lt;/p&gt;
&lt;p&gt;Using a debugger is a very different experience. You add one or more breakpoints in the code where you want to know what&amp;#39;s happening. You then run the code and wait for it to hit the breakpoint.&lt;/p&gt;
&lt;p&gt;Then, you get a debugging console to check a variable&amp;#39;s values at the breakpoint location. You go back and forth in the execution steps.&lt;/p&gt;
&lt;p&gt;As we will see later, we can even add conditional breakpoints directly from the debugging console. This makes it easier to avoid exiting the debugging console, so you can add breakpoints you&amp;#39;ve forgotten about.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;Since &lt;a href=&quot;https://www.ruby-lang.org/en/news/2021/12/25/ruby-3-1-0-released/&quot;&gt;Ruby 3.1&lt;/a&gt;, a version of the &lt;code&gt;debug&lt;/code&gt; gem ships with Ruby. We recommend adding it to your &lt;code&gt;Gemfile&lt;/code&gt; so you&amp;#39;re using the latest version.&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;debug&lt;/code&gt; to your Gemfile and then run &lt;code&gt;bundle install&lt;/code&gt;. I recommend adding it to &lt;code&gt;development&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; groups for debugging tests too.&lt;/p&gt;
&lt;h2&gt;Basic Debugging Techniques with Debug for Ruby&lt;/h2&gt;
&lt;p&gt;Now let&amp;#39;s run through some simple debugging methods using &lt;code&gt;debug&lt;/code&gt;: using breakpoints, stepping, other commands, moving in the stack, and using a map. We&amp;#39;ll then examine the more advanced method of adding breakpoints on the fly.&lt;/p&gt;
&lt;h3&gt;Breakpoints&lt;/h3&gt;
&lt;p&gt;Breakpoints are calls that tell the debugger to stop. You can do this in modern IDEs that are integrated into a debugger with a simple click in the sidebar. The standard way is to add &lt;code&gt;binding.break&lt;/code&gt; at the line we want to stop at.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;debug&amp;#39;

class Hornet
  def initialize
    @colors = [:yellow, :red, :black]
  end

  def show_up
    binding.break   # debugger will stop here
    puts &amp;quot;bzzz&amp;quot;
  end
end

Hornet.new.show_up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By running this little program, we will get the following console output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[debug] ruby test.rb
[4, 13] in test.rb
     4|   def initialize
     5|     @colors = [:yellow, :red, :black]
     6|   end
     7|
     8|   def show_up
=&amp;gt;   9|     binding.break   # debugger will stop here
    10|     puts &amp;quot;bzzz&amp;quot;
    11|   end
    12| end
    13|
=&amp;gt;#0	Hornet#show_up at test.rb:9
  #1	&amp;lt;main&amp;gt; at test.rb:14
(ruby) @colors
[:yellow, :red, :black]
(rdgb)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we can access the instance variable from the breakpoint.&lt;/p&gt;
&lt;h2&gt;Stepping&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s dig into a more complex example using stepping.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Book
  attr_accessor :title, :author, :price

  def initialize(title, author, price)
    @title = title
    @author = author
    @price = price
  end
end

class BookStore
  def initialize
    @books = []
  end

  def add_book(book)
    @books &amp;lt;&amp;lt; book
  end

  def remove_book(title)
    @books.delete_if { |book| book.title == title }
  end

  def find_by_title(title)
    @books.find { |book| book.title.include?(title) }
  end
end

# Sample Usage:
store = BookStore.new
book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)

store.add_book(book1)
store.add_book(book2)
store.add_book(book3)

puts store.find_by_title(&amp;quot;Hobbit&amp;quot;).title
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;This example app manages books in a bookstore. But at the moment, we cannot be sure which book will be returned when we search titles containing &amp;#39;Hobbit&amp;#39;. It might well be &amp;quot;The Hobbit&amp;quot;, but it&amp;#39;s not certain.&lt;/p&gt;
&lt;p&gt;To help debug this, we&amp;#39;ll jump into the &lt;code&gt;find_by_title&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s add a breakpoint to one of the methods:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def find_by_title(title)
  binding.break
  @books.find { |book| book.title.include?(title) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then launch the program and get to the breakpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;@box [debug] ruby library.rb                                                                                                                          20:25:07
[22, 31] in library.rb
    22|   def remove_book(title)
    23|     @books.delete_if { |book| book.title == title }
    24|   end
    25|
    26|   def find_by_title(title)
=&amp;gt;  27|     binding.break
    28|     @books.find { |book| book.title.include?(title) }
    29|   end
    30| end
    31|
=&amp;gt;#0	BookStore#find_by_title(title=&amp;quot;Hobbit&amp;quot;) at library.rb:27
  #1	&amp;lt;main&amp;gt; at library.rb:42
(rdbg)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The top part of the console tells us which line and file we are at. We can then query the value of the &lt;code&gt;title&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(rdbg) title
&amp;quot;Hobbit&amp;quot;
(rdbg)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can run the code right in that context to see what&amp;#39;s happening:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(ruby) @books.find { |book| book.title.include?(title) }
#&amp;lt;Book:0x00007fd05e4d59f0 @author=&amp;quot;J.R.R. Tolkien&amp;quot;, @price=15.0, @title=&amp;quot;The Hobbit&amp;quot;&amp;gt;
(rdbg)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Here might be a good time to reflect on how you want the program you are building and this piece of code to behave. Expressing the code through RSpec tests might be an excellent way to clarify what it should do.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;#39;s now continue to the next breakpoint by using the &lt;code&gt;continue&lt;/code&gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(rdbg) continue    # command
The Hobbit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, it goes on until the end of the program.&lt;/p&gt;
&lt;h2&gt;More Commands to Assist Debugging&lt;/h2&gt;
&lt;p&gt;Of course, we can add more breakpoints to our code to stop at another place. But we can also use commands to move within the stack of our program without restarting it.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s add one more breakpoint to the &lt;code&gt;add_book&lt;/code&gt; method, just after instantiating the bookstore.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def add_book(book)
  binding.break
  @books &amp;lt;&amp;lt; book
end

# [ .. ]

store = BookStore.new
binding.break
book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when we run the program, it will stop before the &lt;code&gt;book1&lt;/code&gt; variable is instantiated. The &lt;code&gt;continue&lt;/code&gt; command will run the program until the next breakpoint or exit.&lt;/p&gt;
&lt;h3&gt;Using &lt;code&gt;next&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Instead of &lt;code&gt;continue&lt;/code&gt;, we can use the &lt;code&gt;next&lt;/code&gt; command, which will only run the next code line, so we can debug our app in smaller steps. We will need to run &lt;code&gt;next&lt;/code&gt; twice to run the line where &lt;code&gt;book1&lt;/code&gt; is defined before we can inspect it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[30, 39] in library.rb
    30|   end
    31| end
    32|
    33| # Sample Usage:
    34| store = BookStore.new
=&amp;gt;  35| binding.break
    36| book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
    37| book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
    38| book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
    39|
=&amp;gt;#0	&amp;lt;main&amp;gt; at library.rb:35
(ruby) book1
nil
(rdbg) next    # command
[31, 40] in library.rb
    31| end
    32|
    33| # Sample Usage:
    34| store = BookStore.new
    35| binding.break
=&amp;gt;  36| book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
    37| book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
    38| book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
    39|
    40| store.add_book(book1)
=&amp;gt;#0	&amp;lt;main&amp;gt; at library.rb:36
(ruby) book1
nil
(rdbg) next    # command
[32, 41] in library.rb
    32|
    33| # Sample Usage:
    34| store = BookStore.new
    35| binding.break
    36| book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
=&amp;gt;  37| book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
    38| book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
    39|
    40| store.add_book(book1)
    41| store.add_book(book2)
=&amp;gt;#0	&amp;lt;main&amp;gt; at library.rb:37
(ruby) book1
#&amp;lt;Book:0x00007f50fb5f9da0 @author=&amp;quot;Frank Herbert&amp;quot;, @price=20.0, @title=&amp;quot;Dune&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each &lt;code&gt;next&lt;/code&gt; call will run the next line. But it will not step into the code called by &lt;code&gt;Book.new&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Using &lt;code&gt;step&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;In some cases, we may know that an issue lies within a specific call. The &lt;code&gt;step&lt;/code&gt; command is great for debugging this.&lt;/p&gt;
&lt;p&gt;For example, when we are at line 37, we can use &lt;code&gt;step&lt;/code&gt; to follow the execution of the &lt;code&gt;Book&lt;/code&gt; object that fills the &lt;code&gt;book2&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(rdbg) step    # command
[2, 11] in library.rb
     2|
     3| class Book
     4|   attr_accessor :title, :author, :price
     5|
     6|   def initialize(title, author, price)
=&amp;gt;   7|     @title = title
     8|     @author = author
     9|     @price = price
    10|   end
    11| end
=&amp;gt;#0	Book#initialize(title=&amp;quot;The Hobbit&amp;quot;, author=&amp;quot;J.R.R. Tolkien&amp;quot;, price=15.0) at library.rb:7
  #1	[C] Class#new at library.rb:37
  # and 1 frames (use `bt&amp;#39; command for all frames)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;step&lt;/code&gt; command brings us directly to the first line of the &lt;code&gt;initialize&lt;/code&gt; method in the &lt;code&gt;Book&lt;/code&gt; class. (If you are new to Ruby, the &lt;code&gt;new&lt;/code&gt; class method is called the &lt;code&gt;initialize&lt;/code&gt; method after it does some internal work). Now we can use the next step from within that method and follow the trail.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next&lt;/code&gt; and &lt;code&gt;step&lt;/code&gt; are crucial to get familiar with, as they allow us to move forward at different levels and speeds.&lt;/p&gt;
&lt;h3&gt;Moving In the Stack&lt;/h3&gt;
&lt;p&gt;We can move up and down (or backward and forwards) in the stack by using the &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; commands. Calling &lt;code&gt;up&lt;/code&gt; twice will get us back to line 37:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[2, 11] in library.rb
     2|
     3| class Book
     4|   attr_accessor :title, :author, :price
     5|
     6|   def initialize(title, author, price)
=&amp;gt;   7|     @title = title
     8|     @author = author
     9|     @price = price
    10|   end
    11| end
=&amp;gt;#0	Book#initialize(title=&amp;quot;The Hobbit&amp;quot;, author=&amp;quot;J.R.R. Tolkien&amp;quot;, price=15.0) at library.rb:7
  #1	[C] Class#new at library.rb:37
  # and 1 frames (use `bt&amp;#39; command for all frames)
(rdbg) up    # command
# No sourcefile available for library.rb
=&amp;gt;#1	[C] Class#new at library.rb:37
(rdbg) up    # command
=&amp;gt;  37| book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to call it twice as we skipped over one step, thanks to the &lt;code&gt;next&lt;/code&gt; command: the call to the parent class of the &lt;code&gt;Book&lt;/code&gt; class: &lt;code&gt;Class&lt;/code&gt; itself (and the &lt;code&gt;new&lt;/code&gt; method).&lt;/p&gt;
&lt;h3&gt;Using a Map&lt;/h3&gt;
&lt;p&gt;When we start to use &lt;code&gt;up&lt;/code&gt; , &lt;code&gt;down&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt;, and &lt;code&gt;step&lt;/code&gt;, it&amp;#39;s handy to know two more commands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list&lt;/code&gt;: to show where we are in the code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bt&lt;/code&gt; (or &lt;code&gt;backtrace&lt;/code&gt;): to show the trace of the steps we have followed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, when we are at line 37, the &lt;code&gt;bt&lt;/code&gt; command displays the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(rdbg) bt    # backtrace command
  #0	Book#initialize(title=&amp;quot;The Hobbit&amp;quot;, author=&amp;quot;J.R.R. Tolkien&amp;quot;, price=15.0) at library.rb:7
  #1	[C] Class#new at library.rb:37
=&amp;gt;#2	&amp;lt;main&amp;gt; at library.rb:37
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling &lt;code&gt;down&lt;/code&gt; twice brings us to step &lt;code&gt;#0&lt;/code&gt;. We can also pass an additional integer to both &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; to move through as many steps as we want to in one go.&lt;/p&gt;
&lt;h3&gt;Knowing What&amp;#39;s Available&lt;/h3&gt;
&lt;p&gt;A very practical command to know is &lt;code&gt;ls&lt;/code&gt;. It will list the variables and methods available to you at your current point in the stack.&lt;/p&gt;
&lt;p&gt;For example, on line 37, we see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(rdbg) ls    # outline command
Object.methods: inspect  to_s
locals: book1  book2  book3  store
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using &lt;code&gt;finish&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;We can go to our next breakpoint using &lt;code&gt;continue&lt;/code&gt;. However, the &lt;code&gt;finish&lt;/code&gt; or &lt;code&gt;fin&lt;/code&gt; command will also bring us to the next breakpoint, or to the end of our program.&lt;/p&gt;
&lt;p&gt;You can exit more quickly with &lt;code&gt;Ctrl-D&lt;/code&gt; or &lt;code&gt;quit&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Adding Breakpoints On the Fly&lt;/h2&gt;
&lt;p&gt;A more advanced practice is to add breakpoints on the fly while running the debugger.&lt;/p&gt;
&lt;p&gt;We have different ways to do that. Let&amp;#39;s start with some more simple ways to add a breakpoint:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To a specific line — &lt;code&gt;break &amp;lt;line number&amp;gt;&lt;/code&gt; — in the current file.&lt;/li&gt;
&lt;li&gt;To the start of a specific method in a specific class: &lt;code&gt;break ClassName#method_name&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(rdbg) break 38    # command
#0  BP - Line  /mnt/data/Code/clients/AppSignal/debug/library.rb:38 (line)
(rdbg) break BookStore#find_by_title    # command
#1  BP - Method  BookStore#find_by_title at library.rb:27
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Called on its own, the &lt;code&gt;break&lt;/code&gt; command will list the existing breakpoints (the ones added through the debug console):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(rdbg) break    # command
#0  BP - Line  /mnt/data/Code/clients/AppSignal/debug/library.rb:38 (line)
#1  BP - Method  BookStore#find_by_title at library.rb:27
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also remove breakpoints that are added this way using the &lt;code&gt;del&lt;/code&gt; or &lt;code&gt;delete&lt;/code&gt; command:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;del&lt;/code&gt; will remove all breakpoints in one go (confirmation is needed).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;del X&lt;/code&gt; deletes breakpoints numbered X in the breakpoints list.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Adding Conditions&lt;/h3&gt;
&lt;p&gt;You can also add conditions when setting a breakpoint. Imagine a method that goes wrong when the book title is &amp;quot;Germinal&amp;quot;, but that goes ok if it&amp;#39;s &amp;quot;Notre Dame&amp;quot;. In this case, we can add a breakpoint on the method, but only if the book title matches.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(rdbg) break BookStore#find_by_title if: book1.title == &amp;quot;Germinal&amp;quot;    # command
#1  BP - Method  BookStore#find_by_title at library.rb:27 if: book1.title == &amp;quot;Germinal&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Integration with IDEs&lt;/h2&gt;
&lt;p&gt;Many of us rely on modern IDEs and text editors that have support for direct debugging. A good choice is &lt;code&gt;rdbg&lt;/code&gt;: it integrates well with many IDEs.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ruby/debug#use-rdbg-with-commands-written-in-ruby&quot;&gt;Check the debug README for more details on &lt;code&gt;rdbg&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Recap and Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we covered the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installing &lt;code&gt;debug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Adding breakpoints from your favorite code editor with &lt;code&gt;binding.break&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Looking at the value of variables and objects from a debugger session&lt;/li&gt;
&lt;li&gt;Navigating within execution frames from the debugger console (with &lt;code&gt;up&lt;/code&gt;, &lt;code&gt;down&lt;/code&gt;, and &lt;code&gt;next&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Listing available variables and methods at any point in the console with &lt;code&gt;ls&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Adding breakpoints and conditional breakpoints on the fly from the debugger console&lt;/li&gt;
&lt;li&gt;Listing and removing breakpoints (with &lt;code&gt;break&lt;/code&gt;, &lt;code&gt;delete &amp;lt;number&amp;gt;&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ending a debugging session with &lt;code&gt;finish&lt;/code&gt;, &lt;code&gt;continue&lt;/code&gt;, or &lt;code&gt;quit&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;help&lt;/code&gt; command also provides plenty of details on the commands we have seen here and more. You can run &lt;code&gt;help break&lt;/code&gt; (for example) to learn more about the &lt;code&gt;break&lt;/code&gt; command and its subcommands.&lt;/p&gt;
&lt;p&gt;In conclusion, the &lt;code&gt;debug&lt;/code&gt; tool will greatly help you with debugging over the years.&lt;/p&gt;
&lt;p&gt;Most debuggers use similar commands, so don&amp;#39;t hesitate to try others out too (check out our post on &lt;a href=&quot;https://blog.appsignal.com/2024/05/08/debugging-in-ruby-with-pry-byebug.html&quot;&gt;pry-byebug&lt;/a&gt;, for example).&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Monitor the Performance of Your Ruby on Rails Application Using AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/06/12/monitor-the-performance-of-your-ruby-on-rails-application-using-appsignal.html"/>
    <id>https://blog.appsignal.com/2024/06/12/monitor-the-performance-of-your-ruby-on-rails-application-using-appsignal.html</id>
    <published>2024-06-12T00:00:00+00:00</published>
    <updated>2024-06-12T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the second part of this two-part series, we&#039;ll use AppSignal to monitor the performance of our Ruby on Rails application.</summary>
    <content type="html">&lt;p&gt;In the first part of this article series, we deployed a simple Ruby on Rails application to DigitalOcean&amp;#39;s app platform. We also hooked up a Rails app to AppSignal, seeing how simple errors are tracked and displayed in AppSignal&amp;#39;s Errors dashboard.&lt;/p&gt;
&lt;p&gt;In this part of the series, we&amp;#39;ll dive into how to set up the following for your Ruby on Rails application using AppSignal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance monitoring&lt;/li&gt;
&lt;li&gt;Rails background jobs monitoring, including how to monitor simple API calls&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Notification alerts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get into it!&lt;/p&gt;
&lt;h2&gt;Monitoring Rails App Performance Using AppSignal&lt;/h2&gt;
&lt;p&gt;When your uptime monitor shows that your app is up, it may be tempting to think that everything is okay. But, in reality, trouble could be brewing under the hood in the form of slow processes, unoptimized database queries, and long-running service calls.&lt;/p&gt;
&lt;p&gt;This is a very important matter when you consider that there&amp;#39;s a &lt;a href=&quot;https://www.thinkwithgoogle.com/marketing-strategies/app-and-mobile/mobile-page-speed-conversion-data/&quot;&gt;correlation between slow-loading web pages and a low visitor conversion rate&lt;/a&gt;. In a nutshell, those slow-running processes will cost you a lot if left unchecked. But with so many moving parts to a Ruby on Rails app, the important questions are: what should you look for, and where?&lt;/p&gt;
&lt;p&gt;That&amp;#39;s where AppSignal comes into play. Let&amp;#39;s look at how you can use AppSignal to keep track of your Rails app&amp;#39;s performance.&lt;/p&gt;
&lt;h3&gt;Tracking Response Times&lt;/h3&gt;
&lt;p&gt;From the default dashboard, AppSignal provides two graphs that can give you a quick overview of how slow (or fast) your Rails app is running: the &lt;em&gt;Throughput&lt;/em&gt; and &lt;em&gt;Response time&lt;/em&gt; graphs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/appsignal-performance-tracking-dashboard.png&quot; alt=&quot;Rails performance tracking with Appsignal&quot;/&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Throughput&lt;/strong&gt; - This basically measures how many requests per second your app is currently processing (not to be confused with how many requests per second your app can handle overall). The basic rule of thumb is: the more requests per second, the better. However, if your app server handles too many requests per second, it might be time to scale your server resources to handle this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Response time&lt;/strong&gt; - The average time a browser response takes in milliseconds. The more time a response takes, the worse your app is running. A good rule of thumb here is that if your Rails web app has sub-100ms response times, you can consider it fast, while 300ms+ response times can be considered slow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, let&amp;#39;s say we want to track our app&amp;#39;s response times and throughput over a seven-day window. That&amp;#39;s very easy to do — just go to the &lt;em&gt;Graphs&lt;/em&gt; sub-menu under &lt;em&gt;Performance&lt;/em&gt; located on the left-side menu, then use the time filter buttons on top of the graphs, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/appsignal-performance-graphs.png&quot; alt=&quot;Performance graphs&quot;/&gt;&lt;/p&gt;
&lt;p&gt;With that, you can easily see if your app runs slow on some days compared to others.&lt;/p&gt;
&lt;p&gt;While on this view, it&amp;#39;s also important to check out the &lt;em&gt;Event groups&lt;/em&gt; graph, located below the &lt;em&gt;Response time&lt;/em&gt; and &lt;em&gt;Throughput&lt;/em&gt; graphs:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/event-groups-breakdown.png&quot; alt=&quot;Event groups breakdown&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This graph gives you details on how fast or slow your app is running in the controller layer and the view layer. From here, you can find out what&amp;#39;s causing slow response times and fix the issues accordingly.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s now switch gears to tracking database queries using AppSignal.&lt;/p&gt;
&lt;h3&gt;Tracking Database Queries&lt;/h3&gt;
&lt;p&gt;It is generally true that you can squeeze a lot more speed from your app by optimizing response times and throughput. However, more often than not, slow, unoptimized database queries are the worst offenders when it comes to sluggish app behavior.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take an example of the infamous N+1 query. In the &lt;a href=&quot;https://github.com/iamaestimo/expense-tracker&quot;&gt;expense tracker app&lt;/a&gt; introduced in part 1, the index method in the expenses controller looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controller/expenses_controller.rb

class ExpensesController &amp;lt; ApplicationController
  ...
  def index
    @expenses = Expense.all
  end
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This might look innocent, but if you take a look at the &lt;em&gt;Issues&lt;/em&gt; list in your AppSignal dashboard, you&amp;#39;ll see something interesting:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/n1-query-issue-list.png&quot; alt=&quot;N+1 Query issue&quot;/&gt;&lt;/p&gt;
&lt;p&gt;An N+1 query has been highlighted in the expenses controller&amp;#39;s index method.&lt;/p&gt;
&lt;p&gt;If we dig further by clicking on the issue link, we get to an &lt;em&gt;Issue details&lt;/em&gt; screen like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/slow-query-explanation.png&quot; alt=&quot;Slow query explanation&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Notice how AppSignal offers an explanation of the issue, including a link to a more detailed blog post showing you how to deal with it.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s move on and learn how AppSignal helps you out with background jobs.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Keeping Track of Rails Background Jobs with AppSignal&lt;/h3&gt;
&lt;p&gt;Background jobs are a common feature in many production Ruby on Rails applications, with a variety of job queue processing gems and libraries for you to choose from.&lt;/p&gt;
&lt;p&gt;AppSignal supports tracking and monitoring various background job processing libraries, including Sidekiq, Que, Delayed Job, Resque, and others.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say we want a feature where users get daily currency exchange rates in their dashboard.&lt;/p&gt;
&lt;p&gt;For this to work, we need an API call to a service providing such rates (we&amp;#39;ll use one that doesn&amp;#39;t require too much upfront configuration or payment, like &lt;a href=&quot;https://www.exchangerate-api.com/docs/free&quot;&gt;this one&lt;/a&gt;, to fetch currency rates). We also need a background job to queue the call to this service at regular intervals.&lt;/p&gt;
&lt;p&gt;The background processing gem we&amp;#39;ll use in the expense tracker app is &lt;a href=&quot;https://github.com/bensheldon/good_job&quot;&gt;GoodJob&lt;/a&gt;, but you&amp;#39;re free to use whatever suits you.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s create a background job to fetch rates:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/jobs/fetch_rates.rb
...
class FetchRates &amp;lt; ApplicationJob
  self.queue_adapter = :good_job

  def perform
    # fetch rates
    response = HTTParty.get(&amp;#39;https://open.er-api.com/v6/latest/USD&amp;#39;)

    if response[&amp;#39;result&amp;#39;] == &amp;quot;success&amp;quot;
      Rate.create(
        base_rate_name: response[&amp;#39;base_code&amp;#39;],
        eur: response[&amp;#39;rates&amp;#39;][&amp;#39;EUR&amp;#39;],
        cad: response[&amp;#39;rates&amp;#39;][&amp;#39;CAD&amp;#39;],
        aud: response[&amp;#39;rates&amp;#39;][&amp;#39;AUD&amp;#39;],
        gbp: response[&amp;#39;rates&amp;#39;][&amp;#39;GBP&amp;#39;]
        )
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then set GoodJob to run a cron for this job every few minutes (or however you see fit). AppSignal automatically detects the presence of the background service and includes a nifty filter so you can separate it from the default web dashboards:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/background-jobs-dashboards.png&quot; alt=&quot;Background jobs dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Without the need for much tooling upfront, AppSignal will also detect API calls and keep track of any issues:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/background-jobs-slow-events.png&quot; alt=&quot;Slow events&quot;/&gt;&lt;/p&gt;
&lt;p&gt;If you want to dive deeper into monitoring Rails background jobs with AppSignal, I recommend you &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/active-job.html&quot;&gt;check out the documentation&lt;/a&gt;. For now, let&amp;#39;s turn our attention to uptime monitoring.&lt;/p&gt;
&lt;h2&gt;Uptime Monitoring&lt;/h2&gt;
&lt;p&gt;Another matter that concerns many Rails developers is the continuous availability of their application for end users. Any downtime might mean loss of revenue and have other negative consequences.&lt;/p&gt;
&lt;p&gt;Even so, you wouldn&amp;#39;t want to poll the uptime status of your app manually. You can set up uptime monitoring using AppSignal.&lt;/p&gt;
&lt;p&gt;Setting it up is a breeze. Begin by clicking on the &lt;em&gt;Uptime monitoring&lt;/em&gt; link in the left-side menu.&lt;/p&gt;
&lt;p&gt;Then, when you click on the &lt;em&gt;create uptime monitor&lt;/em&gt; button, you should get a dialog similar to the one shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/uptime-monitor-1.png&quot; alt=&quot;Create uptime monitor - one&quot;/&gt;
&lt;img src=&quot;/images/blog/2024-06/uptime-monitor-2.png&quot; alt=&quot;Create uptime monitor - two&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Complete your uptime monitor by adding the most important settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt; - Give your uptime monitor an appropriate name.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt; - Provide the full URL to the uptime route. Beginning with Rails 7.1, the default uptime route is &lt;em&gt;&lt;a href=&quot;https://your-app-domain/up&quot;&gt;https://your-app-domain/up&lt;/a&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Region&lt;/strong&gt; - Select the regions where you want to check for uptime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notification&lt;/strong&gt; - Choose the notification channel/s to receive alerts on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that done, go ahead and create the monitor!&lt;/p&gt;
&lt;p&gt;One thing to note with the AppSignal uptime monitor is that since polling is done almost every minute or so, it&amp;#39;s very easy to hit your plan&amp;#39;s limits. But there&amp;#39;s a very good workaround to this which you can read more about &lt;a href=&quot;https://docs.appsignal.com/uptime-monitoring/setup.html&quot;&gt;in their docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before moving on to how AppSignal can help with logging, there&amp;#39;s another important feature to complete the uptime monitoring layer: free &lt;a href=&quot;https://docs.appsignal.com/uptime-monitoring/public-status-page.html#set-up&quot;&gt;public status pages&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Click on the &lt;em&gt;creating a public status&lt;/em&gt; page link as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/public-status-page-1.png&quot; alt=&quot;Creating a public status page - step 1&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Then click on the &lt;em&gt;New status page&lt;/em&gt; button, bringing you to this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/public-status-page-3.png&quot; alt=&quot;Creating a public status page - step 3&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/public-status-page-4.png&quot; alt=&quot;Creating a public status page - step 4&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Go ahead and fill in all the required information. Note that if you use a custom domain, you&amp;#39;ll need to add a CNAME directive pointing to &lt;code&gt;cname.appsignal-status.com&lt;/code&gt; in your custom domain&amp;#39;s DNS settings.&lt;/p&gt;
&lt;h2&gt;Logging with AppSignal&lt;/h2&gt;
&lt;p&gt;Logging is a relatively new feature in AppSignal, but one that is timely and welcome. Using AppSignal, you don&amp;#39;t have to run application monitoring and logging as separate services.&lt;/p&gt;
&lt;p&gt;To get started with logging, make sure you are running the latest version of the AppSignal gem. If you have an older version of the gem, update it with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle update appsignal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create a new initializer and edit it like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/appsignal_logging.rb

appsignal_logger = Appsignal::Logger.new(&amp;quot;rails&amp;quot;)
Rails.logger.broadcast_to(appsignal_logger)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can access your app&amp;#39;s logs like so:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/logging-dashboard.png&quot; alt=&quot;Logging dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Notice that you also get a nice filter to filter log records by severity, as well as access to live logs.&lt;/p&gt;
&lt;p&gt;You can go through &lt;a href=&quot;https://docs.appsignal.com/logging/platforms/integrations/ruby.html&quot;&gt;AppSignal&amp;#39;s logging for Ruby documentation here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Other Notable Features&lt;/h2&gt;
&lt;p&gt;There are a couple of other notable features you can use in AppSignal: anomaly detection and custom metrics.&lt;/p&gt;
&lt;h3&gt;Anomaly Detection&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s say you are concerned with your app running out of memory. You can easily set up a trigger that sends an email notification if your app has a notable drop in available memory.&lt;/p&gt;
&lt;p&gt;Go to the &lt;em&gt;Anomaly detection&lt;/em&gt; link and create a new trigger, then configure it:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-06/trigger-for-low-memory.png&quot; alt=&quot;Trigger for low memory&quot;/&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Metric&lt;/strong&gt; - In my case, I am interested in memory usage, but you could choose any other metric that is specific to your use case.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trigger details&lt;/strong&gt; - Here, you&amp;#39;ll configure the details relevant to your trigger. In my case, the trigger will fire if the host memory goes below 200MB.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Custom Metrics&lt;/h3&gt;
&lt;p&gt;AppSignal also gives you the tools to capture and visualize custom metrics. This feature alone warrants an entire article. You can read all about it in &lt;a href=&quot;https://blog.appsignal.com/2023/04/26/how-to-monitor-custom-metrics-with-appsignal.html&quot;&gt;How to Monitor Custom Metrics with AppSignal&lt;/a&gt; and &lt;a href=&quot;https://docs.appsignal.com/metrics/custom.html&quot;&gt;check out AppSignal&amp;#39;s docs&lt;/a&gt; too.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In part one of this article series, we set up a Rails app hosted on DigitalOcean and monitored it for errors using AppSignal.&lt;/p&gt;
&lt;p&gt;In this second and final part, we looked at some of AppSignal&amp;#39;s great features for your Rails app, including performance monitoring, uptime monitoring, logging, and more.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s so much more to AppSignal than could effectively be covered by this article series. I highly encourage you to check it out for your Rails app.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Use Tailwind CSS for Your Ruby On Rails Project</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/06/05/how-to-use-tailwind-css-for-your-ruby-on-rails-project.html"/>
    <id>https://blog.appsignal.com/2024/06/05/how-to-use-tailwind-css-for-your-ruby-on-rails-project.html</id>
    <published>2024-06-05T00:00:00+00:00</published>
    <updated>2024-06-05T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s see how we can use Tailwind CSS in a Rails application and explore its utility-first approach.</summary>
    <content type="html">&lt;p&gt;It&amp;#39;s hard to overstate the importance of Cascading Style Sheets (CSS) for all websites. Since the first CSS standards were published in late 1996, we have come quite far regarding features and ecosystems.&lt;/p&gt;
&lt;p&gt;Several frameworks have appeared and proved popular, one of the most recent being Tailwind CSS.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll first examine Tailwind&amp;#39;s utility-first approach before diving into how to use it in a Ruby on Rails application. You will see how Tailwind helps you to build excellent websites without the need for custom CSS and long debugging sessions.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Tailwind CSS: A Utility-First Approach&lt;/h2&gt;
&lt;p&gt;Most CSS frameworks (Foundation, Bootstrap, or Bulma, for example) provide ready-to-use components such as buttons and form fields, so you can quickly assemble blocks to shape an interface.&lt;/p&gt;
&lt;p&gt;Typically, adding a button with Bootstrap looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;button class=&amp;quot;btn btn-primary&amp;quot;&amp;gt;My Button&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, a simple button is defined and styled by applying the &lt;code&gt;btn&lt;/code&gt; and &lt;code&gt;btn-primary&lt;/code&gt; classes. &lt;code&gt;btn-primary&lt;/code&gt; sets the right color for our use case. Yet, that interface can&amp;#39;t fit our needs, so we add a custom CSS stylesheet to customize every component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;button class=&amp;quot;btn btn-primary admin-button&amp;quot;&amp;gt;My Button&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tailwind is a &amp;quot;utility-first&amp;quot; concept. Instead of providing ready-to-use components such as buttons, it has low-level utility classes that you can compose to build custom designs. As such, it encourages a more functional approach to styling, where you apply pre-defined classes directly in your HTML. It aims to minimize the need for custom CSS and promotes design consistency through the constraints of the utility classes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Utility-first&amp;quot; means that Tailwind provides atomic, single-purpose classes you can combine to construct complex designs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;#39;s have a look at some code to compare Tailwind and Bootstrap. First, here is how Tailwind lets us style a simple button:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;button class=&amp;quot;bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded&amp;quot;&amp;gt;
  My Button
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a series of button element classes to configure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Background color &lt;code&gt;bg-blue-500&lt;/code&gt;:&lt;/strong&gt; While &amp;#39;blue&amp;#39; is a pre-picked color, we can set the color shade with the number. The higher the number, the darker the color.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Background color on hover:&lt;/strong&gt; &lt;code&gt;hover:bg-blue-600&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text color &lt;code&gt;text-white&lt;/code&gt;:&lt;/strong&gt; No need for a number here, as it&amp;#39;s white; there is always a default shade if you don&amp;#39;t specify a number, such as with text-red.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vertical padding &lt;code&gt;py-2&lt;/code&gt;:&lt;/strong&gt; &amp;#39;p&amp;#39; is padding, &amp;#39;y&amp;#39; is for the vertical axis, &amp;#39;2&amp;#39; is the spacing value, not in pixels but a scale defined in Tailwind.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Horizontal padding &lt;code&gt;px-4&lt;/code&gt;:&lt;/strong&gt; Same as above, with &amp;#39;x&amp;#39; for the horizontal axis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rounding corners:&lt;/strong&gt; &lt;code&gt;rounded&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This looks more verbose than the Bootstrap example, but by only adding classes, we can adjust each part of the style. We don&amp;#39;t need to create a custom CSS class.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You might not be happy with these colors, but the good news is that you can add custom colors. We will cover that later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;A Word on Scales&lt;/h3&gt;
&lt;p&gt;CSS is mighty when it comes to spacing (such as margins and padding), and you can work with pixels and rems (root-em, a size relative to the size of the root element). This tends to be difficult, though. Tailwind comes with its own spacing scale that hides complexity while also helping with proportionality.&lt;/p&gt;
&lt;p&gt;By default, Tailwind offers values between 0 and 96, with each step proportional to the others. For example, the value &lt;code&gt;16&lt;/code&gt; has twice as much spacing as &lt;code&gt;8&lt;/code&gt;. Thanks to this, we don&amp;#39;t have to do the math to work with rems or pixels. We can define our preferred values and reuse them throughout our design.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tailwindcss.com/docs/customizing-spacing#default-spacing-scale&quot;&gt;Read more about spacing in Tailwind CSS&amp;#39;s documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Setting Up Tailwind in a Ruby on Rails Environment&lt;/h2&gt;
&lt;p&gt;Ruby on Rails 7.x directly supports Tailwind in its application generator.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$&amp;gt; cd ~/workspace/ &amp;amp;&amp;amp; mkdir tailwind-tryout &amp;amp;&amp;amp; cd tailwind-tryout
$&amp;gt; rails new -d sqlite3 -c tailwind -T .
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;We&amp;#39;ll skip the test configuration (-T) to avoid adding unnecessary complexity to this article.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Tailwind has a neat feature that generates the CSS file your application needs. Other frameworks require you to include a whole CSS file defining a framework (even the pieces you don&amp;#39;t use). In contrast, Tailwind will scan your project and generate a CSS file that contains only the classes your project needs.&lt;/p&gt;
&lt;p&gt;You do need to run a little utility to make that happen. In development mode, you can run a watcher daemon that will keep things up to date as you work: &lt;code&gt;bin/rails tailwindcss:watch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also add the watcher daemon to your &lt;code&gt;Procfile&lt;/code&gt;, then use &lt;code&gt;foreman&lt;/code&gt; or &lt;code&gt;overmind&lt;/code&gt; to start the &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;css&lt;/code&gt; processes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;web: bin/rails server
css: bin/rails tailwindcss:watch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s now use it within a simple landing page:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;bin/rails generate controller Landing index
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then head to &lt;a href=&quot;http://localhost:3000/landing/index&quot;&gt;http://localhost:3000/landing/index&lt;/a&gt;.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Dissecting Our Landing Page&lt;/h3&gt;
&lt;p&gt;Every landing page needs a title. The generator works since we configured our application to use Tailwind as its CSS framework.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/landing/index.html.erb
&amp;lt;div&amp;gt;
  &amp;lt;h1 class=&amp;quot;font-bold text-4xl&amp;quot;&amp;gt;Landing#index&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;Find me in app/views/landing/index.html.erb&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We find something that looks like standard HTML here. We have only two classes for the h1 tag:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;font-bold&lt;/code&gt;: to control the &lt;a href=&quot;https://tailwindcss.com/docs/font-weight&quot;&gt;font weight&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text-4xl&lt;/code&gt;: to control the &lt;a href=&quot;https://tailwindcss.com/docs/font-size&quot;&gt;font size&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we change &lt;code&gt;text-4xl&lt;/code&gt; to &lt;code&gt;text-xl&lt;/code&gt; and reload the page, the new style will be automatically applied. Looking at the terminal where Foreman is running, you will see that Tailwind has generated a stylesheet in the background again.
That&amp;#39;s how simple it is to integrate Tailwind into a Ruby on Rails application (this relies on the &lt;a href=&quot;https://github.com/rails/tailwindcss-rails&quot;&gt;tailwindcss-rails gem&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Configuring Tailwind for Ruby on Rails&lt;/h3&gt;
&lt;p&gt;You can edit the &lt;code&gt;config/tailwind.config.js&lt;/code&gt; file to adjust Tailwind&amp;#39;s settings (e.g., to add additional colors, specify a font to use, adjust spacing, etc).&lt;/p&gt;
&lt;p&gt;For example, we could add a &amp;quot;copper&amp;quot; color to our backgrounds and text:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;module.exports = {
  content: [&amp;quot;./src/**/*.{html,js}&amp;quot;],
  theme: {
    colors: {
      copper: {
        100: &amp;quot;#FAD9C1&amp;quot;,
        200: &amp;quot;#F6C8A4&amp;quot;,
        300: &amp;quot;#F2B786&amp;quot;,
        400: &amp;quot;#EEA669&amp;quot;,
        500: &amp;quot;#E9944C&amp;quot;,
        600: &amp;quot;#D17F3E&amp;quot;,
        700: &amp;quot;#B96A31&amp;quot;,
        800: &amp;quot;#A15524&amp;quot;,
        900: &amp;quot;#8A4018&amp;quot;,
        dark: &amp;quot;#8A4018&amp;quot;,
      },
    },
    fontFamily: {
      serif: [&amp;quot;Times&amp;quot;, &amp;quot;serif&amp;quot;],
    },
    extend: {
      spacing: {
        &amp;quot;8xl&amp;quot;: &amp;quot;108rem&amp;quot;,
      },
    },
  },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that the shades are helpful but can instead be named. If we only need three shades, for example, we can use &amp;#39;light&amp;#39;, &amp;#39;medium&amp;#39;, and &amp;#39;dark&amp;#39; instead of numbers in our views.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We can then use the shades in our title:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;h1 class=&amp;quot;font-bold text-4xl text-copper-200&amp;quot;&amp;gt;Landing#index&amp;lt;/h1&amp;gt;
&amp;lt;h2 class=&amp;quot;font-bold text-xl text-copper-dark&amp;quot;&amp;gt;Subtitle&amp;lt;/h2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can find details about this in the &lt;a href=&quot;https://github.com/rails/tailwindcss-rails?tab=readme-ov-file#configuration&quot;&gt;tailwindcss-rails gem&amp;#39;s README&lt;/a&gt; and also the &lt;a href=&quot;https://tailwindcss.com/docs/configuration&quot;&gt;Tailwind CSS documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Asset Pipeline&lt;/h3&gt;
&lt;p&gt;We have seen how &lt;code&gt;bin/rails tailwindcss:watch&lt;/code&gt; keeps our stylesheets updated in local development mode. If we need to build the stylesheets just once, we can use &lt;code&gt;bin/rails tailwindcss:build&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;For production use, you can rely on &lt;code&gt;bin/rails assets:precompile&lt;/code&gt; to directly call &lt;code&gt;bin/rails tailwindcss:build&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://guides.rubyonrails.org/asset_pipeline.html&quot;&gt;Learn more about the asset pipeline for Ruby on Rails applications&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Tailwind for Rails in Action&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s check out a couple of practical uses of Tailwind in some views: a form and a responsive navigation bar.&lt;/p&gt;
&lt;h3&gt;A Simple Form&lt;/h3&gt;
&lt;p&gt;Using the Ruby on Rails generator, we create a &lt;code&gt;user&lt;/code&gt; resource:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;bin/rails g resource user email:string password:string
bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then alter the &lt;code&gt;users_controller.rb&lt;/code&gt; file and create a view for the form.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/users_controller.rb
class UsersController &amp;lt; ApplicationController
  def def new
    @user = User.new
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/users/new.html.erb
&amp;lt;div&amp;gt;
  &amp;lt;h1 class=&amp;quot;font-bold text-4xl text-blue-500&amp;quot;&amp;gt;Users#new&amp;lt;/h1&amp;gt;

  &amp;lt;%= form_with model: @user, local: true do |form| %&amp;gt;
    &amp;lt;div class=&amp;quot;mb-6&amp;quot;&amp;gt;
      &amp;lt;%= form.label :email, class: &amp;quot;block mb-2 text-sm font-medium text-blue-900&amp;quot; %&amp;gt;
      &amp;lt;%= form.text_field :email, class: &amp;quot;bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5&amp;quot; %&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;mb-6&amp;quot;&amp;gt;
      &amp;lt;%= form.label :password, class: &amp;quot;block mb-2 text-sm font-medium text-gray-900&amp;quot; %&amp;gt;
      &amp;lt;%= form.password_field :password, class: &amp;quot;bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5&amp;quot; %&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;button type=&amp;quot;submit&amp;quot; class=&amp;quot;text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center&amp;quot;&amp;gt;Submit&amp;lt;/button&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We style each piece individually, adjusting the text color, background color, borders, padding, margins, etc. There is nothing beyond standard Tailwind here, yet we customize the form to fit our needs.&lt;/p&gt;
&lt;h3&gt;A Responsive Navigation Bar&lt;/h3&gt;
&lt;p&gt;We can add conditional breakpoints based on a browser&amp;#39;s minimum width using any utility class in Tailwind. For example, the following title will change color depending on the window size:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;h2
  class=&amp;quot;text-base font-semibold text-gray-900 sm:text-teal-800 lg:text-purple-500&amp;quot;
&amp;gt;
  Additional information
&amp;lt;/h2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, the color is a dark shade of gray. When a browser window&amp;#39;s width is between 640px and 1024px, it&amp;#39;s a shade of teal. If a window&amp;#39;s width is above 1024px, it&amp;#39;s a shade of purple.&lt;/p&gt;
&lt;p&gt;As Tailwind can also handle &lt;a href=&quot;https://tailwindcss.com/docs/grid-template-columns&quot;&gt;columns&lt;/a&gt;, here is an example to showcase how an element&amp;#39;s column width can change based on window size:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&amp;quot;sm:col-span-2 md:col-span-3&amp;quot;&amp;gt;
  &amp;lt;label for=&amp;quot;region&amp;quot; class=&amp;quot;block text-sm font-medium leading-6 text-gray-900&amp;quot;
    &amp;gt;State&amp;lt;/label
  &amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The label &amp;quot;State&amp;quot; spans two or three columns in this case.&lt;/p&gt;
&lt;p&gt;Here, using Tailwind&amp;#39;s grid layout utilities, we define a grid that is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One column wide by default (&lt;code&gt;grid-cols-1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Six columns wide above 640px width&lt;/li&gt;
&lt;li&gt;Eight columns wide above 768px width&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&amp;quot;mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 md:grid-cols-8&amp;quot;&amp;gt;
&amp;lt;/div
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Breakpoints and their widths:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sm&lt;/code&gt;: 640px&lt;/li&gt;
&lt;li&gt;&lt;code&gt;md&lt;/code&gt;: 768px&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lg&lt;/code&gt;: 1024px&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xl&lt;/code&gt;: 1280px&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2xl&lt;/code&gt;: 1536px&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As we&amp;#39;ve seen, Tailwind simplifies page design and the styling of components.&lt;/p&gt;
&lt;h2&gt;Tailwind vs. Other Frameworks&lt;/h2&gt;
&lt;p&gt;Now that we understand how Tailwind can be used, let&amp;#39;s review its key differences to other frameworks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Utility-based:&lt;/strong&gt; We compose the style of each element using specific CSS classes, each focusing on different parts of the style.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get what we need:&lt;/strong&gt; We only get the parts we need to ship our website, making for faster load times; that optimizes build time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensible:&lt;/strong&gt; We can extend or customize TailwindCSS&amp;#39; defaults through a simple configuration file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy shading of colors:&lt;/strong&gt; There&amp;#39;s no need to figure out how to make lighter or darker shades of a color to handle hover situations, for example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple spacing:&lt;/strong&gt; The hidden and proportional scales simplify spacing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Less custom CSS:&lt;/strong&gt; Since we only assemble classes to style elements, we rely less on custom CSS and can share styles (including complete themes) using HTML files and snippets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ruby on Rails friendly:&lt;/strong&gt; Thanks to the Tailwind gem, everything is integrated into the layouts and the assets pipeline.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;As we&amp;#39;ve seen, Tailwind&amp;#39;s utility-first approach is a great fit for Ruby on Rails. We don&amp;#39;t need to spend time adjusting Tailwind to fit our needs by adding complex custom configurations or additional custom CSS. As we conceive our views and partials, we can use Tailwind utility classes to shape and style them.&lt;/p&gt;
&lt;p&gt;If you want to learn more, you can access many ready-to-use templates and components thanks to Tailwind&amp;#39;s vibrant community, and products such as &lt;a href=&quot;https://tailwindui.com&quot;&gt;TailwindUI&lt;/a&gt; (from Tailwind&amp;#39;s creators).&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Getting Started: Your Ruby On Rails App Hosted On DigitalOcean With AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/05/29/getting-started-your-ruby-on-rails-app-hosted-on-digitalocean-with-appsignal.html"/>
    <id>https://blog.appsignal.com/2024/05/29/getting-started-your-ruby-on-rails-app-hosted-on-digitalocean-with-appsignal.html</id>
    <published>2024-05-29T00:00:00+00:00</published>
    <updated>2024-05-29T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the first part of a two-part series, we&#039;ll introduce how to set up DigitalOcean and AppSignal for a Ruby on Rails application.</summary>
    <content type="html">&lt;p&gt;Imagine this: you’ve just finished working on your brand new Rails app and have deployed it to a cloud provider like DigitalOcean. Like any developer, you’re very proud of your work but you still have lots of questions, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How well your new app will handle traffic&lt;/li&gt;
&lt;li&gt;Whether the optimizations you put in place will actually work, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your goal is to provide the best user experience. You want to be notified whenever errors or other important events occur so you can take care of them fast.&lt;/p&gt;
&lt;p&gt;It would be great to have a setup that automatically monitors your application. Enter AppSignal! In this article, the first part of a two-part series, we&amp;#39;ll set AppSignal up so that you can effectively monitor your Rails app hosted on DigitalOcean.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To follow along, make sure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A local installation of Ruby (this tutorial uses version 3.3.0).&lt;/li&gt;
&lt;li&gt;A local PostgreSQL installation (you can use a Docker version or a locally installed version).&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://cloud.digitalocean.com/registrations/new&quot;&gt;DigitalOcean account&lt;/a&gt; to deploy the application.&lt;/li&gt;
&lt;li&gt;An &lt;a href=&quot;https://appsignal.com/users/sign_up&quot;&gt;AppSignal account&lt;/a&gt; (a free 30-day trial is available).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;An Introduction To Our Ruby On Rails App&lt;/h2&gt;
&lt;p&gt;For this tutorial, we&amp;#39;ll be using a simple Rails 7 expense tracker app. Users will be able to sign up and create entries for their personal expenses, to track their expenses over time.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll deploy this app to DigitalOcean, then configure AppSignal&amp;#39;s monitoring solution to keep track of what is going on under the hood.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/iamaestimo/expense-tracker&quot;&gt;You can grab the source code or fork the app from here&lt;/a&gt; to follow along.&lt;/p&gt;
&lt;h2&gt;Deploying Your Rails App To DigitalOcean&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ll use DigitalOcean&amp;#39;s app platform to deploy our application. The steps below assume that you&amp;#39;ve already forked the expense tracker app described above and that you have a DigitalOcean account ready to go.&lt;/p&gt;
&lt;p&gt;After logging in, we&amp;#39;ll create an app and get it up and running. Since we want to control some parts of this process, though, the first step is to create a database for the app.&lt;/p&gt;
&lt;h3&gt;Creating the App Database&lt;/h3&gt;
&lt;p&gt;Click on the &lt;em&gt;Databases&lt;/em&gt; link on the left-hand menu, then click on the &lt;em&gt;Create Database&lt;/em&gt; link to create a new PostgreSQL database:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/create-new-app-database.png&quot; alt=&quot;Create new app database&quot;/&gt;&lt;/p&gt;
&lt;p&gt;After completing the database settings in the next screen, take note of the database connection string. You&amp;#39;ll use it as an environment variable when creating the app in the next step:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/finalize-database-creation.png&quot; alt=&quot;Finalize database creation&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;At this point, you&amp;#39;ll likely get a warning that your database is open to all incoming connections. Follow the accompanying link to take care of this security warning.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Creating and Deploying the App&lt;/h3&gt;
&lt;p&gt;Finally, create the app by first clicking on the &lt;em&gt;Apps&lt;/em&gt; link on the left side menu:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/login-do-app-platform.png&quot; alt=&quot;DigitalOcean app platform dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Then connect the app&amp;#39;s code repository resource as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/connect-app-repo.png&quot; alt=&quot;Connect app code repo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Continue to the environment variables screen and insert the copied database URL string. Also, add the &lt;code&gt;RAILS_MASTER_KEY&lt;/code&gt; as an environment variable since it will be used in the deployment process.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/app-environment-variables.png&quot; alt=&quot;Setting up the environment variables&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Then, with these environment variables in place, click on the &lt;em&gt;Next&lt;/em&gt; button to deploy the app:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/app-deployment.png&quot; alt=&quot;App deployed&quot;/&gt;&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;With the app successfully deployed, we&amp;#39;ll now set up monitoring using AppSignal.&lt;/p&gt;
&lt;h2&gt;Setting up AppSignal to Monitor Your Rails App&lt;/h2&gt;
&lt;p&gt;First, log in to your &lt;a href=&quot;https://appsignal.com/users/sign_in&quot;&gt;AppSignal account&lt;/a&gt; and choose &amp;#39;Ruby &amp;amp; Rails&amp;#39;.&lt;/p&gt;
&lt;p&gt;Now add the &lt;a href=&quot;https://github.com/appsignal/appsignal-ruby&quot;&gt;AppSignal gem&lt;/a&gt; to the Rails app. This nifty gem will collect errors, exceptions, and relevant performance data, and port it over to AppSignal for analysis.&lt;/p&gt;
&lt;p&gt;Open up the app&amp;#39;s Gemfile and add the gem:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile
gem &amp;quot;appsignal&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, you&amp;#39;ll need to run the installation script that comes with your account-specific API key attached. When you run this install script, you should get something similar to the below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt; bundle exec appsignal install &amp;lt;redacted&amp;gt;

#######################################
## Starting AppSignal Installer      ##
## --------------------------------- ##
## Need help?  support@appsignal.com ##
## Docs?       docs.appsignal.com    ##
#######################################

Validating API key...
  API key valid!

Installing for Ruby on Rails

  Your app&amp;#39;s name is: &amp;#39;ExpenseTracker&amp;#39;
  Do you want to change how this is displayed in AppSignal? (y/n): n
How do you want to configure AppSignal?
  (1) a config file
  (2) environment variables
  Choose (1/2): 1

Writing config file...
  Config file written to config/appsignal.yml

#####################################
## AppSignal installation complete ##
#####################################

  Sending example data to AppSignal...
  Example data sent!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a couple of things to note when you run the script:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You&amp;#39;ll get the option to change your app&amp;#39;s name as it will appear on AppSignal&amp;#39;s dashboard or leave the default option.&lt;/li&gt;
&lt;li&gt;You&amp;#39;ll also get the option to choose how you want AppSignal configured for your app. In my case, I went with the config file option, creating a config file in &lt;code&gt;config/appsignal.yml&lt;/code&gt;, with the below content:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# config/appsignal.yml

default: &amp;amp;defaults
  push_api_key: &amp;quot;&amp;lt;%= ENV[&amp;#39;APPSIGNAL_PUSH_API_KEY&amp;#39;] %&amp;gt;&amp;quot;

  name: &amp;quot;ExpenseTracker&amp;quot;

development:
  &amp;lt;&amp;lt;: *defaults
  active: true

production:
  &amp;lt;&amp;lt;: *defaults
  active: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we define:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;push_api_key&lt;/code&gt; which connects the app to AppSignal.&lt;/li&gt;
&lt;li&gt;The app&amp;#39;s name as it will appear on AppSignal.&lt;/li&gt;
&lt;li&gt;The environments in which AppSignal will monitor the app (in this case, both development and production environments).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the way, if you&amp;#39;d like to turn off AppSignal monitoring on the development environment, just set the &lt;code&gt;active&lt;/code&gt; flag to &lt;code&gt;false&lt;/code&gt;, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;...
development:
  &amp;lt;&amp;lt;: *defaults
  active: false
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Both the config file and environment variable options do the same thing. They define how AppSignal will connect to your app, the app name, and which environment will be monitored.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Assuming everything goes to plan, you should see a screen showing that AppSignal is receiving data from your app!&lt;/p&gt;
&lt;h2&gt;An Introduction to AppSignal&amp;#39;s Dashboard for Rails&lt;/h2&gt;
&lt;p&gt;Now that everything is set up correctly and AppSignal is receiving data from your app, you can access the default app monitoring dashboard view as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/default-appsignal-dashboard.png&quot; alt=&quot;Default Appsignal dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip&lt;/strong&gt;: If you set up monitoring for both development and production environments, you can easily switch between them from the selection shown by the arrow.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;From the default view, you have access to the following default charts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Error rate&lt;/strong&gt; - This will show the rate of errors occurring in your app per unit of time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throughput&lt;/strong&gt; - Here, you&amp;#39;ll get a snapshot of the throughput your app is able to handle in terms of requests per minute.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Response time&lt;/strong&gt; - This will show your app&amp;#39;s response times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latest open errors&lt;/strong&gt; - This section will list the latest errors that will have happened within your app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latest open performance measurements&lt;/strong&gt; - This section lists the latest performance measurements, including method calls, API requests, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, we&amp;#39;ll see how to set up proper error tracking for a Rails application using AppSignal.&lt;/p&gt;
&lt;h2&gt;Monitoring Errors with AppSignal&lt;/h2&gt;
&lt;p&gt;There are more than ten error types that could affect a running Ruby on Rails app. Obviously, some are more common than others. In this section, we&amp;#39;ll simulate a few of these errors and see how AppSignal handles them. Let&amp;#39;s start with a simple example first.&lt;/p&gt;
&lt;p&gt;Since the expense tracker app is using Devise for authentication, let&amp;#39;s add a check for whether a user is signed in. Instead of using the recommended &lt;code&gt;user_signed_in?&lt;/code&gt; method, let&amp;#39;s use the erroneous &lt;code&gt;user_logged_in?&lt;/code&gt; method, which should trigger an &lt;code&gt;ActionView::Template::Error&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/shared/_navbar.html.erb --&amp;gt;
...
&amp;lt;% if user_logged_in? %&amp;gt;
    &amp;lt;%= link_to &amp;#39;Logout&amp;#39;, destroy_user_session_path %&amp;gt;
&amp;lt;% else %&amp;gt;
    &amp;lt;%= link_to new_user_session_path do %&amp;gt;
    Login
    &amp;lt;% end %&amp;gt;
&amp;lt;% end %&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deploy this change, reload the production app, then go into the production environment dashboard in AppSignal and watch how this error shows up:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/action-template-error.png&quot; alt=&quot;ActionView template error on AppSignal&quot;/&gt;&lt;/p&gt;
&lt;p&gt;AppSignal makes it a breeze to get more details on any errors that happen in an application. For example, we can get details about the &lt;em&gt;ActionView&lt;/em&gt; template error by clicking on it from the &amp;#39;Latest Open Errors&amp;#39; dashboard panel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/action-template-error-details.png&quot; alt=&quot;ActionView template error details&quot;/&gt;&lt;/p&gt;
&lt;p&gt;You can clearly see that the error is caused by an undefined method which points us in the right direction to fix it.&lt;/p&gt;
&lt;p&gt;AppSignal&amp;#39;s Errors dashboard also gives you extra information through the &lt;em&gt;Logbook&lt;/em&gt; and &lt;em&gt;Settings&lt;/em&gt; panels:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-05/action-template-error-details-2.png&quot; alt=&quot;AppSignal error dashboard details&quot;/&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Logbook&lt;/strong&gt; - Here, you or your team members can add comments to an error being tracked by AppSignal. The Logbook panel also shows a chronological breakdown of any actions taken regarding the error in question.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Settings&lt;/strong&gt; - From this panel, you can assign an error to another team member, change alert settings (we&amp;#39;ll check these out in detail in the second part of this tutorial), and set error severity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that&amp;#39;s it for this part of the series!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this tutorial, we deployed a simple Rails application to DigitalOcean&amp;#39;s app platform and hooked it up to AppSignal&amp;#39;s application monitoring platform. We also went through how errors are monitored and displayed in AppSignal&amp;#39;s Errors dashboard.&lt;/p&gt;
&lt;p&gt;Obviously, this is just scratching the surface of what&amp;#39;s possible with AppSignal. In the second part of this series, we&amp;#39;ll dive deeper into performance measurements, anomaly detection, uptime monitoring, and logging.&lt;/p&gt;
&lt;p&gt;In the meantime, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Five Things to Avoid in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/05/22/five-things-to-avoid-in-ruby.html"/>
    <id>https://blog.appsignal.com/2024/05/22/five-things-to-avoid-in-ruby.html</id>
    <published>2024-05-22T00:00:00+00:00</published>
    <updated>2024-05-22T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll dive into five common Ruby mistakes and see how we can combat them.</summary>
    <content type="html">&lt;p&gt;As a contract software developer, I am exposed to oodles of Ruby code.
Some code is readable, some obfuscated. Some code eschews whitespace, as if
carriage returns were a scarce natural resource, while other code resembles a
living room fashioned by Vincent Van Duysen. Code, like the people who author
it, varies.&lt;/p&gt;
&lt;p&gt;Yet, it&amp;#39;s ideal to minimize variation. Time and effort are best spent
on novel problems.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll explore five faux pas often seen in Ruby code and learn how to turn these idiosyncrasies into idioms.&lt;/p&gt;
&lt;p&gt;But first, how can we minimize variance in Ruby?&lt;/p&gt;
&lt;h2&gt;Minimizing Variance in Ruby with Rubocop and Idioms&lt;/h2&gt;
&lt;p&gt;Ruby developers can gain efficiency with &lt;a href=&quot;https://rubocop.org&quot;&gt;Rubocop&lt;/a&gt;, which unifies style within a
single project or across many projects. Rails is a boon to uniformity, too, as
it favors convention over configuration. Indeed, disparate Rails codebases are
identical in places and, overall, quite similar.&lt;/p&gt;
&lt;p&gt;Beyond tools and convention, variance is also minimized by idioms, or those
&lt;a href=&quot;https://www.merriam-webster.com/dictionary/idiom&quot;&gt;&amp;quot;structural forms peculiar to a language&amp;quot;&lt;/a&gt;. For example, Ruby syntax is
a collection of explicit idioms enforced by the interpreter. Reopening a class
is a Ruby idiom, too.&lt;/p&gt;
&lt;p&gt;But not all Ruby idioms are dicta. Most Ruby idioms are best practices,
shorthand, and common usages Ruby developers share informally and in practice.&lt;/p&gt;
&lt;p&gt;Learning idioms in Ruby is like learning idioms in any second spoken (or
programming) language. It takes time and practice. Yet the more adept you are at
recognizing and proffering Ruby&amp;#39;s idioms, the better your code will be.&lt;/p&gt;
&lt;h2&gt;What to Avoid in Ruby&lt;/h2&gt;
&lt;p&gt;Now, let&amp;#39;s move on to looking at five things to avoid in Ruby:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Verbosity&lt;/li&gt;
&lt;li&gt;Long expressions to detect &lt;code&gt;nil&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Overuse of &lt;code&gt;self&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Collecting results in temporary variables&lt;/li&gt;
&lt;li&gt;Sorting and filtering in memory&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Verbosity&lt;/h3&gt;
&lt;p&gt;If you&amp;#39;ve delved into Ruby, you can appreciate how expressive, compact, fun,
and flexible it is. Any given problem can be solved in a number of ways. For
example, &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;unless&lt;/code&gt;, &lt;code&gt;case&lt;/code&gt;, and the ternary operator &lt;code&gt;?:&lt;/code&gt; all express
decisions — which one you should apply depends on the problem.&lt;/p&gt;
&lt;p&gt;However, per Ruby idiom, some &lt;code&gt;if&lt;/code&gt; statements are better than others. For
instance, the following blocks of code achieve the same result, but one is
idiomatic to Ruby:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Verbose approach

actor = nil

if response == 1
  actor = &amp;quot;Groucho&amp;quot;
elsif response == 2
  actor = &amp;quot;Chico&amp;quot;
elsif response == 3
  actor = &amp;quot;Harpo&amp;quot;
else
  actor = &amp;quot;Zeppo&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Idiomatic approach

actor =
  if response == 1
    &amp;quot;Groucho&amp;quot;
  elsif response == 2
    &amp;quot;Chico&amp;quot;
  elsif response == 3
    &amp;quot;Harpo&amp;quot;
  else
    &amp;quot;Zeppo&amp;quot;
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Almost every Ruby statement and expression yields a value, including &lt;code&gt;if&lt;/code&gt;, which
returns a final code statement value and a matching condition. The
version of the &lt;code&gt;if&lt;/code&gt; statement in the second code block above leverages this behavior. If &lt;code&gt;response&lt;/code&gt;
is &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;actor&lt;/code&gt; is set to &lt;code&gt;Chico&lt;/code&gt;. Assigning the result of an &lt;code&gt;if&lt;/code&gt; statement is
idiomatic to Ruby. (The same construct can be applied to &lt;code&gt;case&lt;/code&gt;, &lt;code&gt;unless&lt;/code&gt;,
&lt;code&gt;begin/end&lt;/code&gt;, and others.)&lt;/p&gt;
&lt;p&gt;Another Ruby idiom is present: you need not predefine a variable used
within an &lt;code&gt;if&lt;/code&gt; statement (or &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, and others). So, the latter code
removes the line &lt;code&gt;actor = nil&lt;/code&gt;. In Ruby, unlike other languages, the body of an
&lt;code&gt;if&lt;/code&gt; statement is &lt;em&gt;not&lt;/em&gt; considered a separate scope.&lt;/p&gt;
&lt;h3&gt;Long Expressions to Detect &lt;code&gt;nil&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;nil&lt;/code&gt; represents &amp;quot;nothing&amp;quot; in Ruby. It&amp;#39;s a legitimate value and is its own class
(&lt;code&gt;NilClass&lt;/code&gt;) with methods. Like other classes, if you call a method that&amp;#39;s not defined
on &lt;code&gt;nil&lt;/code&gt;, Ruby throws an exception akin to &lt;code&gt;undefined method &amp;#39;xxx&amp;#39; for nil:NilClass&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To avoid this exception, you must first test a variable to determine if it&amp;#39;s &lt;code&gt;nil.&lt;/code&gt;
For example, code such as this is common in Rails applications:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if user &amp;amp;&amp;amp; user.plan &amp;amp;&amp;amp; user.plan.name == &amp;#39;Standard&amp;#39;
  # ... some code for the Standard plan
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The trouble is that such a long chain of assertions is unwieldy. Imagine having to
repeat the condition &lt;code&gt;user &amp;amp;&amp;amp; user.plan &amp;amp;&amp;amp; ...&lt;/code&gt; every time you have to reference
a user&amp;#39;s plan name.&lt;/p&gt;
&lt;p&gt;Instead, use Ruby&amp;#39;s safe navigation operator, &lt;code&gt;&amp;amp;.&lt;/code&gt; It is shorthand for
the logic &amp;quot;If a method is called on &lt;code&gt;nil&lt;/code&gt;, return &lt;code&gt;nil&lt;/code&gt;; otherwise, call the method per normal.&amp;quot;
Using &lt;code&gt;&amp;amp;.&lt;/code&gt; reduces the code above to the much more readable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if user&amp;amp;.plan&amp;amp;.name == &amp;#39;Standard&amp;#39;
  // ... some code
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;user&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt; and &lt;code&gt;plan&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, the expression &lt;code&gt;user&amp;amp;.plan&amp;amp;.name&lt;/code&gt; is
&lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The example above assumes &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;plan&lt;/code&gt; represent a custom structure or
Rails model of some kind. You can also use the safe navigation operator if a
value represents an &lt;code&gt;Array&lt;/code&gt; or &lt;code&gt;Hash&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, assume &lt;code&gt;a_list_of_values&lt;/code&gt; is an &lt;code&gt;Array&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;a_list_of_values[index]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the variable &lt;code&gt;a_list_of_values&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, an exception is thrown. If you expectantly try
&lt;code&gt;a_list_of_values&amp;amp;.[index]&lt;/code&gt;, a syntax error occurs. Instead, use &lt;code&gt;&amp;amp;.&lt;/code&gt; with the
&lt;code&gt;Array#at&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;a_list_of_values&amp;amp;.at(index)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if &lt;code&gt;a_list_of_values&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, the result of the expression is &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Overuse of &lt;code&gt;self&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Ruby uses &lt;code&gt;self&lt;/code&gt; in three substantive ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To define class methods&lt;/li&gt;
&lt;li&gt;To refer to the current object&lt;/li&gt;
&lt;li&gt;To differentiate between a local variable and a method if both have the same name&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&amp;#39;s an example class demonstrating all three usages.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Rectangle
  def self.area(length, width)
    new(length, width).area
  end

  def self.volume(length, width, height)
    area(length, width) * height
  end

  def initialize(length, width, height = nil)
    self.length = length
    self.width  = width
    self.height = height
  end

  def area
    length * width
  end

  def volume
    area = 100
    self.area * height
  end

  private

  attr_reader :length, :width, :height
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;def self.area&lt;/code&gt; is an example of the first purpose for &lt;code&gt;self&lt;/code&gt;, defining a class
method. Given this class, the Ruby code &lt;code&gt;puts Rectangle.area(10, 5)&lt;/code&gt; produces
&lt;code&gt;50&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The code &lt;code&gt;self.length = length&lt;/code&gt; demonstrates the second application of &lt;code&gt;self&lt;/code&gt;:
the instance variable &lt;code&gt;length&lt;/code&gt; is set to the value of the argument &lt;code&gt;length&lt;/code&gt;.
(The &lt;code&gt;attr_reader&lt;/code&gt; statement at the bottom defines the instance variable and
provides a getter method.) Here, the statement &lt;code&gt;self.length = length&lt;/code&gt; is
functionally the same as &lt;code&gt;@length = length&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The third use of &lt;code&gt;self&lt;/code&gt; is shown in the &lt;code&gt;volume&lt;/code&gt; method. What does &lt;code&gt;puts Rectangle.volume(10, 5, 2)&lt;/code&gt; emit? The answer is &lt;code&gt;100&lt;/code&gt;. The line &lt;code&gt;area = 100&lt;/code&gt;
sets a method-scoped, local variable named &lt;code&gt;area&lt;/code&gt;. However, the line &lt;code&gt;self.area&lt;/code&gt;
refers to the method &lt;code&gt;area&lt;/code&gt;. Hence, the answer is &lt;code&gt;10 * 5 * 2 = 100&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Consider one more example. What is the output if the &lt;code&gt;volume&lt;/code&gt; method is written
like this?:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt; def volume
   height = 10
   self.area * height
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The answer is &lt;code&gt;500&lt;/code&gt; because both uses of &lt;code&gt;height&lt;/code&gt; refer to the local variable,
not the instance variable.&lt;/p&gt;
&lt;p&gt;Inexperienced Rails developers make unnecessary use of &lt;code&gt;self&lt;/code&gt;, as in:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Class User
# first_name: String
# last_name: String
# ...

class User &amp;lt; ApplicationRecord
  ...
  def full_name
    &amp;quot;#{self.first_name} #{self.last_name}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Technically, this code is correct, yet using &lt;code&gt;self&lt;/code&gt; to refer to model attributes is unnecessary. If you combine Rubocop with your
development environment, Rubocop flags this issue for you to correct.&lt;/p&gt;
&lt;h3&gt;Collecting Results in Temporary Variables&lt;/h3&gt;
&lt;p&gt;A common code chore is processing lists of records. You might eliminate records
due to certain criteria, map one set of values to another, or separate one set of
records into multiple categories. A typical solution is to iterate over the list
and accumulate a result.&lt;/p&gt;
&lt;p&gt;For instance, consider this a solution to find all even
numbers from a list of integers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def even_numbers(list)
  even_numbers = []

  list.each do |number|
    even_numbers &amp;lt;&amp;lt; number if number&amp;amp;.even?
  end

  return even_numbers
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code creates an empty list to aggregate results and then iterates over the
list, ignoring &lt;code&gt;nil&lt;/code&gt; items, and accumulating the even values. Finally, it
returns the list to the caller. This code serves its purpose, but it isn&amp;#39;t
idiomatic.&lt;/p&gt;
&lt;p&gt;A better approach is to use Ruby&amp;#39;s Enumerable methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def even_numbers(list)
  list.select(&amp;amp;:even?) # shorthand for `.select { |v| v.even? }`
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;select&lt;/code&gt; is one of many Enumerable methods. Each Enumerable method iterates
over a list and collects results based on a condition. Specifically, &lt;code&gt;select&lt;/code&gt;
iterates over a list and accumulates all items where a condition yields a &lt;em&gt;truthy&lt;/em&gt; value.
In the example above, &lt;code&gt;even?&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; if the item is an even number.
&lt;code&gt;select&lt;/code&gt; returns a new list, leaving the original list unchanged. A variant named
&lt;code&gt;select!&lt;/code&gt; performs the same purpose, but alters (mutates) the original list.&lt;/p&gt;
&lt;p&gt;The Enumerable methods include &lt;code&gt;reject&lt;/code&gt; and &lt;code&gt;map&lt;/code&gt; (also known as &lt;code&gt;collect&lt;/code&gt;).
&lt;code&gt;reject&lt;/code&gt; collects all items from a list where a condition yields a falsey value.
&lt;code&gt;map&lt;/code&gt; returns a new list where each item in the original list is transformed by
an expression.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s one more example Enumerable method in action. First, some non-idiomatic code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def transform(hash)
  new_hash = {}

  hash.each_pair do |key, value|
    new_hash[key] = value ? value * 2 : nil
  end

  return new_hash
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now a more idiomatic approach:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def transform(hash)
  hash.transform_values { |value| value&amp;amp;.*(2) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;transform_values&lt;/code&gt; is another Enumerable method available for &lt;code&gt;Hash&lt;/code&gt;. It returns
a new hash with the same keys, but each associated value is transformed.
Remember, even integers are objects in Ruby and have methods. &lt;code&gt;value&amp;amp;.*(2)&lt;/code&gt;
returns &lt;code&gt;nil&lt;/code&gt; if &lt;code&gt;value&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, else &lt;code&gt;value * 2&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Sorting and Filtering in Memory&lt;/h3&gt;
&lt;p&gt;Here&amp;#39;s one last faux pas — this one is specific to Rails and ActiveRecord. Let&amp;#39;s examine this code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# class Student
# name: String
# gpa: Float
# ...
#

class Student &amp;lt; ApplicationRecord
  ...

  def self.top_students
    Student
      .all
      .sort_by { |score| student.gpa }
      .select { |score| student.gpa &amp;gt;= 90.0 }
      .reverse
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling &lt;code&gt;Student.top_students&lt;/code&gt; returns all students with a GPA greater than or
equal to &lt;code&gt;90.0&lt;/code&gt; in ranked order, from highest to lowest. Technically, this code
is correct, but it isn&amp;#39;t very efficient in space or time because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It must load all records from the &lt;code&gt;students&lt;/code&gt; table into memory.&lt;/li&gt;
&lt;li&gt;It first sorts all records and &lt;em&gt;then&lt;/em&gt; filters based on GPA, performing unnecessary work.&lt;/li&gt;
&lt;li&gt;It reverses the order of the list in memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sorting and filtering are best performed in the database, if possible, using ActiveRecord&amp;#39;s
tools.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def self.top_students
  Student.where(gpa: 90.0..).order(gpa: :desc)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The shorthand &lt;code&gt;90.0..&lt;/code&gt; in the &lt;code&gt;where&lt;/code&gt; clause is a Ruby &lt;code&gt;Range&lt;/code&gt; expressing a value
between &lt;code&gt;90.0&lt;/code&gt; and &lt;code&gt;Float::INFINITY&lt;/code&gt;. If &lt;code&gt;gpa&lt;/code&gt; is indexed in the &lt;code&gt;students&lt;/code&gt; table,
this query is likely very fast and loads only the matching records. If the student
records are being fetched for display, more efficiency (in memory) can be gained
via pagination (albeit at the possible expense of more queries to fetch the batches).&lt;/p&gt;
&lt;h2&gt;Wrapping Up and Next Steps&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ve covered five key things to avoid in Ruby and the idioms to use instead.&lt;/p&gt;
&lt;p&gt;I highly recommend reading the documentation for Ruby&amp;#39;s core classes and
modules, including &lt;a href=&quot;https://ruby-doc.org/3.3.0/Array.html&quot;&gt;&lt;code&gt;Array&lt;/code&gt;&lt;/a&gt;,
&lt;a href=&quot;https://ruby-doc.org/3.3.0/Hash.html&quot;&gt;&lt;code&gt;Hash&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://ruby-doc.org/3.3.0/Enumerable.html&quot;&gt;&lt;code&gt;Enumerable&lt;/code&gt;&lt;/a&gt;.
The documents are a
treasure trove of methods and techniques, and chances are a method exists to
solve your problem at hand. Turn knowledge into practice by writing small code
samples to learn how each method works.&lt;/p&gt;
&lt;p&gt;Add Rubocop into your workflow, even your text editor. Rubocop keeps your code
looking nice but can also flag idiosyncratic code. Using Rubocop is one of the best ways to learn to code the Ruby way.&lt;/p&gt;
&lt;p&gt;Finally, read other developers&amp;#39; code, especially open-source Ruby projects. To
peruse the code of any gem, run &lt;code&gt;bundle open &amp;lt;gem&amp;gt;&lt;/code&gt;, where &lt;code&gt;&amp;lt;gem&amp;gt;&lt;/code&gt; is the name
of a library. If you&amp;#39;ve included a debugger in your &lt;em&gt;Gemfile&lt;/em&gt;, you can even set
breakpoints in any gem and step through the code.&lt;/p&gt;
&lt;p&gt;Go forth and code!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.P.S. &lt;a href=&quot;https://www.appsignal.com/tour/errors&quot;&gt;Use AppSignal for Ruby for deeper debugging insights&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Creating Forms in Ruby on Rails with Simple Form</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/05/15/creating-forms-in-ruby-on-rails-with-simple-form.html"/>
    <id>https://blog.appsignal.com/2024/05/15/creating-forms-in-ruby-on-rails-with-simple-form.html</id>
    <published>2024-05-15T00:00:00+00:00</published>
    <updated>2024-05-15T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Simple Form helps you to build and manage forms in Rails. Let&#039;s examine what Simple Form is, why we might need it, and some real use cases.</summary>
    <content type="html">&lt;p&gt;Ruby on Rails has changed how we build web applications. Early on, the framework came with some great features to help you get started and build robust applications.&lt;/p&gt;
&lt;p&gt;However, it can still be tricky to build and handle forms. Simple Form is a great option. Let&amp;#39;s examine what Simple Form is, why we might need it, and some real use cases.&lt;/p&gt;
&lt;h2&gt;Forms and Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Ruby on Rails really simplifies building applications. Yet, it also requires your constant attention to keep your codebase streamlined and coherent. One strategy is to avoid writing code by using abstractions, for example. Code that isn&amp;#39;t in your actual application is code you don&amp;#39;t have to manage. That&amp;#39;s why we like abstractions so much, right? Simple Form is just such an abstraction that simplifies building forms for web pages.&lt;/p&gt;
&lt;p&gt;Forms are generally complex to build, even using Ruby on Rails. Each field requires attention to define the right type and attach it to the correct attribute. Simple Form eases that pain, as you don&amp;#39;t need to find the exact type required for each field.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take a form that gets a user&amp;#39;s details, with &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, and &lt;code&gt;email&lt;/code&gt; fields. Consider that we have the &lt;code&gt;User&lt;/code&gt; model with similar related attributes. With Simple Form, the form that fills in attributes looks like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= simple_form_for @user do |f| %&amp;gt;
  &amp;lt;%= f.input :name, required: false %&amp;gt;
  &amp;lt;%= f.input :username %&amp;gt;
  &amp;lt;%= f.input :email %&amp;gt;
  &amp;lt;%= f.button :submit %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we are not specifying the types of each field. Simple Form will pick the correct input type for each field based on the column&amp;#39;s type.&lt;/p&gt;
&lt;p&gt;This saves us time and keeps the code readable and easier to maintain.&lt;/p&gt;
&lt;h2&gt;Installing Simple Form&lt;/h2&gt;
&lt;p&gt;To install Simple Form in your project, add the &lt;code&gt;simple_form&lt;/code&gt; gem to the Gemfile. Once you have run &lt;code&gt;bundle install&lt;/code&gt;, you can use the included generator to set it up correctly for your application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails generate simple_form:install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;A note on Bootstrap and Zurb Foundation:&lt;/strong&gt; &lt;em&gt;Both Bootstrap and Zurb Foundation 5 are supported within Simple Form. So, if you are using one of them, you can use the &lt;code&gt;--bootstrap&lt;/code&gt; or &lt;code&gt;--foundation&lt;/code&gt; parameter with the generator to include additional configurations during the installation. In the case of Bootstrap, this would look like the following:&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails generate simple_form:install --bootstrap
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Basic Usage of Simple Form&lt;/h2&gt;
&lt;p&gt;As we pointed out earlier, Simple Form will generate a complete form mainly based on the data related to your objects. Yet, it&amp;#39;s also important to understand that Simple Form will not only generate the appropriate field for each attribute but also provide labels and display error hints above the input fields themselves!&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s consider a fresh new Ruby on Rails 7.x application. We&amp;#39;ll generate a User model and build a form for it using Simple Form.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails g model User username:string password:string email:string remember_me:boolean
# and follow with running the migration of course
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can generate a controller with the &lt;code&gt;new&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, and &lt;code&gt;index&lt;/code&gt; actions. The aim is to have something we can play with in the form.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails g controller users
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/users_controller.rb
class UsersController &amp;lt; ApplicationController
  def new
    render :new, locals: { user: User.new }
  end

  def create
    user = User.create(user_params)
    redirect_to users_path
  end

  def index
    render :index, locals: { users: User.all }
  end

  private

  def user_params
    params.require(:user).permit(:username, :password, :email)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/routes.rb
Rails.application.routes.draw do
  resources :users, only: [:new, :create, :index]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These actions require relevant views. Here is a form that handles a user signing up or being added:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/users/new.html.erb --&amp;gt;
&amp;lt;%= simple_form_for user do |f| %&amp;gt;
  &amp;lt;%= f.input :username, label: &amp;#39;Your username please&amp;#39;, error: &amp;#39;Username is mandatory, please specify one&amp;#39; %&amp;gt;
  &amp;lt;%= f.input :password, hint: &amp;#39;No special characters.&amp;#39; %&amp;gt;
  &amp;lt;%= f.input :email, placeholder: &amp;#39;user@domain.com&amp;#39; %&amp;gt;
  &amp;lt;%= f.input :remember_me, inline_label: &amp;#39;Yes, remember me&amp;#39; %&amp;gt;
  &amp;lt;%= f.button :submit %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, we specify a custom label and error for the &lt;code&gt;username&lt;/code&gt; input. If no such customization is passed, it will do its best to guess what the label needs to be based on the attribute&amp;#39;s name.&lt;/p&gt;
&lt;p&gt;We have set a custom hint for the &lt;code&gt;password&lt;/code&gt; field and a custom &lt;code&gt;placeholder&lt;/code&gt; for the &lt;code&gt;email&lt;/code&gt; field. Notice, also, the ability to specify an &amp;quot;inline label&amp;quot; instead of one located above the field (the usual default in forms).&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;It&amp;#39;s also possible to disable labels, hints, and errors by passing the &lt;code&gt;false&lt;/code&gt; value for those attributes: &lt;code&gt;&amp;lt;%= f.input :password_confirmation, label: false %&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We need to complement this with one more view to make it usable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/users/index.html.erb --&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;% users.each do |user| %&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;%= user.username %&amp;gt; (&amp;lt;%= user.email %&amp;gt;)&amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;
&amp;lt;%= link_to &amp;#39;New User&amp;#39;, new_user_path %&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can then start the application using &lt;code&gt;rails s&lt;/code&gt;. Head to &lt;code&gt;localhost:3000/users/new&lt;/code&gt; to see and use the form.
Let&amp;#39;s focus on how the form differs from a classic Ruby on Rails form.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A note on validations&lt;/strong&gt;: &lt;em&gt;Simple Form can work with mandatory fields out of the box. For example, if we were to add the following presence validation in our User model:&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  validates :username, presence:true

  # more code
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;This will be used by Simple Form to add a little &amp;#39;*&amp;#39; next to the &lt;code&gt;username&lt;/code&gt; field, specifying that it&amp;#39;s mandatory. This doesn&amp;#39;t manage the errors automatically, so you still have to handle that in the controller action. Return to the appropriate field that has errors and act on them.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;About Column Types and Form Fields&lt;/h3&gt;
&lt;p&gt;As mentioned earlier, we haven&amp;#39;t specified a type for each field. The &lt;a href=&quot;https://github.com/heartcombo/simple_form#available-input-types-and-defaults-for-each-column-type&quot;&gt;Simple Form README&lt;/a&gt; has a complete list of all available input types and the defaults for each column type. Here are the most used ones:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column Type&lt;/th&gt;
&lt;th&gt;Generated HTML element&lt;/th&gt;
&lt;th&gt;Comment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=text]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=checkbox]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(passwords) &lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=password]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any column with a name containing &amp;#39;password&amp;#39;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=textarea]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;integer&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=number]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;datetime select&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;time select&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;country&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;select&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Booleans&lt;/h3&gt;
&lt;p&gt;As we can see in the above table, boolean attributes will be represented, by default, as checkboxes. In many cases, that&amp;#39;s what we&amp;#39;ll want. But if not, there is a way to customize this in Simple Form with the &lt;code&gt;as&lt;/code&gt; attribute, which allows you to specify if you will show radio buttons or a dropdown instead.&lt;/p&gt;
&lt;p&gt;Here is how to specify radio buttons instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= f.input :remember_me, input_html: { value: &amp;#39;1&amp;#39; }, as: :radio_buttons %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate the following HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&amp;quot;input radio_buttons optional user_remember_me&amp;quot;&amp;gt;
  &amp;lt;label class=&amp;quot;radio_buttons optional&amp;quot;&amp;gt;Remember me&amp;lt;/label&amp;gt;
  &amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;user[remember_me]&amp;quot; value=&amp;quot;&amp;quot; autocomplete=&amp;quot;off&amp;quot; /&amp;gt;
  &amp;lt;span class=&amp;quot;radio&amp;quot;&amp;gt;
    &amp;lt;label for=&amp;quot;user_remember_me_true&amp;quot;&amp;gt;
      &amp;lt;input
        value=&amp;quot;true&amp;quot;
        class=&amp;quot;radio_buttons optional&amp;quot;
        type=&amp;quot;radio&amp;quot;
        name=&amp;quot;user[remember_me]&amp;quot;
        id=&amp;quot;user_remember_me_true&amp;quot;
      /&amp;gt;Yes
    &amp;lt;/label&amp;gt;
  &amp;lt;/span&amp;gt;
  &amp;lt;span class=&amp;quot;radio&amp;quot;&amp;gt;
    &amp;lt;label for=&amp;quot;user_remember_me_false&amp;quot;&amp;gt;
      &amp;lt;input
        value=&amp;quot;false&amp;quot;
        class=&amp;quot;radio_buttons optional&amp;quot;
        readonly=&amp;quot;readonly&amp;quot;
        type=&amp;quot;radio&amp;quot;
        name=&amp;quot;user[remember_me]&amp;quot;
        id=&amp;quot;user_remember_me_false&amp;quot;
      /&amp;gt;No
    &amp;lt;/label&amp;gt;
  &amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s impressive that such a small piece of Ruby code gets you such a complete piece of HTML!&lt;/p&gt;
&lt;h3&gt;HTML&lt;/h3&gt;
&lt;p&gt;From the previous example, we can see how Simple Form generates a lot of HTML for each field. That includes the HTML for the input field and a wrapper div around the label and input field. We can customize those by specifying a custom class and id using the &lt;code&gt;input_html&lt;/code&gt; and &lt;code&gt;wrapper_html&lt;/code&gt; attributes. Here is an example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= simple_form_for @user do |f| %&amp;gt;
  &amp;lt;%= f.input :username, wrapper_html: { class: &amp;#39;username&amp;#39; }, input_html { id: &amp;#39;username&amp;#39; } %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s also a way to set attributes for the related HTML, such as &lt;code&gt;maxlength&lt;/code&gt; or &lt;code&gt;value&lt;/code&gt;. Let&amp;#39;s take the password field from our user form. We can limit the size and length of the field with the &lt;code&gt;maxlength&lt;/code&gt; attribute.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= f.input :password, input_html: { maxlength: 20 } %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Custom Inputs and Additional Options&lt;/h2&gt;
&lt;p&gt;Simple Form is a Ruby library that prepares HTML nodes for you. It comes with a whole set of fields, but you can also add your own. To do so, you can create classes inheriting from Simple Form&amp;#39;s classes. Let&amp;#39;s consider defining a custom social network input field with a little &amp;#39;@&amp;#39; prefix in front of the actual field.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/inputs/social_handle_input.rb
class SocialHandleInput &amp;lt; SimpleForm::Inputs::Base
  def input(wrapper_options)
    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)

    &amp;quot;@ #{@builder.text_field(attribute_name, merged_input_options)}&amp;quot;.html_safe
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can use it in the following way:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= f.input :network_handle, as: :social_handle %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that, if your User model doesn&amp;#39;t have a &lt;code&gt;network_handle&lt;/code&gt; attribute, you must add it through a migration or cheat with an &lt;code&gt;attr_accessor&lt;/code&gt; in the model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;i18n Support&lt;/h2&gt;
&lt;p&gt;As we&amp;#39;ve mentioned, building forms is often a pain point. But things get even more painful when it comes to websites and applications that support multiple languages. Simple Form, thankfully, follows Ruby on Rails standards. You can use a &lt;code&gt;simple_form&lt;/code&gt; key in the local files to define translations for all labels, hints, placeholders, prompts, etc.&lt;/p&gt;
&lt;p&gt;Here is a little example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;en:
  simple_form:
    labels:
      user:
        username: &amp;#39;User name&amp;#39;
        password: &amp;#39;Password&amp;#39;
    hints:
      user:
        username: &amp;#39;User name to sign in.&amp;#39;
        password: &amp;#39;No special characters, please.&amp;#39;
    placeholders:
      user:
        username: &amp;#39;Your username&amp;#39;
        password: &amp;#39;****&amp;#39;
    include_blanks:
      user:
        age: &amp;#39;Rather not say&amp;#39;
    prompts:
      user:
        role: &amp;#39;Select your role&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Value Objects&lt;/h2&gt;
&lt;p&gt;Sometimes, we might use custom, non-ActiveRecord classes to instantiate the main object a form relies upon. This can be done to compose data from multiple models into a synthetic one for either business logic reasons or to gather several attributes into a single object.&lt;/p&gt;
&lt;p&gt;Whatever the reason, you can still rely on Simple Form to build forms for that kind of object. To do so, the object class needs to implement, in the best case, three methods: &lt;code&gt;to_model&lt;/code&gt;, &lt;code&gt;to_key&lt;/code&gt;, and &lt;code&gt;persisted?&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;to_model&lt;/code&gt; method will point to the object itself:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def to_model
  self
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;to_key&lt;/code&gt; allows us to point to the identifier attribute for the object — usually, that means an &lt;code&gt;id&lt;/code&gt; attribute named:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def to_key
  id
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, the &lt;code&gt;persisted?&lt;/code&gt; method is there to tell Simple Form if the object is directly persisted or not.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def persisted?
  false
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If that method isn&amp;#39;t present, the &lt;code&gt;f.submit&lt;/code&gt; helper isn&amp;#39;t usable.&lt;/p&gt;
&lt;p&gt;There is a faster way, though — including the &lt;code&gt;ActiveModel::Model&lt;/code&gt; module in the class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/account.rb
class Account
  include ActiveModel::Model

  attr_accessor :id, :company_name
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And let&amp;#39;s use it through the User model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/user.rb
class User &amp;lt; ApplicationRecord
  attr_accessor :network_handle

  def account
    @account ||= Account.new(id: 1, company_name: &amp;quot;Acme Inc.&amp;quot;)
  end

  def company_name
    account.company_name
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then add a field for the company name within the user form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= f.input :company_name %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, this attribute will not get saved in a table. However, the same concept can be applied to compose a value object with attributes pulled from several models.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ve seen that Simple Form is easy to install and integrate into your Ruby on Rails application. Simple Form not only takes care of the details of each field in your form, but also generates complex and complete HTML so your form can be styled and shaped with ease.&lt;/p&gt;
&lt;p&gt;I encourage you to dive into &lt;a href=&quot;https://www.rubydoc.info/gems/simple_form/1.3.1&quot;&gt;Simple Form&amp;#39;s documentation&lt;/a&gt; to see how powerful it can be.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Debugging in Ruby with pry-byebug</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/05/08/debugging-in-ruby-with-pry-byebug.html"/>
    <id>https://blog.appsignal.com/2024/05/08/debugging-in-ruby-with-pry-byebug.html</id>
    <published>2024-05-08T00:00:00+00:00</published>
    <updated>2024-05-08T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s set up and use pry-byebug, a gem that adds debugging and stack navigation to pry using byebug.</summary>
    <content type="html">&lt;p&gt;For a software engineer, even the basic use of a debugger can save a lot of pain: adding breakpoints (places in the code the program will stop at and expose the current context) is very easy, and navigating from one breakpoint to another isn&amp;#39;t difficult either.&lt;/p&gt;
&lt;p&gt;And with just that, you can say goodbye to a program&amp;#39;s many &lt;code&gt;puts&lt;/code&gt; and runs. Just add one or more breakpoints and run your program. Then you&amp;#39;re able to access not only the variables and objects you might have thought of, but also anything accessible from that point in the code.&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll focus on &lt;code&gt;pry-byebug&lt;/code&gt;, a gem that adds debugging and stack navigation to &lt;code&gt;pry&lt;/code&gt; using &lt;code&gt;byebug&lt;/code&gt;. We will see how to set up and use &lt;code&gt;pry-byebug&lt;/code&gt;, how it integrates with Ruby programs, and a few advanced techniques.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Set Up &lt;code&gt;pry-byebug&lt;/code&gt; for Ruby&lt;/h2&gt;
&lt;p&gt;The setup is really simple. Just add the &lt;a href=&quot;https://github.com/deivid-rodriguez/pry-byebug&quot;&gt;&lt;code&gt;pry-byebug&lt;/code&gt; gem&lt;/a&gt; to your Gemfile, and then run &lt;code&gt;bundle install&lt;/code&gt;. I&amp;#39;d advise you to add it to the &lt;code&gt;development&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; groups to debug tests as well.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s now cover some simple debugging using breakpoints.&lt;/p&gt;
&lt;h3&gt;Basic Debugging with Breakpoints&lt;/h3&gt;
&lt;p&gt;The whole principle of debuggers is to get rid of &lt;code&gt;printf debugging&lt;/code&gt; by giving you direct access to a running program&amp;#39;s stack. So, we need to add breakpoints to tell the interpreter where to stop.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take the example of a simple bookstore with software that manages the titles it offers.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Book
  attr_accessor :title, :author, :price

  def initialize(title, author, price)
    @title = title
    @author = author
    @price = price
  end
end

class BookStore
  def initialize
    @books = []
  end

  def add_book(book)
    @books &amp;lt;&amp;lt; book
  end

  def remove_book(title)
    @books.delete_if { |book| book.title == title }
  end

  def find_by_title(title)
    @books.find { |book| book.title.include?(title) }
  end
end

# Sample Usage:
store = BookStore.new
book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)

store.add_book(book1)
store.add_book(book2)
store.add_book(book3)

puts store.find_by_title(&amp;quot;Hobbit&amp;quot;).title
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This program is simple enough. Of course, there is no storage, but that&amp;#39;s not needed to demonstrate what we want to do.&lt;/p&gt;
&lt;p&gt;The last call will probably list &amp;quot;The Hobbit&amp;quot;, but that&amp;#39;s not certain and might not be the one we want. To debug this, it&amp;#39;s best to &amp;quot;jump&amp;quot; into the &lt;code&gt;find_by_title&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;So, add the following lines to the top of the file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;pry&amp;#39;
require &amp;#39;pry-byebug&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will also add a breakpoint to the method using &lt;code&gt;binding.pry&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def find_by_title(title)
  binding.pry
  @books.find { |book| book.title.include?(title) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s then launch the program and get to the breakpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;From: test-pry-byebug/app.rb:29 BookStore#find_by_title:

    27: def find_by_title(title)
    28:   binding.pry
 =&amp;gt; 29:   @books.find { |book| book.title.include?(title) }
    30: end

[1] pry(#&amp;lt;BookStore&amp;gt;)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing you should see is that we know exactly where we are: there&amp;#39;s no need to guess which part of the program that line in STDOUT comes from. Of course, we have only one breakpoint here, but you can easily understand how useful this might be if many breakpoints are used.&lt;/p&gt;
&lt;p&gt;We can then check the value of the &lt;code&gt;title&lt;/code&gt; variable. There likely isn&amp;#39;t much to be surprised by here. The issue is with the use of &lt;code&gt;find&lt;/code&gt; (instead of &lt;code&gt;select&lt;/code&gt;, or something more complex) to find and return a list of books instead of just one book.&lt;/p&gt;
&lt;p&gt;By being right in the context, you can experiment with both &lt;code&gt;find&lt;/code&gt; and &lt;code&gt;select&lt;/code&gt; and decide on a course of action.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Here might be a good point to reflect on the expected behavior of the program you are building and this piece of code. It might be good to clarify what the code should do through RSpec tests.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once you are ok with that step or want to see what&amp;#39;s happening until the next breakpoint, type &lt;code&gt;continue&lt;/code&gt;, and the program execution will start again.&lt;/p&gt;
&lt;h3&gt;Making Small Steps from Breakpoints&lt;/h3&gt;
&lt;p&gt;With &lt;code&gt;pry-byebug&lt;/code&gt;, you can take smaller steps from breakpoints to see what happens after each line (particularly helpful for multiple calls using a method). Once you have stepped into the program and are at a breakpoint, use the &lt;code&gt;next&lt;/code&gt; command to execute the next line.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take the case of adding a breakpoint within the &lt;code&gt;add_book&lt;/code&gt; method just before the first three books are created in the bookstore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def add_book(book)
  binding.pry
  @books &amp;lt;&amp;lt; book
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;store = BookStore.new
binding.pry
book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Upon execution, the program will first stop just before the &lt;code&gt;book1&lt;/code&gt; variable is instantiated. We might want to know the result of that call. To do so, we don&amp;#39;t have to stop the program and add another breakpoint. We just have to type &lt;code&gt;next&lt;/code&gt; in the pry console and hit &lt;code&gt;Enter&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;From: test-pry-byebug/app.rb:37 :

    32: end
    33:
    34: # Sample Usage:
    35: store = BookStore.new
    36: binding.pry
 =&amp;gt; 37: book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
    38: book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
    39: book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
    40:
[1] pry(main)&amp;gt; next
From: test-pry-byebug/app.rb:38 :

    33:
    34: # Sample Usage:
    35: store = BookStore.new
    36: binding.pry
    37: book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
 =&amp;gt; 38: book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
    39: book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then have access to &lt;code&gt;book1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[1] pry(main)&amp;gt; book1
=&amp;gt; #&amp;lt;Book:0x00007fad37f964d8 @author=&amp;quot;Frank Herbert&amp;quot;, @price=20.0, @title=&amp;quot;Dune&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This beats a lot of back-and-forth insertion and tweaking &lt;code&gt;puts&lt;/code&gt; calls.&lt;/p&gt;
&lt;h3&gt;Even Smaller Steps&lt;/h3&gt;
&lt;p&gt;We might want to step into the &lt;code&gt;initialize&lt;/code&gt; method (called through &lt;code&gt;new&lt;/code&gt;) to see more details. We can do so by using &lt;code&gt;step&lt;/code&gt; instead of &lt;code&gt;next&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;From: test-pry-byebug/app.rb:8 Book#initialize:

     7: def initialize(title, author, price)
 =&amp;gt;  8:   @title = title
     9:   @author = author
    10:   @price = price
    11: end

[1] pry(#&amp;lt;Book&amp;gt;)&amp;gt; title
=&amp;gt; &amp;quot;The Hobbit&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows us to step into a method when it&amp;#39;s called rather than skip over it as we do with &lt;code&gt;next&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Free from &lt;code&gt;puts&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;By now, you should see how this approach saves time and frees us from using &lt;code&gt;puts&lt;/code&gt; or its equivalent to debug a program. Right from the debugger shell, we can look into the values of objects and variables to figure out why a program behaves like it does. Furthermore, you can save a lot of time from the shell by adding breakpoints on the fly, thus avoiding the &amp;quot;quit, move &lt;code&gt;binding.pry&lt;/code&gt;, restart&amp;quot; loop.&lt;/p&gt;
&lt;h3&gt;Finishing a Debugging Session&lt;/h3&gt;
&lt;p&gt;You can call &lt;code&gt;continue&lt;/code&gt; to go to the next breakpoint or the end of a program. &lt;code&gt;finish&lt;/code&gt; executes and finishes the current step, returning just after that step. In the previous example, we end up at the following line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[2] pry(#&amp;lt;Book&amp;gt;)&amp;gt; finish

From: test-pry-byebug/app.rb:39 :

    34: # Sample Usage:
    35: store = BookStore.new
    36: binding.pry
    37: book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
    38: book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
 =&amp;gt; 39: book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, &lt;code&gt;!!!&lt;/code&gt; exits the debugger directly.&lt;/p&gt;
&lt;h2&gt;Advanced Use Cases of &lt;code&gt;pry-byebug&lt;/code&gt; for Ruby&lt;/h2&gt;
&lt;p&gt;Now let&amp;#39;s dive into a few more advanced use cases, including rewinding and replaying, adding breakpoints on the fly, and conditional breakpoints.&lt;/p&gt;
&lt;h3&gt;Rewinding and Replaying&lt;/h3&gt;
&lt;p&gt;Can we move back and forth between two points? Yes, totally. Given our previous case, we could move up and down from the &lt;code&gt;initialize&lt;/code&gt; method to its call. Just use &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; commands after &lt;code&gt;step&lt;/code&gt; to get into the method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;From: test-pry-byebug/app.rb:37 :

    34: # Sample Usage:
    35: store = BookStore.new
    36: binding.pry
 =&amp;gt; 37: book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
    38: book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
    39: book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)


[1] pry(main)&amp;gt; step

From: test-pry-byebug/app.rb:8 Book#initialize:

     7: def initialize(title, author, price)
 =&amp;gt;  8:   @title = title
     9:   @author = author
    10:   @price = price
    11: end

[1] pry(#&amp;lt;Book&amp;gt;)&amp;gt; up

From: test-pry-byebug/app.rb:37 :

    35: store = BookStore.new
    36: binding.pry
 =&amp;gt; 37: book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
    38: book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
    39: book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)


[1] pry(main)&amp;gt; down

From: test-pry-byebug/app.rb:8 Book#initialize:

     7: def initialize(title, author, price)
 =&amp;gt;  8:   @title = title
     9:   @author = author
    10:   @price = price
    11: end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we need details about the stack we are in, we can call the &lt;code&gt;backtrace&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[1] pry(#&amp;lt;Book&amp;gt;)&amp;gt; backtrace
--&amp;gt; #0  Book.initialize(title#String, author#String, price#Float) at test-pry-byebug/app.rb:8
    ͱ-- #1  Class.new(*args) at test-pry-byebug/app.rb:37
    #2  &amp;lt;main&amp;gt; at test-pry-byebug/app.rb:37

From: /mnt/data/Code/Pier22/courses/raw-courses/rspec/git-repo/test-pry-byebug/app.rb:8 Book#initialize:

     7: def initialize(title, author, price)
 =&amp;gt;  8:   @title = title
     9:   @author = author
    10:   @price = price
    11: end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The list of frames is in the stack at the top, numbered from 0 to 2. A little cursor shows us which frame we are currently in.&lt;/p&gt;
&lt;h2&gt;Add Breakpoints On the Fly&lt;/h2&gt;
&lt;p&gt;To avoid additional debugger exits, we can add breakpoints on the fly from the debugger console, using the &lt;code&gt;break&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;You can add breakpoints in the current file or another file, to a specific line or the start of a specific method.&lt;/p&gt;
&lt;p&gt;For example, let&amp;#39;s add a breakpoint to the start of the &lt;code&gt;find_by_title&lt;/code&gt; method of the &lt;code&gt;BookStore&lt;/code&gt; class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[2] pry(main)&amp;gt; break BookStore#find_by_title

  Breakpoint 2: BookStore#find_by_title (Enabled)

  28: def find_by_title(title)
29:   @books.find { |book| book.title.include?(title) }
30: end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add it to line 38 of the current file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[1] pry(main)&amp;gt; break 38

  Breakpoint 1: /mnt/data/Code/Pier22/courses/raw-courses/rspec/git-repo/test-pry-byebug/app.rb @ 38 (Enabled)

      35: binding.pry
    36: book1 = Book.new(&amp;quot;Dune&amp;quot;, &amp;quot;Frank Herbert&amp;quot;, 20.0)
    37: book2 = Book.new(&amp;quot;The Hobbit&amp;quot;, &amp;quot;J.R.R. Tolkien&amp;quot;, 15.0)
 =&amp;gt; 38: book3 = Book.new(&amp;quot;Hobbit&amp;#39;s Journey&amp;quot;, &amp;quot;Unknown&amp;quot;, 10.0)
    39:
    40: store.add_book(book1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A call to &lt;code&gt;break&lt;/code&gt; lists all the breakpoints in place:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[4] pry(main)&amp;gt; break

  # Enabled At
  -------------

  1 Yes     /mnt/data/Code/Pier22/courses/raw-courses/rspec/git-repo/test-pry-byebug/app.rb @ 38
  2 Yes     BookStore#find_by_title
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that they are numbered, so you can actually delete them using the &lt;code&gt;--delete&lt;/code&gt; argument to &lt;code&gt;break&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[4] pry(main)&amp;gt; break --delete 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Conditional Breakpoints&lt;/h3&gt;
&lt;p&gt;You might want conditional breakpoints, too.&lt;/p&gt;
&lt;p&gt;Imagine that your code only misbehaves if a &lt;code&gt;user&lt;/code&gt; object&amp;#39;s &lt;code&gt;role&lt;/code&gt; attribute is set to &lt;code&gt;:admin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Well, you might want to add the following breakpoint, and a condition to trigger it so that the debugger only stops if that condition is set:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[1] pry(main)&amp;gt; break BookStore#add_book if book.title.match /Dune/

  Breakpoint 1: BookStore#add_book (Enabled) Condition: book.title.match /Dune/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;continue&lt;/code&gt;, the debugger executes the addition of &lt;code&gt;Dune&lt;/code&gt; without stopping, until the next call to &lt;code&gt;add_book&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is a great feature that will save you a lot of time.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;By now, you should have fewer reasons to rely on &lt;code&gt;printf()&lt;/code&gt; debugging when facing issues with your Ruby code. You now know how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;pry-byebug&lt;/code&gt; and some of its plugins&lt;/li&gt;
&lt;li&gt;Add breakpoints from your favorite code editor with &lt;code&gt;binding.pry&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Start a debugger session (starting the app)&lt;/li&gt;
&lt;li&gt;Look at the value of variables and objects from the debugger session (simply calling the object or variable from the debugger console)&lt;/li&gt;
&lt;li&gt;Navigate within the execution frames from the debugger console (with &lt;code&gt;up&lt;/code&gt;, &lt;code&gt;down&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt;, and &lt;code&gt;frame&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add breakpoints on the fly from the debugger console&lt;/li&gt;
&lt;li&gt;Add conditional breakpoints from the debugger console (with &lt;code&gt;break ClassName#method_name if var.nil?&lt;/code&gt;, for example)&lt;/li&gt;
&lt;li&gt;List and remove breakpoints (with &lt;code&gt;break&lt;/code&gt;, &lt;code&gt;break --delete &amp;lt;number&amp;gt;&lt;/code&gt;, and &lt;code&gt;break --disable-all&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Conclude your debugging session with &lt;code&gt;finish&lt;/code&gt;, &lt;code&gt;continue&lt;/code&gt;, or &lt;code&gt;!!!&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, you can also configure &lt;code&gt;pry-byebug&lt;/code&gt; through the &lt;code&gt;~/.pryrc&lt;/code&gt; file to add aliases and a few custom behaviors. See the main &lt;a href=&quot;https://github.com/deivid-rodriguez/pry-byebug#matching-byebug-behaviour&quot;&gt;pry-byebug README&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pry-byebug&lt;/code&gt; is a tool that will help you a lot over the years, saving you time and headaches. It&amp;#39;s definitely worth practicing its use so that you can confidently open it the next time you&amp;#39;re unsure how your code is behaving.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.P.S. &lt;a href=&quot;https://www.appsignal.com/tour/errors&quot;&gt;Use AppSignal for Ruby for deeper debugging insights&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>AnyCable for Ruby on Rails: How Does it Improve over Action Cable?</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/05/01/anycable-for-ruby-on-rails-how-does-it-improve-over-action-cable.html"/>
    <id>https://blog.appsignal.com/2024/05/01/anycable-for-ruby-on-rails-how-does-it-improve-over-action-cable.html</id>
    <published>2024-05-01T00:00:00+00:00</published>
    <updated>2024-05-01T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s see what AnyCable brings to the table and how it outperforms Action Cable.</summary>
    <content type="html">&lt;p&gt;In modern web applications, real-time communication has become more than a feature: it&amp;#39;s gradually evolved into a necessity. Users expect instant updates, live interactions, and dynamic content.&lt;/p&gt;
&lt;p&gt;In Rails applications, Action Cable has long been the go-to solution, harnessing WebSockets to fulfill these demands. In this article, we introduce:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The basics of WebSockets&lt;/li&gt;
&lt;li&gt;How Action Cable enables real-time communication via WebSockets&lt;/li&gt;
&lt;li&gt;Why AnyCable was created&lt;/li&gt;
&lt;li&gt;How to get AnyCable up and running&lt;/li&gt;
&lt;li&gt;The improvements AnyCable brings to the table&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;The WebSocket Technology&lt;/h2&gt;
&lt;p&gt;The WebSocket technology was introduced in 2011 with the publication of &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6455&quot;&gt;RFC 6455&lt;/a&gt;, titled &amp;quot;The WebSocket Protocol&amp;quot;. This standardized protocol facilitated the establishment of persistent, real-time communication channels between web browsers and servers, allowing for more efficient and continuous data exchange compared to traditional HTTP.&lt;/p&gt;
&lt;p&gt;The WebSocket technology enables continuous bi-directional communication between clients and servers, a stark departure from the request-response model of HTTP (where connections terminate after each exchange). This persistence in communication proves pivotal for real-time applications like chat platforms and gaming systems.&lt;/p&gt;
&lt;p&gt;Before the introduction of WebSocket technology, many applications often relied on a technique called polling. Polling involves the client repeatedly sending requests to the server at specified intervals to check for updates or new information. While this method allowed for some level of real-time updates, it was less efficient than WebSocket communication. Polling could lead to unnecessary server load and increased latency, as the client had to initiate a new request each time it wanted to retrieve updated data.&lt;/p&gt;
&lt;p&gt;WebSocket technology addressed these limitations by establishing a persistent connection between the client and the server, enabling more immediate and efficient data transmission without the need for constant polling. This shift significantly improved the responsiveness and real-time capabilities of applications, providing a seamless and dynamic experience for users, particularly in scenarios demanding instant, up-to-the-moment information.&lt;/p&gt;
&lt;p&gt;Both Action Cable and AnyCable harness the power of web sockets to furnish Rails applications with real-time updates, liberating users from the cumbersome need to refresh pages or poll for the latest information.&lt;/p&gt;
&lt;h2&gt;Action Cable and Real-time Communication&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://edgeguides.rubyonrails.org/action_cable_overview.html&quot;&gt;Action Cable&lt;/a&gt; enables the incorporation of real-time features into a Rails application via web sockets. This real-time communication happens between the client side (browser) and the Rails server. A few things are important to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;connection&lt;/em&gt; forms the foundation of this client-server relationship. For every WebSocket connection, Action Cable creates one connection instance.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;channel&lt;/em&gt; refers to a communication pathway that allows clients to subscribe to and receive updates about specific topics or categories of information, e.g., &lt;code&gt;BookChannel&lt;/code&gt;, &lt;code&gt;MovieChannel&lt;/code&gt;, &lt;code&gt;SportsChannel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;subscriber&lt;/em&gt; is a client subscribed to a channel to receive information.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Broadcasting&lt;/em&gt; is when the server publishes information that is transmitted by the relevant channel to the subscribed client/s.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;stream&lt;/em&gt; is the route or pathway through which broadcasts get to the subscribers. By using &lt;code&gt;stream_from&lt;/code&gt;, subscriptions can be made for broadcasting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a connection is established between the client and the server, Action Cable creates a connection object for that connection. With this connection, the client can subscribe to one or more channels. Action Cable broadcasts updates to all subscribed clients via their respective channels.&lt;/p&gt;
&lt;p&gt;A simple example is a browser view showing a library book list that updates when a book is added.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# create the rails application
rails new book_list
cd book_list
# create the needed controller and model
rails g controller books index
rails g model Book name
rails db:migrate
# change the development Action Cable adapter from Async (the default one) to Redis
rails turbo:install:redis
# start the redis server
redis-server
# start the rails server
rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get a real-time update on the book model, we employ Action Cable for broadcasting the updates. We set up Action Cable to broadcast these updates to a channel called &amp;quot;books&amp;quot;. Via Turbo Streams, we create the subscriber to this channel and deliver the updates.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt; # app/models/books.rb
class Book &amp;lt; ApplicationRecord
  broadcasts_to -&amp;gt; (book) { :books }
  # Broadcasts all the activities on this model -&amp;gt; create, update, destroy.
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now render the list of books:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/views/rooms/_book.html.erb
&amp;lt;div id=&amp;lt;%= dom_id(book) %&amp;gt;&amp;gt; &amp;lt;%= book.name %&amp;gt; &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/views/books/index.html.erb
&amp;lt;h1&amp;gt;Books&amp;lt;/h1&amp;gt;
&amp;lt;%= turbo_stream_from &amp;quot;books&amp;quot; %&amp;gt;
&amp;lt;div id=&amp;quot;books&amp;quot;&amp;gt;
  &amp;lt;%= render @books %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As seen below, different actions trigger a broadcast, and Action Cable broadcasts to the channel specified — &amp;quot;books&amp;quot;. The view is also subsequently updated to reflect these updates.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; Book.create(name: &amp;quot;The Avatar&amp;quot;)
  Rendered books/_book.html.erb (Duration: 0.0ms | Allocations: 11)
[ActionCable] Broadcasting to books: &amp;quot;&amp;lt;turbo-stream action=\&amp;quot;append\&amp;quot; target=\&amp;quot;books\&amp;quot;&amp;gt;&amp;lt;template&amp;gt;&amp;lt;div&amp;gt; The Avatar &amp;lt;/div&amp;gt;&amp;lt;/template&amp;gt;&amp;lt;/turbo-stream&amp;gt;&amp;quot;
=&amp;gt; #&amp;lt;Book:0x0000000107c13358&amp;gt;

irb&amp;gt; book_1 = Book.find(1)

irb&amp;gt; book_1.update(name: &amp;quot;The Demo&amp;quot;)
  Rendered books/_book.html.erb (Duration: 0.0ms | Allocations: 11)
[ActionCable] Broadcasting to books: &amp;quot;&amp;lt;turbo-stream action=\&amp;quot;replace\&amp;quot; target=\&amp;quot;book_1\&amp;quot;&amp;gt;&amp;lt;template&amp;gt;&amp;lt;div&amp;gt; The Demo &amp;lt;/div&amp;gt;&amp;lt;/template&amp;gt;&amp;lt;/turbo-stream&amp;gt;&amp;quot;
=&amp;gt; true

irb&amp;gt; book_1.destroy
[ActionCable] Broadcasting to books: &amp;quot;&amp;lt;turbo-stream action=\&amp;quot;remove\&amp;quot; target=\&amp;quot;book_7\&amp;quot;&amp;gt;&amp;lt;/turbo-stream&amp;gt;&amp;quot;
=&amp;gt; #&amp;lt;Book:0x0000000107bd2948
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;In Rails, integrating Action Cable (and, as a result, real-time updates into an application) is quite easy: even a beginner can get up and running in minutes. With Action Cable, we can manage connections, channels, and broadcasting to clients without worrying about the underlying WebSocket details. It also uses the same connection for multiple subscriptions, reducing the overhead of creating new connections for each new real-time update.&lt;/p&gt;
&lt;h2&gt;Why AnyCable for Ruby on Rails?&lt;/h2&gt;
&lt;p&gt;Action Cable is built on Ruby and has significant differences from other servers written in Golang and Erlang. &lt;a href=&quot;https://evilmartians.com/chronicles/anycable-actioncable-on-steroids&quot;&gt;Check out a detailed summary of AnyCable vs. Action Cable benchmark findings&lt;/a&gt;. Here are the key results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Action Cable consumes approximately 4 times more memory when handling 20 thousand idle clients without subscriptions or transmissions.&lt;/li&gt;
&lt;li&gt;Action Cable requires much higher CPU usage.&lt;/li&gt;
&lt;li&gt;Action Cable takes around 10 times longer to broadcast to 10,000 clients. Starting at 1 second for a thousand clients, the time required increases by 1 second for every additional thousand clients.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://anycable.io/&quot;&gt;AnyCable WebSocket Server&lt;/a&gt; was created to combine the beauty of Action Cable with the performance benefits gained from Golang. AnyCable handles WebSockets on a different server called &lt;a href=&quot;https://github.com/anycable/anycable-go&quot;&gt;AnyCable-Go&lt;/a&gt;, effectively reducing the burden on your primary web application.&lt;/p&gt;
&lt;p&gt;In addition to performance advantages, AnyCable offers &lt;strong&gt;resumability&lt;/strong&gt;. If a client becomes disconnected from the server (e.g., due to network issues leading to an offline status), upon reconnection, the client will receive all the messages missed during the disconnection. They are also not required to re-subscribe as sessions are recovered. &lt;a href=&quot;https://youtu.be/4n1SxfK0Uqg?t=916&quot;&gt;See a brief demo of the resumability feature&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;View detailed information on the &lt;a href=&quot;https://evilmartians.com/chronicles/enter-anycable-v1-4-reliable-real-time-features-for-apps-of-any-size&quot;&gt;recent enhancements to AnyCable&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Integrating AnyCable&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s replace Action Cable with AnyCable in our &lt;code&gt;BookList&lt;/code&gt; example.&lt;/p&gt;
&lt;p&gt;Install the gem:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# add the gem to your gemfile
gem &amp;quot;anycable-rails&amp;quot;

# install it
bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, run the setup generator.&lt;/p&gt;
&lt;p&gt;This generator is interactive and asks questions about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The type of development environment in use.&lt;/li&gt;
&lt;li&gt;How you would like to install the &lt;code&gt;AnyCable-Go&lt;/code&gt; websocket server.&lt;/li&gt;
&lt;li&gt;If Heroku is being used for deployment.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;JWT&lt;/code&gt; is the intended means of authentication.&lt;/li&gt;
&lt;li&gt;If you want a &lt;code&gt;Procfile.dev&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this information helps to reduce the amount of manual work you need to do.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g anycable:setup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you did say &amp;quot;yes&amp;quot; to the generation of a &lt;code&gt;Procfile.dev&lt;/code&gt;, it would look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# Procfile.dev

web: bin/rails s
anycable: bundle exec anycable
ws: bin/anycable-go --port=8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hence, you do not need to manually run the rest of the commands below.&lt;/p&gt;
&lt;p&gt;Run the AnyCable RPC server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec anycable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the WebSocket server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;anycable-go --port=8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart the Rails server.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;cable.yml&lt;/code&gt; file, you&amp;#39;ll find that the adapter changes to &lt;code&gt;any_cable&lt;/code&gt; if the &lt;code&gt;ACTION_CABLE_ADAPTER&lt;/code&gt; environment variable is not set.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;adapter: &amp;lt;%= ENV.fetch(&amp;quot;ACTION_CABLE_ADAPTER&amp;quot;, &amp;quot;any_cable&amp;quot;) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In all the &lt;code&gt;$env.rb&lt;/code&gt; files, you&amp;#39;ll find the Action Cable URL set as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;config.action_cable.url = ActionCable.server.config.url = ENV.fetch(&amp;quot;CABLE_URL&amp;quot;, &amp;quot;/cable&amp;quot;) if AnyCable::Rails.enabled?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;any_cable.yml&lt;/code&gt; file, the Redis channel is set to &lt;code&gt;__anycable__&lt;/code&gt;. You can confirm this in the terminal via:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;redis-cli PUBSUB CHANNELS
# 1) &amp;quot;__anycable__&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Read more about the &lt;a href=&quot;https://docs.anycable.io/ruby/configuration&quot;&gt;AnyCable configuration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the Rails server, you&amp;#39;ll find a routing error related to the &lt;code&gt;/cable&lt;/code&gt; endpoint. You&amp;#39;ll also find that, when you filter by &lt;code&gt;websockets&lt;/code&gt; in the &lt;code&gt;Network&lt;/code&gt; tab of the browser, the &lt;code&gt;Request URL&lt;/code&gt; for &lt;code&gt;cable&lt;/code&gt; is &lt;code&gt;ws://localhost:3000/cable&lt;/code&gt;, which is wrong. We should be attempting to connect to &lt;code&gt;ws://localhost:8080/cable&lt;/code&gt;, the value of &lt;code&gt;config.action_cable.url&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To fix this, the &lt;code&gt;action_cable_meta_tag&lt;/code&gt; has to be added to the &lt;code&gt;application.html.erb&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# application.html.erb
&amp;lt;head&amp;gt;
# ...
  &amp;lt;%= action_cable_meta_tag %&amp;gt;
# ...
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This resets the &lt;code&gt;url&lt;/code&gt; to the set value. At this point, any broadcasted stream will show up!&lt;/p&gt;
&lt;h2&gt;Swapping Action Cable for AnyCable in Ruby&lt;/h2&gt;
&lt;p&gt;To enable resumability, AnyCable utilizes the &lt;a href=&quot;https://docs.anycable.io/misc/action_cable_protocol?id=action-cable-extended-protocol&quot;&gt;Action Cable Extended Protocol&lt;/a&gt;, which is implemented by the AnyCable server version 1.4 and later. This protocol is inherently supported by the &lt;a href=&quot;https://github.com/anycable/anycable-client/tree/master?tab=readme-ov-file&quot;&gt;AnyCable JS client&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To integrate this with Turbo Streams, it&amp;#39;s necessary to swap out the default Action Cable client for the AnyCable client. However, when using the &lt;code&gt;@hotwired/turbo-rails&lt;/code&gt; package, the Action Cable &lt;code&gt;getConsumer&lt;/code&gt; function is invoked before the &lt;code&gt;setConsumer&lt;/code&gt; function during the initial page load, leading to the consumer not being overridden, and two web socket connections being opened. The &lt;a href=&quot;https://github.com/anycable/anycable-client/blob/master/packages/turbo-stream/README.md&quot;&gt;@anycable/turbo-stream&lt;/a&gt; package was created to handle this (&lt;a href=&quot;https://anycable.io/blog/anycasts-using-anycable-client/&quot;&gt;read additional details about this issue&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the procedure for this swap.&lt;/p&gt;
&lt;p&gt;Add the &lt;code&gt;@anycable/web&lt;/code&gt; and &lt;code&gt;@anycable/turbo-stream&lt;/code&gt; packages:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bin/importmap pin @anycable/web @anycable/turbo-stream
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pins the stated packages, along with &lt;code&gt;@anycable/core&lt;/code&gt;, &lt;code&gt;@hotwired/turbo&lt;/code&gt;, and &lt;code&gt;nanoevents&lt;/code&gt; to your &lt;code&gt;importmap.rb&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Create the &lt;code&gt;anycable&lt;/code&gt; client:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// app/javascript/cable.js
import { createCable } from &amp;quot;@anycable/web&amp;quot;;

export default createCable();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Refer to this as the new &amp;quot;cable&amp;quot; client:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/importmap.rb
pin &amp;quot;cable&amp;quot;, to: &amp;quot;cable.js&amp;quot;, preload: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the new cable client:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// app/javascript/application.js
// We no longer need @hotwired/turbo-rails
import &amp;quot;@hotwired/turbo&amp;quot;;
import { start } from &amp;quot;@anycable/turbo-stream&amp;quot;;
import cable from &amp;quot;cable&amp;quot;;

start(cable);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this, we can restart our server, and the consumer/client is replaced with AnyCable.&lt;/p&gt;
&lt;p&gt;Read more about &lt;a href=&quot;https://github.com/anycable/anycable-client/blob/master/packages/turbo-stream/README.md&quot;&gt;advanced configurations for the Turbo Streams package&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With the client replaced, &lt;a href=&quot;https://docs.anycable.io/anycable-go/reliable_streams?id=quick-start&quot;&gt;resumability can be implemented&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With resumable sessions, the supported adapters are &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;redisx&lt;/code&gt; (if you use Redis). It is, however, important to note that the broker &lt;code&gt;preset&lt;/code&gt; command automatically sets the adapter as &lt;code&gt;http&lt;/code&gt;. Hence, when using &lt;code&gt;redisx&lt;/code&gt;, it must specified explicitly like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ anycable-go --broker=memory --broadcast_adapter=redisx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you go! You should have resumable sessions working!&lt;/p&gt;
&lt;h2&gt;Community Adoption and Feedback&lt;/h2&gt;
&lt;p&gt;AnyCable has existed for several years and has gained widespread adoption among numerous organizations. Adopters have cited several compelling reasons for its attractiveness, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Seamless migration without the need for architectural changes.&lt;/li&gt;
&lt;li&gt;No requirement to modify Action Cable channels or connections.&lt;/li&gt;
&lt;li&gt;Utilization of a dedicated server for WebSockets.&lt;/li&gt;
&lt;li&gt;Ability to handle a substantial number of concurrent connections without encountering any problems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/orgs/anycable/discussions/68&quot;&gt;See a few testimonials from satisfied users&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ve seen that AnyCable offers performance enhancements to Action Cable and can be used as an alternative when dealing with a large number of connections or high traffic.&lt;/p&gt;
&lt;p&gt;With recent improvements to AnyCable (such as the introduction of &lt;strong&gt;reliable streams&lt;/strong&gt;), AnyCable has become a &lt;strong&gt;solid choice regardless of performance concerns&lt;/strong&gt;. It supports broadcasting and subscription confirmation, thereby making it consistent and reliable. It can also be used in &lt;a href=&quot;https://docs.anycable.io/ruby/non_rails&quot;&gt;non-Rails applications&lt;/a&gt;, and to &lt;a href=&quot;https://docs.anycable.io/guides/serverless&quot;&gt;power serverless JavaScript applications&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Overall, &lt;a href=&quot;https://anycable.io/&quot;&gt;AnyCable&lt;/a&gt; is a reliable choice for modern web applications and supports a variety of servers, allowing for greater flexibility in development.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Should You Use Ruby on Rails or Hanami?</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/04/24/should-you-use-ruby-on-rails-or-hanami.html"/>
    <id>https://blog.appsignal.com/2024/04/24/should-you-use-ruby-on-rails-or-hanami.html</id>
    <published>2024-04-24T00:00:00+00:00</published>
    <updated>2024-04-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore the strengths and weaknesses of Rails and Hanami.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;This post was updated on 23 May 2024 to clarify the differences in models and data persistence between Rails and Hanami.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ruby on Rails is the most popular web framework in the Ruby ecosystem and has a large user base, ranging from freelancers to large established companies. With an active user community and wide-ranging documentation, it can be used to build everything from simple applications to complex web platforms.&lt;/p&gt;
&lt;p&gt;That said, a new contestant is taking on Rails’ dominance for the full-stack Ruby framework title: Hanami. It is a fast, modular Ruby framework with improved performance and maintainability compared to Rails.&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll explore the strengths and weaknesses of each framework in terms of performance, features, testing, and more. So whether you are looking to build a customer-facing web app, an internal tool, or a massively scalable API, you should leave being better informed about what to use for your next project.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started with a brief introduction to each framework.&lt;/p&gt;
&lt;h2&gt;Introducing Ruby on Rails&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://guides.rubyonrails.org/getting_started.html&quot;&gt;Ruby on Rails&lt;/a&gt; is the most well-known Ruby web application development framework with a mission to enhance developer productivity (by making a bunch of assumptions about how apps should be built, often referred to as the &amp;quot;Rails way&amp;quot;).&lt;/p&gt;
&lt;p&gt;Some of these assumptions include making sure developers do not spend time configuring stuff, or what is termed as &amp;quot;convention over configuration&amp;quot;, and an emphasis on using DRY (&amp;quot;don&amp;#39;t repeat yourself&amp;quot;) principles. DRY principles encourage a developer to avoid repeating code over and over again, but instead use single and focused representations of app functionality to ensure maintainability and organization.&lt;/p&gt;
&lt;h2&gt;Introducing Hanami&lt;/h2&gt;
&lt;p&gt;While Rails is very well-known in the Ruby community, &lt;a href=&quot;https://hanamirb.org/&quot;&gt;Hanami&lt;/a&gt; is less so. It&amp;#39;s a fairly new modern Ruby framework trying to take on Rails&amp;#39; dominance of the full-stack web framework space.&lt;/p&gt;
&lt;p&gt;Hanami is built from the ground up to have a small memory footprint and a focus on modularity, which, in turn, makes for a very fast and nimble framework.&lt;/p&gt;
&lt;p&gt;Obviously, these brief introductions won&amp;#39;t really give you all the information you need to decide on the framework that suits you best. For that, we&amp;#39;ll need to dive deeper into each, starting with how they are structured.&lt;/p&gt;
&lt;h2&gt;Structure and Architecture of Rails and Hanami&lt;/h2&gt;
&lt;p&gt;Rails and Hanami are somewhat similar in that both are Ruby frameworks. However, how each is built and their application architecture is where you&amp;#39;ll find most of the differences.&lt;/p&gt;
&lt;p&gt;For starters, with Rails, you have fewer files or &lt;em&gt;abstractions&lt;/em&gt; (the building blocks you use to build your app), which tend to grow in size as you develop your app. On the other hand, Hanami takes abstractions to a whole new level, with lots of files that tend to be smaller in size.&lt;/p&gt;
&lt;p&gt;The diagrams below illustrate the point more clearly. Let&amp;#39;s start with Rails.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-04/rails-app-structure.png&quot; alt=&quot;Rails app structure&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now compare the Rails abstraction diagram with the Hanami one below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-04/hanami-app-structure.png&quot; alt=&quot;Hanami app structure&quot;/&gt;&lt;/p&gt;
&lt;p&gt;As you can see, each framework generally follows the &lt;em&gt;model-view-controller&lt;/em&gt; (MVC) structure. However, Hanami takes abstraction to the next level.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s a simplified outline of how each framework is organized:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Routes&lt;/strong&gt; - Each framework has a routes definition with your app&amp;#39;s endpoints.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controllers vs. actions&lt;/strong&gt; - In Rails, you get controllers with one or a couple of actions in them. Controllers receive and respond to route requests by directing them to the relevant actions. In Hanami, there are no controllers. Instead, you go straight to self-contained actions (each action has its own self-contained class).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Models and persistence&lt;/strong&gt; - The Rails model layer takes care of data validation and database communication, including any queries your app may have. Persistence is handled by the Ruby Object Mapper (ROM). In Hanami, the model layer is more abstract, separated into entities and repositories. Entities handle domain logic and are database-agnostic, whereas &lt;em&gt;repositories&lt;/em&gt; (repos) are used to communicate with the database. Hanami 2.0 onwards ships without a pre-configured persistence layer, allowing you to choose an Object Relational Mapper (ORM) that suits your needs or program ORM free.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View rendering&lt;/strong&gt; - As with the persistence layer, Ruby on Rails views tend to contain everything you need for rendering data to the outside world in one place. This includes all the HTML structure, view helpers, and view logic. When it comes to view rendering in Hanami, things are more abstract. For starters, you have &lt;em&gt;views&lt;/em&gt; that utilize any view helpers the app has and also render the &lt;em&gt;template&lt;/em&gt;. The &lt;em&gt;template&lt;/em&gt; handles the actual HTML structure, while the &lt;em&gt;parts&lt;/em&gt; take care of any presentation logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, what does all this mean when it comes to building an app? With fewer abstractions, Rails is a good choice for getting your app off the ground and is more beginner-friendly (as we shall see later on in this article). With Rails, you can build very robust monolith apps, but your code will get more and more complex as you scale. On the other hand, Hanami&amp;#39;s more complex structure can be daunting to learn, but it will allow you to build massively scalable applications and could make for much better code organization.&lt;/p&gt;
&lt;p&gt;Next, let&amp;#39;s take a look at each framework&amp;#39;s ecosystem.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Ecosystem and Community&lt;/h2&gt;
&lt;p&gt;As we mentioned earlier, Rails has a more established framework than Hanami. Rails has been around for longer and, as such, has a bigger and more mature community.&lt;/p&gt;
&lt;p&gt;The differences in ecosystem and community are summarized below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt; - It doesn&amp;#39;t matter what you&amp;#39;re trying to build, if you use Rails, you have access to very well-done documentation. You are guided through all parts of an app build, from authentication, to data persistence, view presentation, and everything in between. Additionally, a wide range of third-party tutorials cover almost everything imaginable in terms of building an application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, things are not so established on the Hanami side of things. Being a relatively newer framework, Hanami has a comparatively limited documentation base. The Hanami team has done an impressive job with the official guides, but compared to the Rails documentation base, it doesn&amp;#39;t come close.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Community&lt;/strong&gt; - Here, as with the documentation, Rails is the clear winner. Rails has been around much longer than Hanami, so it&amp;#39;s been adopted the most. It doesn&amp;#39;t matter whether you are a beginner or advanced developer, you&amp;#39;ll find a Rails community on relevant subreddits, Slack groups, Discords, and so forth. On the other hand, Hanami is still growing, meaning it has a much smaller community.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gems and libraries&lt;/strong&gt; - Since both Hanami and Rails are Ruby frameworks, it could be argued that gems that work in Rails will work in Hanami. Although this is technically true, you&amp;#39;ll often find that Rails has a more established base of gems and libraries for all sorts of specialized functions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, because of Hanami&amp;#39;s focus on abstractions and specialization, it has some very advanced gems that could take your Rails apps to the next level. For example, the default &lt;a href=&quot;https://dry-rb.org/&quot;&gt;dry-rb gems&lt;/a&gt; in Hanami could bring better code organization and abstraction when used in Rails apps.&lt;/p&gt;
&lt;p&gt;Moving on, let&amp;#39;s compare each framework&amp;#39;s learning curve and adoption.&lt;/p&gt;
&lt;h2&gt;Ease of Use, Adoption, Governance, and Learning Curve&lt;/h2&gt;
&lt;p&gt;For the very reasons outlined in the previous section, Ruby on Rails easily beats Hanami in terms of ease of use, industry adoption, and learning curve, specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Learning&lt;/strong&gt; - Imagine you&amp;#39;re a complete beginner who&amp;#39;s looking to learn a new programming language. Your very first thought will be to check out online learning resources. If a framework has widely available learning materials covering the app-building process from start to finish, you&amp;#39;ll likely choose that language over others. And because Rails has a more established documentation base, it trumps Hanami as the beginner&amp;#39;s choice of framework. In addition, Rails is much more friendly for beginners to pick up since it makes so many assumptions under the hood (compared to Hanami, which offers a more abstract way of building apps). Hanami&amp;#39;s abstractions can be daunting even for established Rails developers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Industry adoption&lt;/strong&gt; - Without including the adoption of other popular and more established frameworks like Python, React, C#, and others, if we consider the adoption of Ruby frameworks, Rails easily eclipses Hanami. The &lt;a href=&quot;https://rubyonrails.org/&quot;&gt;Rails homepage&lt;/a&gt; lists some big-name organizations using the framework. On the other hand, as the new kid on the block, Hanami is not so widely adopted. We&amp;#39;ll have to wait and see whether that will change in the future.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Job market prospects&lt;/strong&gt; - In terms of job prospects, you&amp;#39;ll find more job openings for Rails developers than Hanami developers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Governance&lt;/strong&gt; - Another important aspect that should not be overlooked is governance. A changing landscape guides how open-source frameworks evolve and advance. However, more than that, the core team members of these frameworks often wield immense powers and can dictate what goes into a framework, how it develops, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A good example is &lt;a href=&quot;https://twitter.com/dhh/status/1743664413964374505&quot;&gt;January&amp;#39;s announcement&lt;/a&gt; by David Heinemeier Hansson (DHH), the creator of Rails. He said that, in the future, he would push for Rails to offer first-class support for building full-stack progressive web applications and native notifications. These features would make Rails very attractive to mobile developers.&lt;/p&gt;
&lt;p&gt;In reaction, many developers, some outside the Ruby ecosystem and even others who had dropped Rails over the years, overwhelmingly responded in the positive, saying they would gladly pick up the framework or learn it if DHH kept his word. This example just goes to show you that governance is a very important consideration when deciding what framework to pick.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s now switch gears and look into something more technical, application performance.&lt;/p&gt;
&lt;h2&gt;Performance: Ruby on Rails vs. Hanami&lt;/h2&gt;
&lt;p&gt;As a developer, you will be concerned about your app&amp;#39;s reliability, responsiveness, and how it will utilize server resources once deployed in production. These are fundamental concerns that can greatly affect your choice of framework.&lt;/p&gt;
&lt;p&gt;You could use many methods to run performance tests on your app, one of the most popular being &lt;a href=&quot;https://jmeter.apache.org/&quot;&gt;Apache JMeter&lt;/a&gt;. Let&amp;#39;s use the &lt;a href=&quot;https://web-frameworks-benchmark.netlify.app/result?asc=0&amp;f=hanami,rails&amp;order_by=level64&quot;&gt;benchmark numbers&lt;/a&gt; to compare Hanami and Rails.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s a quick benchmark test showing the number of requests each framework can handle per second:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-04/req-per-second.png&quot; alt=&quot;Request per second Rails vs Hanami&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Hanami beats Rails hands down, with the ability to handle 3 times more requests than Rails can handle.&lt;/p&gt;
&lt;p&gt;This next screenshot shows the average latency values for each framework:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-04/average-latency.png&quot; alt=&quot;Average latency comparison Rails vs Hanami&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Again, Hanami beats Rails with an average latency time that&amp;#39;s about 3 times shorter.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re looking for a Ruby framework that is blazingly fast (let&amp;#39;s say, you need to work on a fast and hugely scalable API), then you&amp;#39;ll be hard-pressed to find anything better than Hanami in the Ruby landscape.&lt;/p&gt;
&lt;h2&gt;Testing&lt;/h2&gt;
&lt;p&gt;When it comes to testing code, both frameworks are very much comparable since you can test either using the versatile &lt;a href=&quot;https://rspec.info/&quot;&gt;RSpec&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;RSpec is included via the &lt;code&gt;hanami-rspec&lt;/code&gt; gem for Hanami apps, whereas you need to install the &lt;code&gt;rspec-rails&lt;/code&gt; gem to use it in your Rails app. The example below shows a very basic test spec that is included with your new Hanami app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# spec/requests/root_spec.rb

RSpec.describe &amp;quot;Root&amp;quot;, type: :request do
  it &amp;quot;is successful&amp;quot; do
    get &amp;quot;/&amp;quot;

    expect(last_response).to be_successful
    expect(last_response.body).to eq(&amp;quot;Hello from Hanami&amp;quot;)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running it with &lt;code&gt;$ bundle exec rspec spec/requests/root_spec.rb&lt;/code&gt; should result in a passing test (assuming you&amp;#39;ve not edited the default view template):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;.

Finished in 0.01812 seconds (files took 0.47734 seconds to load)
1 example, 0 failures
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the Rails side, you need to handle some things by yourself. Firstly, you need to add the RSpec gem to the development/test block in the &lt;code&gt;Gemfile&lt;/code&gt; as shown below, then run &lt;code&gt;bundle&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile
...
group :development, :test do
    gem &amp;#39;rspec-rails&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step is to run the RSpec install with the command &lt;code&gt;bundle exec rails generate rspec:install&lt;/code&gt; to get it ready for your Rails project.&lt;/p&gt;
&lt;p&gt;Finally, before writing and running any tests, you&amp;#39;ll likely need to install another gem to define test data for your Rails app: &lt;a href=&quot;https://github.com/thoughtbot/factory_bot_rails&quot;&gt;FactoryBot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And assuming you&amp;#39;ve set up your test suite properly and defined a few tests, you can run a test just as you would in Hanami, with &lt;code&gt;bundle exec rspec spec/models/user_spec.rb&lt;/code&gt;. You can get your test results just as you would with a Hanami app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;Finished in 0.04421 seconds (files took 0.88106 seconds to load)
1 example, 0 failures
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let&amp;#39;s see how the frameworks compare when it comes to deployment.&lt;/p&gt;
&lt;h2&gt;Deployment&lt;/h2&gt;
&lt;p&gt;Nowadays, there are lots of options for deploying Ruby apps to production.&lt;/p&gt;
&lt;p&gt;To begin with, you could go with a Platform-as-a-Service (PaaS) provider like &lt;a href=&quot;https://www.heroku.com/ruby&quot;&gt;Heroku&lt;/a&gt;, or &lt;a href=&quot;https://fly.io/&quot;&gt;Fly&lt;/a&gt; for a more seamless experience. You can also do a bit of DevOps: set up a Docker installation on a VPS and deploy your app there.&lt;/p&gt;
&lt;p&gt;Whichever option you choose, you can expect deployment to be relatively similar in both cases. The only caveat is the lack of extensive documentation and tutorials for deploying Hanami apps.&lt;/p&gt;
&lt;p&gt;As we pointed out before, being a newer framework, Hanami still doesn&amp;#39;t have extensive documentation covering the deployment process and the challenges that might crop up. If you don&amp;#39;t mind hacking around this, then you should be okay.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, we&amp;#39;ve looked at two Ruby frameworks — Ruby on Rails and Hanami — comparing them in terms of features, architecture, performance, and more. Ultimately, we have to answer the question: &amp;quot;Which framework should I use, and why?&amp;quot;. We can summarize as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you&amp;#39;re a beginner Ruby developer, start with Rails: it&amp;#39;s easier to pick up and learn. If you&amp;#39;re a more advanced Ruby developer, develop a Hanami skillset, as it will help you develop even more robust Ruby applications.&lt;/li&gt;
&lt;li&gt;If you need to develop an app that should be fast, say an API serving several clients, Hanami&amp;#39;s small memory footprint, blazing fast responses, and low latency will serve you well. However, if you just need to develop a monolithic full-stack app to validate a SaaS idea, Rails will definitely get you there faster.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ultimately, what you end up choosing is really up to you. It isn&amp;#39;t a bad idea to acquire skills in both frameworks. That way, you can decide on the best framework to go for, depending on the needs of your project.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.P.S. Did you know AppSignal has an integration for &lt;a href=&quot;https://www.appsignal.com/ruby/rails-monitoring&quot;&gt;Rails&lt;/a&gt; and &lt;a href=&quot;https://www.appsignal.com/ruby/hanami-monitoring&quot;&gt;Hanami&lt;/a&gt;?&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Handling Exceptions in Grape for Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/04/17/handling-exceptions-in-grape-for-ruby.html"/>
    <id>https://blog.appsignal.com/2024/04/17/handling-exceptions-in-grape-for-ruby.html</id>
    <published>2024-04-17T00:00:00+00:00</published>
    <updated>2024-04-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Grape is a popular Ruby framework for building RESTful APIs. Let&#039;s explore the basics of Grape exception handling, including customizing exceptions.</summary>
    <content type="html">&lt;p&gt;Grape is a popular Ruby framework for building RESTful APIs. Exception handling plays a crucial role in ensuring the stability and reliability of any application, including those made with Grape.&lt;/p&gt;
&lt;p&gt;This article will explore the basics of Grape exception handling, including customizing exceptions. We&amp;#39;ll also touch on some best practices, and how to integrate your app with AppSignal for enhanced error monitoring and management.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Basics of Grape Exception Handling&lt;/h2&gt;
&lt;p&gt;In this tutorial, we’ll see how to handle exceptions in a Grape API built in Rails. I have made a demo job board API for this, and &lt;a href=&quot;https://github.com/kinsomicrote/appsignal-grape-api&quot;&gt;you can check out the source code on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Raising an Exception&lt;/h3&gt;
&lt;p&gt;You can raise an exception in Grape by using &lt;code&gt;error!&lt;/code&gt;. For example, in the job API mentioned above, we have a &lt;code&gt;show&lt;/code&gt; route that returns a job based on the &lt;code&gt;ID&lt;/code&gt;. We can return a &lt;code&gt;404&lt;/code&gt; error when the record is not available, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;get do
  if job
    present job
  else
    error!(&amp;#39;404 Not Found&amp;#39;, 404)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you raise an exception, you’ll want to handle it in a “unique” way — you most likely will not want to send the raised exception to your users.&lt;/p&gt;
&lt;p&gt;In Ruby, we have a default mechanism for &lt;a href=&quot;https://stackify.com/rescue-exceptions-ruby/&quot;&gt;exception handling&lt;/a&gt;. It works by wrapping code that might raise an exception in a &lt;code&gt;begin&lt;/code&gt; block. The &lt;code&gt;rescue&lt;/code&gt; block is used to handle the exception that has been raised.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
    #... process, may raise an exception
rescue =&amp;gt;
    #... error handler
else
    #... executes when no error
ensure
    #... always executed
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, here is what that will look like in a typical scenario:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
    File.readlines(&amp;#39;input.txt&amp;#39;).each { |line| values &amp;lt;&amp;lt; Float(line) }
rescue Errno::ENOENT
    p &amp;#39;file not found&amp;#39;
rescue ArgumentError
    p &amp;#39;file contains unparsable numbers&amp;#39;
else
    print values
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The &lt;code&gt;rescue_from&lt;/code&gt; Method&lt;/h3&gt;
&lt;p&gt;When you raise exceptions, or they happen without your direct involvement, you’ll want to handle them properly.
By default, Grape provides a &lt;code&gt;rescue_from&lt;/code&gt; method. This allows you to specify a block of code that gets executed when defined exceptions are raised.&lt;/p&gt;
&lt;p&gt;So, to “rescue” or handle the &lt;code&gt;404&lt;/code&gt; error we raised before any other one that arises in the &lt;code&gt;jobs&lt;/code&gt; resource, we can use the &lt;code&gt;rescue_from&lt;/code&gt; method. The method is added above the &lt;code&gt;jobs&lt;/code&gt; resource.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Rescue 404 errors
rescue_from :all do |error|
  error!({ error: error.message }, 404)
end

# Jobs resource
resource :jobs do
  desc &amp;#39;Return list of jobs&amp;#39;
  get do
    ...
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also specify the content type to be used:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;rescue_from :all do |error|
  error!({ error: error.message }, 404, { &amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;text/error&amp;#39; })
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way of handling an exception is too generic — we are rescuing every form of exception and returning an error with a &lt;code&gt;404&lt;/code&gt; status code. That is misleading if our API users expect to get a &lt;code&gt;400&lt;/code&gt; status code.&lt;/p&gt;
&lt;p&gt;We can instead specify the exception we want to handle:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;rescue_from ActiveRecord::RecordNotFound do |error|
  error!({ error: error.message }, 404, { &amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;text/error&amp;#39; })
end

rescue_from :all do |error|
  error!({ error: error.message }, 500, { &amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;text/error&amp;#39; })
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we encounter an &lt;code&gt;ActiveRecord::RecordNotFound&lt;/code&gt; error, we’ll return an error message with a &lt;code&gt;404&lt;/code&gt; status code. Otherwise, we’ll return an error message with a &lt;code&gt;500&lt;/code&gt; status code.&lt;/p&gt;
&lt;p&gt;This shows that we can improve on what we currently have, but what if we want an error handler that rescues from &lt;strong&gt;all&lt;/strong&gt; errors? That&amp;#39;s where customizing exceptions comes in.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Customizing Exceptions in Grape for Ruby&lt;/h2&gt;
&lt;p&gt;Depending on the type of error encountered, this error handler should be able to return an error message alongside the correct status code.&lt;/p&gt;
&lt;p&gt;First, create a file called &lt;strong&gt;exceptions_handler&lt;/strong&gt;. Then, we’ll move our current exception handlers into the file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# frozen_string_literal: true

module V1
  module ExceptionsHandler
    extend ActiveSupport::Concern

    included do
      rescue_from ActiveRecord::RecordNotFound do |error|
        error!({ error: error.message }, 404, { &amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;text/error&amp;#39; })
      end

      rescue_from :all do |error|
        error!({ error: error.message }, 500, { &amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;text/error&amp;#39; })
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &lt;code&gt;ExceptionHandler&lt;/code&gt; module uses &lt;code&gt;ActiveSupport::Concern&lt;/code&gt; , allowing us to access functionalities like &lt;code&gt;included&lt;/code&gt; and &lt;code&gt;class_methods&lt;/code&gt;. In the snippet above, we have the error handlers in the &lt;code&gt;included&lt;/code&gt; block, so wherever this module is included, they will be available as they’re defined.&lt;/p&gt;
&lt;p&gt;We can go ahead and remove the error handler from the files where we had them previously. Then we can include the &lt;strong&gt;ExceptionsHandler&lt;/strong&gt; module in our API entry file — &lt;strong&gt;api.rb&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# frozen_string_literal: true

module V1
  class API &amp;lt; Grape::API
    include ExceptionsHandler

    mount V1::Jobs
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s create a base error class for our errors. This class will be responsible for returning the error response.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module V1
  module Exceptions
    class BaseError &amp;lt; StandardError
      attr_reader :status, :message
      def initialize(message: nil, status: nil)
        @status = status || 500
        @message = message || &amp;quot;Something unexpected happened.&amp;quot;
      end

      def body
        Rack::Response.new({ error: message }.to_json, status)
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The class accepts two keyword parameters: a &lt;code&gt;message&lt;/code&gt; string and a &lt;code&gt;status&lt;/code&gt;. If none is passed, we’ll use the default.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;body&lt;/code&gt; method, we return a Rack response. By default, the &lt;code&gt;rescue_from&lt;/code&gt; handler must return a &lt;code&gt;Rack::Response&lt;/code&gt; object, call &lt;code&gt;error!&lt;/code&gt;, or raise an exception.&lt;/p&gt;
&lt;p&gt;We can go ahead and make use of it in the &lt;code&gt;ExceptionsHandler&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;included do
  rescue_from ActiveRecord::RecordNotFound do |error|
    error!({ error: error.message }, 404, { &amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;text/error&amp;#39; })
  end

  rescue_from :all do |error|
    Exceptions::BaseError.new(message: error.message).body
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we call the &lt;code&gt;/error&lt;/code&gt; endpoint, we’ll see &lt;code&gt;oops&lt;/code&gt; returned as the response. At this point, we can create a class for &lt;code&gt;NotFound&lt;/code&gt; errors.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module V1
  module Exceptions
    class NotFound &amp;lt; BaseError
      def initialize(message: nil)
        super(
          status: 404,
          message: message || &amp;quot;Oops, we could not find the record you are looking for.&amp;quot;
        )
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;NotFound&lt;/code&gt; class only accepts &lt;code&gt;message&lt;/code&gt;. Since it inherits from &lt;code&gt;BaseError&lt;/code&gt;, we need not return a &lt;code&gt;Rack::Response&lt;/code&gt; again.
We can go ahead and use it in the &lt;code&gt;ExceptionsHandler&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;included do
  rescue_from ActiveRecord::RecordNotFound do |error|
    Exceptions::NotFound.new(message: error).body
  end

  rescue_from :all do |error|
    Exceptions::BaseError.new(message: error.message).body
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if we attempt to raise an error like this manually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;raise Exceptions::NotFound.new(message: &amp;quot;Something unexpected happened.......&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will work fine, but the status code will be &lt;code&gt;500&lt;/code&gt;, because it returns the response in the &lt;code&gt;BaseError&lt;/code&gt; class (as the &lt;code&gt;BaseError&lt;/code&gt; class handles the error).
To fix that, we’ll need to modify the &lt;code&gt;ExceptionHandler&lt;/code&gt; to explicitly use the &lt;code&gt;NotFound&lt;/code&gt; class to handle the error instead.&lt;/p&gt;
&lt;p&gt;So, whenever an error corresponding to &lt;code&gt;ActiveRecord::RecordNotFound&lt;/code&gt; and &lt;code&gt;V1::Exceptions::NotFound&lt;/code&gt; is encountered, use &lt;code&gt;Exceptions::NotFound&lt;/code&gt;. Otherwise, use &lt;code&gt;Exceptions::BaseError&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;included do
  rescue_from ActiveRecord::RecordNotFound do |error|
    Exceptions::NotFound.new(message: error).body
  end

  rescue_from V1::Exceptions::NotFound do |error|
    Exceptions::NotFound.new(message: error.message).body
  end

  rescue_from :all do |error|
    Exceptions::BaseError.new(message: error.message).body
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that we’ll need specific￼&lt;code&gt;rescue_from&lt;/code&gt;￼blocks as we create more error classes. We can improve this by using case statements:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module V1
  module ExceptionsHandler
    extend ActiveSupport::Concern

    included do
      rescue_from :all do |error|
        case error.class.name
        when &amp;#39;ActiveRecord::RecordNotFound&amp;#39;, &amp;#39;V1::Exceptions::NotFound&amp;#39;
          Exceptions::NotFound.new(message: error.message).body
        else
          Exceptions::BaseError.new(message: error.message).body
        end
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà!&lt;/p&gt;
&lt;h2&gt;Best Practices and Tips&lt;/h2&gt;
&lt;p&gt;While there are tons of best practices that you can employ for exception handling, here are a few quick tips to follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Group related exceptions: As we saw in the code above, grouping related exceptions allow us to have maintainable code. As the number of exceptions we want to handle increases, we can add them to our list.&lt;/li&gt;
&lt;li&gt;Use helpers like &lt;code&gt;error!&lt;/code&gt; to quickly raise exceptions. This simplifies your exception handling.&lt;/li&gt;
&lt;li&gt;Make use of exception monitoring tools like AppSignal.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;AppSignal Integration: Grape for Ruby&lt;/h2&gt;
&lt;p&gt;AppSignal helps you to monitor and track errors in your applications. Integrating AppSignal with your Grape API gives you valuable insights into exceptions.
&lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/grape.html&quot;&gt;This guide shows you how to integrate AppSignal with your Grape API&lt;/a&gt;.
Whenever an error occurs in your API, you’ll see it in your AppSignal dashboard, like so:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2024-04/dashboard-grape.png&quot; alt=&quot;AppSignal Dashboard&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Exception handling is a critical aspect of developing robust APIs. In this tutorial, we’ve seen how to properly handle exceptions in a Grape API. We also briefly looked at some best practices and AppSignal&amp;#39;s integration for Grape.&lt;/p&gt;
&lt;p&gt;Exception handling is an ongoing process — one you’ll need to improve on consistently.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.P.S. Did you know that AppSignal offers an Active Record integration? &lt;a href=&quot;https://www.appsignal.com/ruby/active-record-monitoring&quot;&gt;Find out more&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Good Database Migration Practices for Your Ruby on Rails App using Strong Migrations</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/03/20/good-database-migration-practices-for-your-ruby-on-rails-app-using-strong-migrations.html"/>
    <id>https://blog.appsignal.com/2024/03/20/good-database-migration-practices-for-your-ruby-on-rails-app-using-strong-migrations.html</id>
    <published>2024-03-20T00:00:00+00:00</published>
    <updated>2024-03-20T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore Strong Migrations, a gem that can help you maintain good practices with your database migrations in Ruby on Rails.</summary>
    <content type="html">&lt;p&gt;One great feature that comes with modern web frameworks is the ability to manage database schema migrations. However, schema migrations are not 100% safe and remain a recurring cause of issues within projects I have encountered over the last 15 years.&lt;/p&gt;
&lt;p&gt;This article will review the issues surrounding poorly managed schema migrations and then look into Strong Migrations, a gem that can help you avoid most problems. Finally, we will discuss a few good practices around database management.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Issues with Schema Migrations in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Schema migrations are changes to a table or database schema within an RDBMS: adding, renaming, removing, and updating a table (or a column within another table), index, or view. Some schema migrations are inherently risky (such as removing a column or table). Generally, all carry a risk, especially alongside any related code changes.&lt;/p&gt;
&lt;p&gt;Schema migrations are also made worse with larger tables. Some changes, like updating columns and altering indexes, take longer to apply and might imply database locks, causing performance issues.&lt;/p&gt;
&lt;p&gt;We also have to talk about how migrations are brought to production via related code changes. Migrations that add, update, or remove a schema element (table or column) and are used in the code are the most problematic. This is because many developers don&amp;#39;t build good practices into those cases early enough. The typical scenario is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A developer adds a Model and related table.&lt;/li&gt;
&lt;li&gt;They merge the code change.&lt;/li&gt;
&lt;li&gt;The code change gets deployed, but the migration hasn&amp;#39;t run yet.&lt;/li&gt;
&lt;li&gt;The application starts and crashes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The reason for the crash is simple: as an application starts, Ruby on Rails will load the schema but won&amp;#39;t find the new table in the database.&lt;/p&gt;
&lt;h2&gt;How Can We Fix Problems with Schema Migrations?&lt;/h2&gt;
&lt;p&gt;Looking at the above example, the solution is to run a database migration before an application starts. The deployment tooling should take care of that. However, in the case of a column or table removal, a migration must run after an application starts. So the easiest thing is to separate the change into two deployments in the correct order and make sure the tooling or team runs the migration when appropriate. Sometimes, we may have to split the change into more deployments.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take the cases we listed before: adding, removing, or updating a column.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the first case of adding a column, we need to deploy and run a migration before the code change that will start to use it.&lt;/li&gt;
&lt;li&gt;In the second case (removing a column), it&amp;#39;s the opposite: we need to remove code that&amp;#39;s using the column before removing the column itself.&lt;/li&gt;
&lt;li&gt;Updating a column is trickier. Such a change will enforce a lock. And it&amp;#39;s even worse if you change the column type. This is the suitable strategy:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Create a new column.&lt;/li&gt;
&lt;li&gt;Write to both old and new columns.&lt;/li&gt;
&lt;li&gt;Backfill existing data from the old column into the new one.&lt;/li&gt;
&lt;li&gt;Move the reads to the new column.&lt;/li&gt;
&lt;li&gt;Stop writing to the old column.&lt;/li&gt;
&lt;li&gt;Drop the old column.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These three strategies should be used for any project, especially once a database fills up and users rely on the product.&lt;/p&gt;
&lt;p&gt;Yet, just like linters remove some cognitive load by taking care of this kind of toil, it&amp;#39;s best to rely on a tool to automate this.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s where the &lt;a href=&quot;https://github.com/ankane/strong_migrations&quot;&gt;Strong Migrations&lt;/a&gt; library comes into play. It adds a layer of protection around migrations to ensure you can avoid issues upon deployment in any environment.&lt;/p&gt;
&lt;h2&gt;Getting Help from Strong Migrations&lt;/h2&gt;
&lt;p&gt;The folks at Instacart have open-sourced many &lt;a href=&quot;https://www.instacart.com/opensource&quot;&gt;libraries&lt;/a&gt;. Among them sits Strong Migrations, aimed at &amp;quot;catching unsafe migrations in development&amp;quot;. Once installed, it will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect potentially dangerous operations.&lt;/li&gt;
&lt;li&gt;Prevent them from running by default, and tell you why.&lt;/li&gt;
&lt;li&gt;Provide instructions on safer ways to do things.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A migration will be deemed dangerous if it&amp;#39;s likely to cause the issues we discussed earlier: locks and potential errors due to timing. Check out the &lt;a href=&quot;https://github.com/ankane/strong_migrations/blob/master/README.md&quot;&gt;Strong Migrations README on GitHub&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;We will now cover a few cases here.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;This is straightforward: use &lt;code&gt;bundle add strong_migrations&lt;/code&gt;, followed by &lt;code&gt;bundle install&lt;/code&gt;, and &lt;code&gt;rails generate strong_migrations:install&lt;/code&gt;. The last command will create an initializer to configure a few things when the application starts.&lt;/p&gt;
&lt;h3&gt;Adding a Column or a Table&lt;/h3&gt;
&lt;p&gt;There is no help from Strong Migrations on this one, but remember it&amp;#39;s good practice to separate a table addition from the code using it. Please ensure a schema change happens before any code using it runs and expects to find it.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Renaming a Column&lt;/h3&gt;
&lt;p&gt;Here&amp;#39;s where we start to see what Strong Migrations can do for us. Taking the example of a migration that renames a column from &amp;quot;address&amp;quot; to &amp;quot;location&amp;quot;, we get the following message when we try to run it locally in our development environment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;== 20231116202001 RenameUserAddress: migrating ================================
rails aborted!
StandardError: An error has occurred; this and all later migrations have been canceled:

=== Dangerous operation detected #strong_migrations ===

Renaming a column that&amp;#39;s in use will cause errors
in your application. A safer approach is to:

1. Create a new column
2. Write to both columns
3. Backfill data from the old column to new column
4. Move reads from the old column to the new column
5. Stop writing to the old column
6. Drop the old column

article/db/migrate/20231116202001_rename_user_address.rb:3:in `change&amp;#39;
Tasks: TOP =&amp;gt; db:migrate
(See full trace by running task with --trace)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, yes, the Strong Migrations gem does not replace you. Instead, it reminds you that what you are doing is dangerous and that you should not do it. It also tells you how you should do things instead.&lt;/p&gt;
&lt;h3&gt;Removing a Column&lt;/h3&gt;
&lt;p&gt;Now, let&amp;#39;s say that we have gone through the first five steps of the list that Strong Migrations generated for us, and it&amp;#39;s time to remove the old column.&lt;/p&gt;
&lt;p&gt;Is it as simple as creating a migration and using the &lt;code&gt;remove_column&lt;/code&gt; method? No, it&amp;#39;s not.&lt;/p&gt;
&lt;p&gt;Again, Strong Migrations will print the following text:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;== 20231116202636 RemoveUserAddress: migrating ================================
rails aborted!
StandardError: An error has occurred; this and all later migrations have been canceled:

=== Dangerous operation detected #strong_migrations ===

Active Record caches attributes, which causes problems
when removing columns. Be sure to ignore the column:

class User &amp;lt; ApplicationRecord
  self.ignored_columns = [&amp;quot;address&amp;quot;]
end

Deploy the code, then wrap this step in a safety_assured { ... } block.

class RemoveUserAddress &amp;lt; ActiveRecord::Migration[7.0]
  def change
    safety_assured { remove_column :users, :address, :string }
  end
end

article/db/migrate/20231116202636_remove_user_address.rb:3:in `change&amp;#39;
Tasks: TOP =&amp;gt; db:migrate
(See full trace by running task with --trace)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we know what to add to the model and how to change our schema migration. It&amp;#39;s tailored to your use case, including the table&amp;#39;s name and column. Once that is done, the migration will go through without an issue.&lt;/p&gt;
&lt;h3&gt;Backfilling Data&lt;/h3&gt;
&lt;p&gt;The remaining piece on the database strategy side is backfilling data in new columns from old ones. While relying on the &lt;a href=&quot;https://github.com/ilyakatz/data-migrate&quot;&gt;Data Migrate gem&lt;/a&gt; might be tempting, we can also backfill data in a regular migration. A safe backfilling migration will consider a table&amp;#39;s size, the task&amp;#39;s complexity, and avoid transactions.&lt;/p&gt;
&lt;p&gt;By default, Ruby on Rails uses a transaction around each migration. If we include backfilling in the migration doing the schema change, this might take some time and potentially cancel our whole schema migration.&lt;/p&gt;
&lt;p&gt;So, instead, let&amp;#39;s use a separate migration, avoid using a transaction, batch the process, and throttle the work.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class BackfillCountryCodeColumn &amp;lt; ActiveRecord::Migration[7.1]
  disable_ddl_transaction!

  def up
    User.unscoped.in_batches do |relation|
      relation.update_all country_code: &amp;#39;fr&amp;#39;
      sleep(0.01)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;unscoped&lt;/code&gt; ensures that we don&amp;#39;t rely on the &amp;quot;default scope&amp;quot; defined in the model to work with all rows.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;in_batches&lt;/code&gt; forces the block to run in batches of 1000 entries.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sleep&lt;/code&gt; gives some reprieve to the database in between batches.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;An alternative method is to use a script or a rake task to handle this. Whatever solution you pick, remember that backfilling a column or table with data might be pretty intensive for the database. Estimate how much data will be handled and consider the techniques used in the above example, if needed.&lt;/p&gt;
&lt;h2&gt;Adding Strong Migrations to an Existing Ruby on Rails Project&lt;/h2&gt;
&lt;p&gt;Adding Strong Migrations to an existing project is a good idea. However, you might run into issues when setting up a new project instance on a workstation or within a new environment. A failsafe will kick in and prevent any prior migrations from running.&lt;/p&gt;
&lt;p&gt;So, what shall we do? You don&amp;#39;t have to go through all the migrations and alter them accordingly. Instead, you should follow the &lt;a href=&quot;https://guides.rubyonrails.org/active_record_migrations.html#old-migrations&quot;&gt;Ruby on Rails guide&lt;/a&gt; and clean up old migrations.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ve not seen many projects follow that advice, but it&amp;#39;s the &amp;quot;good thing&amp;quot; to do: &lt;code&gt;db/schema.rb&lt;/code&gt; or &lt;code&gt;db/structure.sql&lt;/code&gt; holds a snapshot of the current database schema and can be used to load it. Tasks such as &lt;code&gt;db:setup&lt;/code&gt; and &lt;code&gt;db:prepare&lt;/code&gt; (and ones based on either of them) use that snapshot to load schema in a database. Only pending migrations are run; old ones are barely a line in one table, a mere memory.&lt;/p&gt;
&lt;p&gt;You can remove old migration files if you need them: git (or Mercurial, or Svn) will keep a trace of them. Once a migration has been applied to all existing environments, it can be deleted as the &lt;code&gt;schema.rb&lt;/code&gt; (or &lt;code&gt;structure.sql&lt;/code&gt;) file will serve for new environments.&lt;/p&gt;
&lt;p&gt;Here are the steps you should follow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Before introducing Strong Migrations, ensure all environments are up to date in terms of migrations.&lt;/li&gt;
&lt;li&gt;Remove old migration files.&lt;/li&gt;
&lt;li&gt;Add and install the Strong Migrations gem.&lt;/li&gt;
&lt;li&gt;Start to create new migrations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Doing so will prevent you from going through years of migrations to set them in line with Strong Migrations. It will also lighten the burden of setting up a new environment.&lt;/p&gt;
&lt;p&gt;You can then devise a periodic removal of migration files, keeping the last 5, 10, or any from the previous month, for example.&lt;/p&gt;
&lt;h2&gt;Some Additional Advice on Migrations&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s finish up by sharing some general tips on migrations when it comes to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Production environments and databases&lt;/li&gt;
&lt;li&gt;Avoiding downtime&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Production Environments and Databases&lt;/h3&gt;
&lt;p&gt;Considering your production environment and its database is key when creating and running any migration. A developer environment is rarely bursting at the seams when it comes to data. Consequently, most migrations will run smoothly with barely any waiting time. Production environments have a lot more data that&amp;#39;s more diverse. This will tend to complicate things when it comes to writing and applying a migration.&lt;/p&gt;
&lt;p&gt;You should follow strategies like backfilling. Batching and throttling are also good practices when handling large volumes of data. It&amp;#39;s never too early to start using those to develop good habits. Once a migration is out there and running, we should monitor performance metrics: response time and slow queries. Compare these metrics with previous trends, and look into unexpected changes.&lt;/p&gt;
&lt;h3&gt;Avoiding Downtime&lt;/h3&gt;
&lt;p&gt;One reason that developers tend to be scared about making deployments around the end of the day or the week is the fear of the unknown.&lt;/p&gt;
&lt;p&gt;By following the good practices pointed out by Strong Migrations, we can reduce the amount of risks and unknowns, thus giving us more confidence in our ability to deploy at any time.&lt;/p&gt;
&lt;h3&gt;Backups&lt;/h3&gt;
&lt;p&gt;If things go wrong, don&amp;#39;t panic; you should have a backup ready. Most managed solutions available for PostgreSQL, MySQL, and similar RDBMS offer backups at regular intervals. Some also provide point-in-time recovery, allowing us to rewind to better times.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we first explored some potential issues related to schema migrations in Rails, before seeing how we can fix these issues ourselves. We then introduced Strong Migrations. As we have seen, Strong Migrations ensures that risky changes such as removals and changes to a schema element are done correctly to avoid risk.&lt;/p&gt;
&lt;p&gt;Finally, we touched on a few additional tips regarding migrations.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Turbo Streaming Modals in Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/03/13/turbo-streaming-modals-in-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2024/03/13/turbo-streaming-modals-in-ruby-on-rails.html</id>
    <published>2024-03-13T00:00:00+00:00</published>
    <updated>2024-03-13T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the second and final part of our series, let&#039;s turn to another method used to make modals accessible in Rails: Turbo Streams.</summary>
    <content type="html">&lt;p&gt;In part one of this series, we used Hotwire&amp;#39;s Stimulus and Turbo Frames to present modals in Rails.&lt;/p&gt;
&lt;p&gt;Now, we&amp;#39;ll dive into another method we can use to present modals: Turbo Streams.&lt;/p&gt;
&lt;h2&gt;What Are Turbo Streams in Ruby on Rails?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://turbo.hotwired.dev/handbook/streams&quot;&gt;Turbo Streams&lt;/a&gt; is a subset of Turbo. It allows us to make fine-grained, targeted updates to a page. By default, it contains seven CRUD actions, but we&amp;#39;re free to add more actions within our applications.&lt;/p&gt;
&lt;p&gt;Now, we&amp;#39;ll create a &lt;code&gt;show_remote_modal&lt;/code&gt; action which renders and presents the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; from our previous post.&lt;/p&gt;
&lt;h3&gt;Creating a Custom Action&lt;/h3&gt;
&lt;p&gt;Create a folder to place all custom Stream Actions in:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ mkdir app/javascript/stream_actions
$ touch app/javascript/stream_actions/index.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a file for the Action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ touch app/javascript/stream_actions/show_remote_modal.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Import the Stream Actions into the application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/stream_actions/index.js

import &amp;quot;./show_remote_modal&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/application.js

// ...
import &amp;quot;stream_actions&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&amp;#39;re using import maps, you&amp;#39;ll need to update the config and restart the server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/importmap.rb

# ...
pin_all_from &amp;quot;app/javascript/stream_actions&amp;quot;, under: &amp;quot;stream_actions&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Change the global remote modal container to an HTML element instead of a Turbo Frame:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/layouts/application.html.erb %&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;%# ... %&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;%= yield %&amp;gt;

    &amp;lt;remote-modal-container&amp;gt;&amp;lt;/remote-modal-container&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The custom Stream Action can be implemented as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/stream_actions/show_remote_modal.js

Turbo.StreamActions.show_remote_modal = function () {
  const container = document.querySelector(&amp;quot;remote-modal-container&amp;quot;);
  container.replaceChildren(this.templateContent);
  container.querySelector(&amp;quot;dialog&amp;quot;).showModal();
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above snippet, &lt;code&gt;this&lt;/code&gt; refers to &lt;a href=&quot;https://github.com/hotwired/turbo/blob/main/src/elements/stream_element.js&quot;&gt;&lt;code&gt;StreamElement&lt;/code&gt;&lt;/a&gt;, which is the &lt;a href=&quot;https://javascript.info/custom-elements&quot;&gt;custom element&lt;/a&gt; underpinning &lt;code&gt;&amp;lt;turbo-stream&amp;gt;&lt;/code&gt;. The &lt;code&gt;templateContent&lt;/code&gt; getter is defined by this element.&lt;/p&gt;
&lt;h3&gt;Using the Action with a Rails Helper&lt;/h3&gt;
&lt;p&gt;Since this is a custom Action, we&amp;#39;ll need to manually create a Rails helper to use it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ bin/rails generate helper TurboStreamActions
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/helpers/turbo_stream_actions.rb

module TurboStreamActionsHelper
  def show_remote_modal(&amp;amp;block)
    turbo_stream_action_tag(
      :show_remote_modal,
      template: @view_context.capture(&amp;amp;block)
     )
  end
end

Turbo::Streams::TagBuilder.prepend(TurboStreamActionsHelper)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now use this helper in our views.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/support/tickets/new.html.erb %&amp;gt;

&amp;lt;%= turbo_stream.show_remote_modal do %&amp;gt;
  &amp;lt;dialog id=&amp;quot;contact_form_modal&amp;quot; aria-labelledby=&amp;quot;modal_title&amp;quot;&amp;gt;
    &amp;lt;header&amp;gt;
      &amp;lt;h2 id=&amp;quot;modal_title&amp;quot;&amp;gt;
        Contact us
      &amp;lt;/h2&amp;gt;
      &amp;lt;form method=&amp;quot;dialog&amp;quot;&amp;gt;
        &amp;lt;button aria-label=&amp;quot;close&amp;quot;&amp;gt;X&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/header&amp;gt;

    &amp;lt;%= form_with(url: support_tickets_path) do |form| %&amp;gt;
      &amp;lt;%= form.label :message, &amp;quot;Your message&amp;quot; %&amp;gt;
      &amp;lt;%= form.text_area :message, autofocus: true %&amp;gt;

      &amp;lt;%= form.button &amp;quot;Close&amp;quot;, value: nil, formmethod: :dialog %&amp;gt;
      &amp;lt;%= form.button &amp;quot;Send&amp;quot; %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/dialog&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember to remove the &lt;code&gt;data-controller&lt;/code&gt; attribute: we don&amp;#39;t need it anymore. In fact, we can delete the controller itself.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ rm app/javascript/controllers/remote_modal_controller.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ll also need to change the template&amp;#39;s name so it renders as a Turbo Stream.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ mv \
    app/views/support/tickets/new.html.erb \
    app/views/support/tickets/new.turbo_stream.erb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turbo Streams are disabled by default for &lt;code&gt;GET&lt;/code&gt; requests, so we&amp;#39;ll need to manually enable them for the link:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/support/show.html.erb %&amp;gt;

&amp;lt;%# ... %&amp;gt;

&amp;lt;%= link_to new_support_ticket_path, data: { turbo_stream: true } do %&amp;gt;
  Show contact form
&amp;lt;% end %&amp;gt;

&amp;lt;%# ... %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Refresh the page and click &lt;em&gt;Show contact form&lt;/em&gt;. It should still work as before, but now it&amp;#39;s rendered using a custom Stream Action!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this two-part series, we explored three different methods to present modals using Hotwire: Stimulus, Turbo Frames, and Turbo Streams. More importantly, the modals were presented with accessibility as the main consideration.&lt;/p&gt;
&lt;p&gt;The web should be usable by everyone and it&amp;#39;s important for us, as web developers, to put in the effort to make websites accessible.&lt;/p&gt;
&lt;p&gt;Basecamp&amp;#39;s &lt;a href=&quot;https://github.com/basecamp/accessibility&quot;&gt;accessibility guide&lt;/a&gt; is publicly available and a fantastic resource to learn the ropes.&lt;/p&gt;
&lt;p&gt;I also recommend checking out the docs for &lt;a href=&quot;https://stimulus.hotwired.dev&quot;&gt;Stimulus&lt;/a&gt; and &lt;a href=&quot;https://turbo.hotwired.dev&quot;&gt;Turbo&lt;/a&gt; to familiarise yourself with all their features and the APIs used in this series.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Active Record or Sequel: Which Best Fits The Needs of Your Ruby App?</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/03/06/active-record-or-sequel-which-best-fits-the-needs-of-your-ruby-app.html"/>
    <id>https://blog.appsignal.com/2024/03/06/active-record-or-sequel-which-best-fits-the-needs-of-your-ruby-app.html</id>
    <published>2024-03-06T00:00:00+00:00</published>
    <updated>2024-03-06T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s compare some Active Record features to its lesser-known but powerful cousin, Sequel.</summary>
    <content type="html">&lt;p&gt;When it comes to choosing an object-relational mapping (ORM) library for your Ruby application, Active Record is usually the favorite choice. It&amp;#39;s an easy-to-use ORM library that allows for lots of data wrangling without resorting to SQL. All the same, you might wonder: &amp;quot;Is Active Record the only Ruby ORM library I can use?&amp;quot;&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll compare some Active Record features to its lesser-known but powerful cousin, Sequel. There are too many points of comparison to cover everything (such as how each library handles &lt;em&gt;CRUD&lt;/em&gt; operations, table joins, associations, database replication and sharding, etc). Instead, we&amp;#39;ll scratch the surface of a few database operations — namely, filtering, database locking, and transactions — and show how each library handles them.&lt;/p&gt;
&lt;p&gt;By the end, you&amp;#39;ll have a better idea about how to use each library&amp;#39;s strengths to the fullest.&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s learn what object-relational mapping is all about.&lt;/p&gt;
&lt;h2&gt;What Is Object-Relational Mapping (ORM)?&lt;/h2&gt;
&lt;p&gt;Object-relational mapping is a way to pass data between an application and its data store, usually a relational database like SQLite, PostgreSQL, or MySQL. Without ORM techniques, you would be forced to write raw SQL queries for all operations to get data in or out of your Ruby app&amp;#39;s database. But with an ORM, you get access to class objects and methods, making it much easier to read/write data to your database without using raw SQL.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s introduce the two ORM libraries we&amp;#39;re looking at, starting with Active Record, and then moving on to Sequel.&lt;/p&gt;
&lt;h2&gt;Introducing Active Record and Sequel for Ruby&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s quickly examine what Active Record and Sequel can do.&lt;/p&gt;
&lt;h3&gt;Active Record&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rubydoc.info/gems/activerecord&quot;&gt;Active Record&lt;/a&gt; is the most well-known Ruby object-relational mapping library. It defines class objects that are directly mapped to tables in a database and provides convenient methods for manipulating data using these classes.&lt;/p&gt;
&lt;p&gt;The library was first described by the pioneering computer scientist Martin Fowler in his book &lt;em&gt;Patterns of Enterprise Architecture&lt;/em&gt;. It comes bundled with Rails, which could explain why it&amp;#39;s more popular than Sequel. That said, Active Record is suitable for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Object-oriented database operations&lt;/li&gt;
&lt;li&gt;Performing validations on models before persisting them in the database&lt;/li&gt;
&lt;li&gt;Representing models, their data, and relationships&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Sequel&lt;/h3&gt;
&lt;p&gt;On the other hand, &lt;a href=&quot;https://github.com/jeremyevans/sequel&quot;&gt;Sequel&lt;/a&gt; is the lesser-known of the two. It includes a powerful DSL for manipulating data within a variety of databases.&lt;/p&gt;
&lt;p&gt;Sequel can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Represent database records as Ruby objects&lt;/li&gt;
&lt;li&gt;Handle simple and even complex associations in a concise way&lt;/li&gt;
&lt;li&gt;Do database sharding, database replications, and more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One standout feature that makes Sequel so powerful is its &lt;a href=&quot;https://sequel.jeremyevans.net/rdoc/files/doc/dataset_basics_rdoc.html&quot;&gt;&lt;em&gt;dataset&lt;/em&gt;&lt;/a&gt;. A dataset is Sequel&amp;#39;s way of directly representing an SQL query and making it readily available for a developer to use.&lt;/p&gt;
&lt;p&gt;Of course, a lot more could be said of each of these ORM libraries, but we&amp;#39;ll leave it at that. Let&amp;#39;s dive straight into our first example to see how Active Record and Sequel deal with record filtering.&lt;/p&gt;
&lt;h2&gt;Filtering Records&lt;/h2&gt;
&lt;p&gt;Filtering data is the process of refining data results by applying specific filters. By using filters, you can fetch exactly what you want from a dataset. Let&amp;#39;s see how each ORM library achieves this, starting with Active Record.&lt;/p&gt;
&lt;h3&gt;Using Active Record&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s say you want to filter data to return all orders of products priced under $20. How would you achieve this using Active Record?&lt;/p&gt;
&lt;p&gt;You can apply &lt;em&gt;conditions&lt;/em&gt; to your Active Record query. Active Record has many methods that act as conditions for filtering data, including &lt;code&gt;where&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt;, and &lt;code&gt;where.not&lt;/code&gt;. While it&amp;#39;s not possible to get into all of the conditions in this article, we&amp;#39;ll sample a few and see how they stack up against Sequel&amp;#39;s filter methods.&lt;/p&gt;
&lt;p&gt;For Active Record, you&amp;#39;ll first need to set up the proper model associations like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# models/order.rb
class Order &amp;lt; ActiveRecord::Base
  belongs_to :product
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then the Product model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# models/product.rb

class Product &amp;lt; ActiveRecord::Base
  has_many :orders
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now for the Active Record query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;product_orders_under_20 = Order.joins(:product).where(&amp;#39;products.price &amp;lt; ?&amp;#39;, 20)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we&amp;#39;ve used a &lt;em&gt;joins&lt;/em&gt; to include results from the products table and then applied a &lt;em&gt;where&lt;/em&gt; condition to filter for the products that fit our desired criteria.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Using Sequel&lt;/h3&gt;
&lt;p&gt;Sequel&amp;#39;s use of SQL representations in the form of datasets makes filtering a breeze. Let&amp;#39;s consider how Sequel would handle the example we used for Active Record.&lt;/p&gt;
&lt;p&gt;Just like we did with Active Record, you need to define some models first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# models/product.rb

class Product &amp;lt; Sequel::Model
  one_to_many :orders
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Order&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# models/order.rb

class Order &amp;lt; Sequel::Model
  many_to_one :product
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this is how you&amp;#39;d run the query with Sequel:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;product_orders_under_20 = DB[:orders].join(:products, id: :product_id).where { price &amp;lt; 20 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next up, let&amp;#39;s look at database locks.&lt;/p&gt;
&lt;h2&gt;Database Locking&lt;/h2&gt;
&lt;p&gt;Imagine you have a content management system where a user with the editor role can edit other users&amp;#39; posts. Let&amp;#39;s say there are several editors in the organization and it just so happens that two editors end up working on the same post at the same time. Such a scenario raises several questions, such as which editor&amp;#39;s work is saved, assuming both commit changes at the same time.&lt;/p&gt;
&lt;p&gt;This challenge is solved through the use of database locks.&lt;/p&gt;
&lt;h3&gt;Using Active Record&lt;/h3&gt;
&lt;p&gt;Active Record allows for two types of locks: optimistic locks and pessimistic locks. In optimistic locking, you need to include a version column in the respective database table. This is so that a record is checked against a matching version to ensure that another user or process doesn&amp;#39;t modify it. If it is, an error is raised.&lt;/p&gt;
&lt;p&gt;The example below shows how optimistic locking happens:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;invoice1 = Invoice.find(1)
invoice2 = Invoice.find(1)

invoice1.total_amount = 100
invoice1.save # 100

invoice2.total_amount = 500
invoice2.save # raises ActiveRecord::StaleObjectError
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When it comes to pessimistic locks, Active Record can implement them at the row level if your database allows for it. There are several ways of achieving pessimistic locking with Active Record, but we won&amp;#39;t go into the details now. Instead, the example below should give you an idea of how this is done:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;Invoice.lock.find(1) # applies a pessimistic lock on the record so that an UPDATE is done
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using Sequel&lt;/h3&gt;
&lt;p&gt;On the other hand, Sequel implements optimistic locking through a &lt;a href=&quot;https://sequel.jeremyevans.net/plugins.html&quot;&gt;plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You first add the plugin to the model like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# invoice.rb

class Invoice &amp;lt; Sequel::Model
  plugin :optimistic_locking
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then test this with two concurrent queries:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;invoice1 = Invoice.find(1)
invoice2 = Invoice.find(1)

invoice1.update(status: &amp;#39;Sent&amp;#39;) # record will be updated

invoice2.update(status: &amp;#39;Void&amp;#39;) # raises Sequel::NoExistingObject error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For pessimistic locking, Sequel implements the lock when the records (or record) are fetched using the following methods: &lt;code&gt;lock_style&lt;/code&gt;, &lt;code&gt;lock&lt;/code&gt;, and &lt;code&gt;for_update&lt;/code&gt;. Again, we won&amp;#39;t provide details on this. You can read &lt;a href=&quot;https://sequel.jeremyevans.net/documentation.html&quot;&gt;the documentation&lt;/a&gt; or &lt;a href=&quot;https://sequel.jeremyevans.net/2010/03/09/pessimistic-locking.html&quot;&gt;Jeremy Evans&amp;#39; Pessimistic Locking blog post&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s an example using the &lt;code&gt;for_update&lt;/code&gt; method to implement a pessimistic lock:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;dataset = DB[:invoices]
invoice1 = dataset.where(id: 1).for_update # will apply a pessimistic lock on invoice1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&amp;#39;s take a look at database transactions.&lt;/p&gt;
&lt;h2&gt;Database Transactions&lt;/h2&gt;
&lt;p&gt;To understand the concept of database transactions, consider a peer-to-peer lending app where Person A sends some money to Person B who can then withdraw the money and use it. Here, the app needs to first withdraw money from Person A&amp;#39;s account and then send that amount over to Person B.&lt;/p&gt;
&lt;p&gt;In such a scenario, you wouldn&amp;#39;t want the app to send money to Person B if there&amp;#39;s an error with the withdrawal from Person A&amp;#39;s account. The ideal solution would be to wrap the two processes in a &amp;quot;database transaction&amp;quot;, so if one of the processes fails (usually the first one), then the database can be rolled back to its original state before any of the transactions happened.&lt;/p&gt;
&lt;h3&gt;Using Active Record&lt;/h3&gt;
&lt;p&gt;With Active Record, implementing these queries without the use of database transactions might look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;person_a = Person.find_by(name: &amp;#39;A&amp;#39;)
person_b = Person.find_by(name: &amp;#39;B&amp;#39;)

withdrawal = person_a.accounts.first.withdraw(250)
person_b.accounts.first.account_total # 100

person_b.find(B).accounts.first.deposit(withdrawal)
person_b.accounts.first.account_total # 450
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although this could work, you also need to account for something going wrong with the withdrawal and take steps to take care of it. Let&amp;#39;s now use a database transaction to improve this example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;a_account = Person.find_by(name: &amp;#39;A&amp;#39;).accounts.first
b_account = Person.find_by(name: &amp;#39;B&amp;#39;).accounts.first

Account.transaction do
  a_account.withdraw(250)
  b_account.deposit(250)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we use &lt;code&gt;ActiveRecord::Transactions&lt;/code&gt; to wrap the two queries in a transaction so that the database can be rolled back if something goes wrong.&lt;/p&gt;
&lt;p&gt;What about Sequel?&lt;/p&gt;
&lt;h3&gt;Using Sequel&lt;/h3&gt;
&lt;p&gt;Just like in Active Record, if you want to implement transactions in Sequel, you&amp;#39;ll need to be explicit (although there are situations where this is enabled by default).&lt;/p&gt;
&lt;p&gt;With the same example we used for Active Record, this is how to wrap our queries using a transaction in Sequel:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;DB.transaction do
  Person.first(name: &amp;quot;A&amp;quot;).accounts.first.withdraw(250)
  Person.first(name: &amp;quot;B&amp;quot;).accounts.first.deposit(250)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From this example, we see that Sequel&amp;#39;s dataset feature allows for complex transactional queries to be built using a few commands.&lt;/p&gt;
&lt;h2&gt;When To Choose Active Record Vs. Sequel for Ruby&lt;/h2&gt;
&lt;p&gt;When should you use Active Record, and when might Sequel be a better choice? Here&amp;#39;s a quick summary of each:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Active Record is very beginner-friendly, in that, it conveniently hides complex SQL queries under the hood. On the other hand, if you are an advanced user who&amp;#39;s comfortable using SQL, then this may be a bit limiting. Additionally, if you&amp;#39;re looking for more powerful features like proxying queries, you&amp;#39;ll likely need to add complementary gems on top of Active Record.&lt;/li&gt;
&lt;li&gt;Sequel is very powerful, especially for those who master its dataset feature. That said, you can expect a steeper learning curve compared to Active Record. If you get through that, though, you will be rewarded with a flexible ORM that is perfect for almost anything you could throw at it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, we&amp;#39;ve compared two Ruby object-relational mapping libraries, Active Record and Sequel. We explored how each handles filtering, database locking, and transactions. Finally, we touched on when you might want to use one ORM over the other.&lt;/p&gt;
&lt;p&gt;Until next time, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.P.S. Did you know that AppSignal offers an Active Record integration? &lt;a href=&quot;https://www.appsignal.com/ruby/active-record-monitoring&quot;&gt;Find out more&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Hotwire Modals in Ruby on Rails with Stimulus and Turbo Frames</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/02/21/hotwire-modals-in-ruby-on-rails-with-stimulus-and-turbo-frames.html"/>
    <id>https://blog.appsignal.com/2024/02/21/hotwire-modals-in-ruby-on-rails-with-stimulus-and-turbo-frames.html</id>
    <published>2024-02-21T00:00:00+00:00</published>
    <updated>2024-02-21T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the first part of our series, we&#039;ll explore two Hotwire methods to make modals accessible in your Rails application.</summary>
    <content type="html">&lt;p&gt;Modals are widely used on the web, but they are rarely built with accessibility in mind. When a modal is displayed, the background is dimmed visually but it&amp;#39;s still visible to screen readers and keyboard-only users.&lt;/p&gt;
&lt;p&gt;In this post, the first of a two-part series, we&amp;#39;ll look at presenting accessible modals in Rails using two different approaches powered by Hotwire&amp;#39;s Turbo and Stimulus libraries.&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s see what we need to do to make modals accessible.&lt;/p&gt;
&lt;h2&gt;How Can We Make Modals Accessible?&lt;/h2&gt;
&lt;p&gt;To make modals accessible, we need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hide the background from screen readers.&lt;/li&gt;
&lt;li&gt;Implement &amp;quot;focus trapping&amp;quot; so keyboard users cannot focus on elements outside the modal.&lt;/li&gt;
&lt;li&gt;Shift focus to the first focusable element in the modal (if there is one).&lt;/li&gt;
&lt;li&gt;Dismiss the modal with the &lt;code&gt;Esc&lt;/code&gt; key.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We could accomplish the first two by setting &lt;code&gt;inert=&amp;quot;true&amp;quot;&lt;/code&gt; on the background elements, but this attribute can&amp;#39;t be set on an ancestor of the modal. We can do the last two with some more custom JavaScript. As you can imagine this is rather tedious.&lt;/p&gt;
&lt;p&gt;The HTML &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element gives us most of the above list for free, and is supported for &lt;a href=&quot;https://caniuse.com/?search=dialog&quot;&gt;94.25% of global users&lt;/a&gt;, meaning it&amp;#39;s ideal in most cases.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look a bit more closely at the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;h2&gt;The &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; Element&lt;/h2&gt;
&lt;p&gt;According to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog&quot;&gt;MDN&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The HTML &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element is used to create both modal and non-modal dialog boxes. Modal dialog boxes interrupt interaction with the rest of the page being inert, while non-modal dialog boxes allow interaction with the rest of the page.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means that a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; isn&amp;#39;t necessarily a modal. It can be presented within the document flow as well.&lt;/p&gt;
&lt;h3&gt;Presenting a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;To present a dialog as a modal, we need to use JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;dialog&amp;gt;Lorem ipsum ....&amp;lt;/dialog&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const modal = document.querySelector(&amp;quot;dialog&amp;quot;);
modal.showModal();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When presenting a dialog this way, the background is made inert, focus is trapped in the modal, and it can be dismissed using the &lt;code&gt;Esc&lt;/code&gt; key.&lt;/p&gt;
&lt;p&gt;To present a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; in a non-modal context, and hence make it visible by default, we use the &lt;code&gt;open&lt;/code&gt; attribute:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;dialog open&amp;gt;Lorem ipsum ....&amp;lt;/dialog&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We won&amp;#39;t get the accessibility features using this method, though, as the dialog hasn&amp;#39;t been presented &amp;quot;modally&amp;quot;.&lt;/p&gt;
&lt;h3&gt;Dismissing a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;A modally presented &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; can be dismissed using JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const modal = document.querySelector(&amp;quot;dialog&amp;quot;);
modal.showModal();
modal.close();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It can also be closed using a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; with the &lt;code&gt;dialog&lt;/code&gt; method. This is a great way to implement a &amp;quot;close&amp;quot; button.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;dialog&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;h2&amp;gt;A modal dialog&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&amp;quot;dialog&amp;quot;&amp;gt;
      &amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;Close&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  &amp;lt;/header&amp;gt;

  Lorem ipsum...
&amp;lt;/dialog&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That covers the basics of the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element, so let&amp;#39;s look at using it with Hotwire.&lt;/p&gt;
&lt;h2&gt;Modal Presentation Using Stimulus in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stimulus.hotwired.dev&quot;&gt;Stimulus&lt;/a&gt; is a JavaScript library under the &lt;a href=&quot;https://hotwired.dev&quot;&gt;Hotwire&lt;/a&gt; umbrella. It allows us to attach pieces of JavaScript logic to HTML elements encapsulated in &lt;em&gt;controllers&lt;/em&gt;. This post assumes a basic familiarity with its API.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start with a Rails controller and view. As an example use case, we&amp;#39;ll use a &lt;em&gt;support&lt;/em&gt; page which has a button to display some contact details in a modal. Generate a controller and action using:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ bin/rails generate controller support show
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Amend the created route to a more user-friendly path:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/routes.rb
Rails.application.routes.draw do
  # ...

  get &amp;#39;/support&amp;#39;, to: &amp;quot;support#show&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sketch out a button and dialog in the newly generated view file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/support/show.html.erb %&amp;gt;

&amp;lt;button&amp;gt;
  Show contact details
&amp;lt;/button&amp;gt;

&amp;lt;dialog aria-labelledby=&amp;quot;modal_title&amp;quot;&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;h2 id=&amp;quot;modal_title&amp;quot;&amp;gt;
      Contact details
    &amp;lt;/h2&amp;gt;
    &amp;lt;form method=&amp;quot;dialog&amp;quot;&amp;gt;
      &amp;lt;button aria-label=&amp;quot;close&amp;quot;&amp;gt;X&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  &amp;lt;/header&amp;gt;

  &amp;lt;p&amp;gt;
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
    incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
    nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
  &amp;lt;/p&amp;gt;
&amp;lt;/dialog&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Run the Rails server and navigate to the &lt;code&gt;/support&lt;/code&gt; path. You should see a button there, but it doesn&amp;#39;t do anything at the moment. Let&amp;#39;s create a Stimulus controller and wire it up.&lt;/p&gt;
&lt;h3&gt;The Stimulus Controller in Rails&lt;/h3&gt;
&lt;p&gt;Use the generator to create a Stimulus controller.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ bin/rails generate stimulus modal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the button is clicked, we need to get a reference to the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; and call &lt;code&gt;showModal()&lt;/code&gt; on it. To keep the controller generic, we&amp;#39;ll pass in the element&amp;#39;s &lt;code&gt;id&lt;/code&gt; as a &lt;em&gt;param&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/modal_controller.js

import { Controller } from &amp;quot;@hotwired/stimulus&amp;quot;;

// Connects to data-controller=&amp;quot;modal&amp;quot;
export default class extends Controller {
  show(event) {
    const dialog = document.getElementById(event.params.dialog);
    dialog.showModal();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now decorate the button with the required &lt;code&gt;data-&lt;/code&gt; attributes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/support/show.html.erb %&amp;gt;

&amp;lt;button
  data-controller=&amp;quot;modal&amp;quot;
  data-action=&amp;quot;modal#show&amp;quot;
  data-modal-dialog-param=&amp;quot;contact_details_modal&amp;quot;&amp;gt;
  Show contact details
&amp;lt;/button&amp;gt;

&amp;lt;dialog id=&amp;quot;contact_details_modal&amp;quot; aria-labelledby=&amp;quot;modal_title&amp;quot;&amp;gt;
  &amp;lt;%# ... %&amp;gt;
&amp;lt;/dialog&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Refresh the page and the button should now work!&lt;/p&gt;
&lt;p&gt;There&amp;#39;s an opportunity to remove some boilerplate code here. The &lt;code&gt;modal&lt;/code&gt; controller should always be attached to a button that shows the modal. We can remove the &lt;code&gt;data-action&lt;/code&gt; from the markup and set it in the controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/modal_controller.js

// ...
export default class extends Controller {
  connect() {
    this.element.dataset.action = &amp;quot;modal#show&amp;quot;;
  }

  show(event) {
    // ...
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Styling the Modal&lt;/h3&gt;
&lt;p&gt;The dialog looks fairly basic, so let&amp;#39;s add some styles:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scss&quot;&gt;/* app/assets/stylesheets/application.scss */

dialog {
  width: 80vw;
  margin: auto;

  &amp;amp;::backdrop {
    background: red;
    opacity: 0.2;
  }

  header {
    display: flex;
    align-items: center;

    h2 {
      flex: 0 1 100%;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;::backdrop&lt;/code&gt; pseudo-element is a really great way to style the modal&amp;#39;s background!&lt;/p&gt;
&lt;p&gt;Try using the &lt;code&gt;Tab&lt;/code&gt; key to navigate around the page and you&amp;#39;ll see that the focus is trapped within the modal. It&amp;#39;s worth reviewing the page &lt;a href=&quot;https://github.com/basecamp/accessibility/blob/master/Section-1/Autiditing-Accessibility-On-The-Web.md#review-the-page-using-voiceover-mac-or-nvda-win-screen-reader&quot;&gt;with a screen reader&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;This is the simplest way to modally present a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;. Next, we&amp;#39;ll look at a server-driven way to present modals: Turbo Frame.&lt;/p&gt;
&lt;h2&gt;Turbo Frame Powered Modals for Ruby&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://turbo.hotwired.dev/reference/frames&quot;&gt;Turbo Frames&lt;/a&gt; are a subset of the &lt;a href=&quot;https://turbo.hotwired.dev&quot;&gt;Turbo&lt;/a&gt; library which is also under the Hotwire umbrella. It allows us to scope navigation to specific parts of a page, updating them in isolation from the rest of the page.&lt;/p&gt;
&lt;p&gt;We can use Turbo Frames to present a modal rendered from the server.&lt;/p&gt;
&lt;h3&gt;Setting Up&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s add another button to display a modal &lt;em&gt;contact form&lt;/em&gt;. We&amp;#39;ll need a controller and action to render the form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ bin/rails generate controller support/tickets new create
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace the auto-generated routes with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.routes.draw do
  # ...

  namespace :support do
    resources :tickets, only: [:new, :create]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ll also need a global Turbo Frame to render modals, so let&amp;#39;s put it in the main &lt;em&gt;application&lt;/em&gt; layout:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/layouts/application.html.erb %&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;%# ... %&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;%= yield %&amp;gt;

    &amp;lt;%= turbo_frame_tag :remote_modal %&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a link to show the contact form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/support/show.html.erb %&amp;gt;

&amp;lt;button
  data-controller=&amp;quot;modal&amp;quot;
  data-modal-dialog-param=&amp;quot;contact_details_modal&amp;quot;&amp;gt;
  Show contact details
&amp;lt;/button&amp;gt;

&amp;lt;%= link_to new_support_ticket_path, data: { turbo_frame: :remote_modal } do %&amp;gt;
  Show contact form
&amp;lt;% end %&amp;gt;

&amp;lt;%# ... %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you&amp;#39;re done!&lt;/p&gt;
&lt;h3&gt;Rendering and Presenting the Form&lt;/h3&gt;
&lt;p&gt;Clicking the &lt;em&gt;Show contact form&lt;/em&gt; link will expect a &lt;code&gt;&amp;lt;turbo-frame&amp;gt;&lt;/code&gt; with &lt;code&gt;id=&amp;quot;remote_modal&amp;quot;&lt;/code&gt; in the response and update the global Turbo Frame with its content. Fill in the view with the form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/support/tickets/new.html.erb %&amp;gt;

&amp;lt;%= turbo_frame_tag :remote_modal do %&amp;gt;
  &amp;lt;dialog id=&amp;quot;contact_form_modal&amp;quot; aria-labelledby=&amp;quot;modal_title&amp;quot;&amp;gt;
    &amp;lt;header&amp;gt;
      &amp;lt;h2 id=&amp;quot;modal_title&amp;quot;&amp;gt;
        Contact us
      &amp;lt;/h2&amp;gt;
      &amp;lt;form method=&amp;quot;dialog&amp;quot;&amp;gt;
        &amp;lt;button aria-label=&amp;quot;close&amp;quot;&amp;gt;X&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/header&amp;gt;

    &amp;lt;%= form_with(url: support_tickets_path) do |form| %&amp;gt;
      &amp;lt;%= form.label :message, &amp;quot;Your message&amp;quot; %&amp;gt;
      &amp;lt;%= form.text_area :message, autofocus: true %&amp;gt;

      &amp;lt;%= form.button &amp;quot;Close&amp;quot;, value: nil, formmethod: :dialog %&amp;gt;
      &amp;lt;%= form.button &amp;quot;Send&amp;quot; %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/dialog&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ve now got a text area in the modal which is a focusable element. For accessibility, it should be focused by default when the modal is presented. The &lt;code&gt;autofocus&lt;/code&gt; attribute is used to accomplish this.&lt;/p&gt;
&lt;p&gt;Refresh the page and try clicking &lt;em&gt;Show contact form&lt;/em&gt;. Nothing will happen visually, but on inspecting the HTML, you&amp;#39;ll notice the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; has been rendered in the &lt;code&gt;remote_modal&lt;/code&gt; Turbo Frame. We haven&amp;#39;t presented it yet, which is why it&amp;#39;s invisible.&lt;/p&gt;
&lt;p&gt;We could render it with the &lt;code&gt;open&lt;/code&gt; attribute, but that would defeat the purpose as it&amp;#39;d be presented in a non-modal context without the modal accessibility features.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s create another Stimulus controller to present the form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ bin/rails generate stimulus remote_modal
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/remote_modal_controller.js

import { Controller } from &amp;quot;@hotwired/stimulus&amp;quot;;

// Connects to data-controller=&amp;quot;remote-modal&amp;quot;
export default class extends Controller {
  connect() {
    this.element.showModal();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And hook it up to the dialog:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# app/views/support/tickets/new.html.erb %&amp;gt;

&amp;lt;%= turbo_frame_tag :remote_modal do %&amp;gt;
  &amp;lt;dialog
    id=&amp;quot;contact_form_modal&amp;quot;
    aria-labelledby=&amp;quot;modal_title&amp;quot;
    data-controller=&amp;quot;remote-modal&amp;quot;&amp;gt;
    &amp;lt;%# ... %&amp;gt;
  &amp;lt;/dialog&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Refresh the page and try viewing the contact form again. This time it should work. We&amp;#39;ve just rendered a modal from the server!&lt;/p&gt;
&lt;p&gt;While this is quite convenient, it&amp;#39;s not ideal to create a new Stimulus controller just to present a modal. There&amp;#39;s another method we can use as well: Turbo Streaming Modals. We&amp;#39;ll take a look at those in part two of this series.&lt;/p&gt;
&lt;h2&gt;Up Next: Turbo Streaming Modals&lt;/h2&gt;
&lt;p&gt;In this post, we explored two different methods to present modals using Hotwire: Stimulus and Turbo Frames.&lt;/p&gt;
&lt;p&gt;In part two, we&amp;#39;ll look at another way to present modals: using Turbo Streams.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Full-Text Search for Ruby on Rails with Litesearch</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/02/14/full-text-search-for-ruby-on-rails-with-litesearch.html"/>
    <id>https://blog.appsignal.com/2024/02/14/full-text-search-for-ruby-on-rails-with-litesearch.html</id>
    <published>2024-02-14T00:00:00+00:00</published>
    <updated>2024-02-14T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In part six of our series, we&#039;ll use Litesearch, the last piece of the puzzle in LiteStack.</summary>
    <content type="html">&lt;p&gt;In this post, we&amp;#39;ll turn to the last piece of the puzzle in LiteStack: Litesearch.&lt;/p&gt;
&lt;p&gt;As an example, we will equip a prompts index page with a search bar to query a database for certain prompts. We will generate a couple of fake records to test our search functionality against.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get to it!&lt;/p&gt;
&lt;h2&gt;What Is Litesearch?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/oldmoe/litestack#litesearch&quot;&gt;Litesearch&lt;/a&gt;&lt;/strong&gt; is a convenience wrapper built around &lt;a href=&quot;https://sqlite.org/fts5.html&quot;&gt;FTS5&lt;/a&gt;, SQLite&amp;#39;s virtual table-based &lt;strong&gt;full-text search&lt;/strong&gt; module.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll dive into the mechanics a bit later. For now, we will assume that Litesearch is a Ruby module providing a simple API to perform text searches against a SQLite database. This works in standalone mode, but we will focus on the ActiveRecord integration, of course.&lt;/p&gt;
&lt;h2&gt;Configure the &lt;code&gt;Prompt&lt;/code&gt; Model for Litesearch&lt;/h2&gt;
&lt;p&gt;The single addition we must make to our prompt model is a &lt;strong&gt;search index schema definition&lt;/strong&gt;. To do this, we have to include the &lt;code&gt;Litesearch::Model&lt;/code&gt; module in our model and call the &lt;code&gt;litesearch&lt;/code&gt; class method to add fields to the schema:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  class Prompt &amp;lt; ApplicationRecord
    include AccountScoped
+   include Litesearch::Model

    # ...

+   litesearch do |schema|
+     schema.fields [:title]
+   end

    # ...
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also target associations like so, and change the tokenizer used for indexing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;litesearch do |schema|
  schema.field :account_name, target: &amp;quot;accounts.name&amp;quot;
  schema.tokenizer :porter
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Currently, ActionText fields are not supported.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s quickly try this out in the Rails console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;gt; Current.account = Account.first
&amp;gt; Prompt.search(&amp;quot;teddy&amp;quot;)
  Prompt Load (7.4ms)  SELECT prompts.*, -prompts_search_idx.rank AS search_rank FROM &amp;quot;prompts&amp;quot; INNER JOIN prompts_search_idx ON prompts.id = prompts_search_idx.rowid AND rank != 0 AND prompts_search_idx MATCH &amp;#39;teddy&amp;#39; WHERE &amp;quot;prompts&amp;quot;.&amp;quot;account_id&amp;quot; = ? ORDER BY prompts_search_idx.rank  [[&amp;quot;account_id&amp;quot;, 1]]
=&amp;gt;
[#&amp;lt;Prompt:0x0000000105f80fb0
  id: 1,
  title: &amp;quot;A cute teddy bear&amp;quot;,
  prompt_image: &amp;lt;...&amp;gt;,
  account_id: 1,
  created_at: Fri, 12 Jan 2024 10:47:08.604031000 UTC +00:00,
  updated_at: Fri, 12 Jan 2024 10:47:41.321896000 UTC +00:00,
  content_type: &amp;quot;image/jpeg&amp;quot;,
  search_rank: 1.0e-06&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember to set &lt;code&gt;Current.account&lt;/code&gt;, because our prompt model is scoped to an account, otherwise we get an empty result set.&lt;/p&gt;
&lt;p&gt;Impressive! By changing only 4 lines of code, we already have a crude working version of full-text search.&lt;/p&gt;
&lt;h2&gt;Add a Typeahead Search Bar to Our Ruby on Rails Application&lt;/h2&gt;
&lt;p&gt;Next up, we&amp;#39;ll combine a few of the techniques we&amp;#39;ve reviewed to implement snappy typeahead searching. Before we do that, though, let&amp;#39;s generate more sample data. I will use the popular &lt;a href=&quot;https://github.com/faker-ruby/faker&quot;&gt;faker&lt;/a&gt; gem to do that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add faker --group development
$ bin/rails console
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Drop into a Rails console and create 50 sample prompts. I&amp;#39;m re-using the first prompt&amp;#39;s image data here. Also, note that I&amp;#39;m again setting the &lt;code&gt;Current.account&lt;/code&gt; first.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;gt; Current.account = Account.first
* 50.times do
*   Prompt.create(title: &amp;quot;a #{Faker::Adjective.positive} #{Faker::Creature::Animal.name}&amp;quot;, content_type: &amp;quot;image/png&amp;quot;, prompt_image: Prompt.first.prompt_image)
&amp;gt; end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To prepare our user interface for reactive searching, we will wrap the prompts grid in a Turbo frame. This frame will be replaced every time the search query changes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/prompts/index.html.erb --&amp;gt;
  &amp;lt;h1&amp;gt;Prompts&amp;lt;/h1&amp;gt;

+ &amp;lt;%= turbo_frame_tag :prompts, class: &amp;quot;grid&amp;quot; do %&amp;gt;
- &amp;lt;div id=&amp;quot;prompts&amp;quot; class=&amp;quot;grid&amp;quot;&amp;gt;
    &amp;lt;% @prompts.each do |prompt| %&amp;gt;
      &amp;lt;%= link_to prompt do %&amp;gt;
        &amp;lt;%= render &amp;quot;index&amp;quot;, prompt: prompt %&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;% end %&amp;gt;
+ &amp;lt;% end %&amp;gt;
- &amp;lt;/div&amp;gt;

  &amp;lt;%= link_to &amp;quot;New prompt&amp;quot;, new_prompt_path %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;PromptsController&lt;/code&gt; needs to be updated to filter prompts if a &lt;code&gt;query&lt;/code&gt; parameter is passed in:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/controllers/prompts_controller.rb
  class PromptsController &amp;lt; ApplicationController
    # ...

    def index
      @prompts = Prompt.all
+
+     @prompts = @prompts.search(params[:query]) if params[:query].present?
    end

    # ...
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&amp;#39;s rig up the search bar in the prompt index view. For this, we&amp;#39;ll use a shoelace input component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/prompts/index.html.erb --&amp;gt;
  &amp;lt;h1&amp;gt;Prompts&amp;lt;/h1&amp;gt;

+ &amp;lt;section&amp;gt;
+   &amp;lt;sl-input name=&amp;quot;search&amp;quot; type=&amp;quot;search&amp;quot; placeholder=&amp;quot;Search for a prompt title&amp;quot; clearable&amp;gt;
+     &amp;lt;sl-icon name=&amp;quot;search&amp;quot; slot=&amp;quot;suffix&amp;quot;&amp;gt;&amp;lt;/sl-icon&amp;gt;
+   &amp;lt;/sl-input&amp;gt;
+ &amp;lt;/section&amp;gt;

  &amp;lt;div id=&amp;quot;prompts&amp;quot; class=&amp;quot;grid&amp;quot;&amp;gt;
    &amp;lt;% @prompts.each do |prompt| %&amp;gt;
      &amp;lt;%= link_to prompt do %&amp;gt;
        &amp;lt;%= render &amp;quot;index&amp;quot;, prompt: prompt %&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;%= link_to &amp;quot;New prompt&amp;quot;, new_prompt_path %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;To implement typeahead searching, we must add a bit of custom JavaScript to &lt;code&gt;app/javascript/application.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  // app/javascript/application.js

  // Entry point for the build script in your package.json
  import &amp;quot;@hotwired/turbo-rails&amp;quot;;
  import &amp;quot;./controllers&amp;quot;;
  import &amp;quot;trix&amp;quot;;
  import &amp;quot;@rails/actiontext&amp;quot;;

  import { setBasePath } from &amp;quot;@shoelace-style/shoelace&amp;quot;;

  setBasePath(&amp;quot;/&amp;quot;);
+
+ document
+   .querySelector(&amp;quot;sl-input[name=search]&amp;quot;)
+   .addEventListener(&amp;quot;keyup&amp;quot;, (event) =&amp;gt; {
+     document.querySelector(
+       &amp;quot;#prompts&amp;quot;
+     ).src = `/prompts?query=${encodeURIComponent(event.target.value)}`;
+   });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tiny JavaScript snippet does little more than place a &lt;code&gt;keyup&lt;/code&gt; listener on our search field, and update the Turbo Frame&amp;#39;s &lt;code&gt;src&lt;/code&gt; attribute afterward. The input&amp;#39;s &lt;code&gt;value&lt;/code&gt; is added as the &lt;code&gt;query&lt;/code&gt; parameter. Turbo Frame&amp;#39;s default behavior performs the rest of the magic: reloading when the &lt;code&gt;src&lt;/code&gt; attribute changes, with the updated content fetched from the server.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s what this looks like:&lt;/p&gt;
&lt;Video fileName=&quot;/images/blog/2024-02/typeahead-search&quot; /&gt;

&lt;h1&gt;Excursus: Highlighting Search Results Using a Turbo Event in Rails&lt;/h1&gt;
&lt;p&gt;Currently, Litesearch doesn&amp;#39;t feature a native highlighting solution like &lt;a href=&quot;https://github.com/Casecommons/pg_search?tab=readme-ov-file#highlight&quot;&gt;pg_search&lt;/a&gt;, but it is pretty easy to build this ourselves using the &lt;code&gt;before-frame-render&lt;/code&gt; event:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  // Entry point for the build script in your package.json
  import &amp;quot;@hotwired/turbo-rails&amp;quot;;
  import &amp;quot;./controllers&amp;quot;;
  import &amp;quot;trix&amp;quot;;
  import &amp;quot;@rails/actiontext&amp;quot;;

  import { setBasePath } from &amp;quot;@shoelace-style/shoelace&amp;quot;;

  setBasePath(&amp;quot;/&amp;quot;);

  document
    .querySelector(&amp;quot;sl-input[name=search]&amp;quot;)
    .addEventListener(&amp;quot;keyup&amp;quot;, (event) =&amp;gt; {
      document.querySelector(
        &amp;quot;#prompts&amp;quot;
      ).src = `/prompts?query=${encodeURIComponent(event.target.value)}`;
    });

+ document
+   .querySelector(&amp;quot;turbo-frame#prompts&amp;quot;)
+   .addEventListener(&amp;quot;turbo:before-frame-render&amp;quot;, (event) =&amp;gt; {
+     event.preventDefault();
+
+     const newHTML = event.detail.newFrame.innerHTML;
+
+     const query = document.querySelector(&amp;quot;sl-input[name=search]&amp;quot;).value;
+     if (!!query) {
+       event.detail.newFrame.innerHTML = newHTML.replace(
+         new RegExp(`(${query})`, &amp;quot;ig&amp;quot;),
+         &amp;quot;&amp;lt;em&amp;gt;$1&amp;lt;/em&amp;gt;&amp;quot;
+       );
+     }
+
+     event.detail.resume();
+   });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This leverages a nifty, somewhat hidden Turbo feature: intercepting rendering. The &lt;a href=&quot;https://turbo.hotwired.dev/reference/events&quot;&gt;Turbo &lt;code&gt;before-render&lt;/code&gt; and &lt;code&gt;before-frame-render&lt;/code&gt; events&lt;/a&gt; support pausing rendering and mangling returned HTML from the server. Here, we use this to wrap each occurrence of a search query in an &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; tag:&lt;/p&gt;
&lt;Video fileName=&quot;/images/blog/2024-02/typeahead-search-highlight&quot; /&gt;

&lt;h2&gt;Under the Hood: Litesearch for Ruby on Rails Explained&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve covered the basics of activating and configuring Litesearch for your LiteStack-powered Ruby on Rails application. As you might have guessed, there&amp;#39;s a lot more potential hidden here.&lt;/p&gt;
&lt;p&gt;So let&amp;#39;s briefly examine how Litesearch wraps around and leverages SQLite&amp;#39;s built-in full-text search module, &lt;a href=&quot;https://sqlite.org/fts5.html&quot;&gt;FTS5&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Virtual Tables in SQLite&lt;/h3&gt;
&lt;p&gt;First, let&amp;#39;s discuss the notion of &lt;strong&gt;&lt;a href=&quot;https://sqlite.org/vtab.html&quot;&gt;virtual tables&lt;/a&gt;&lt;/strong&gt; in SQLite. Since there&amp;#39;s no direct counterpart in the PostgreSQL or MySQL realm, it pays off to learn about these.&lt;/p&gt;
&lt;p&gt;From the vantage point of a user issuing an SQL statement against the database, a virtual table is a transparent proxy that adheres to the interface of a table. In the background, however, every query or manipulation invokes a callback of the virtual table structure instead of writing to disk.&lt;/p&gt;
&lt;p&gt;In short, a virtual table is something you reach for when you want to access &amp;quot;foreign&amp;quot; data without leaving the domain of your database connection. Apart from full-text search, other examples include &lt;a href=&quot;https://sqlite.org/rtree.html&quot;&gt;geospatial indices&lt;/a&gt; or accessing a different file format, such as CSV.&lt;/p&gt;
&lt;h3&gt;SQLite&amp;#39;s FTS5 Full-Text Search Extension&lt;/h3&gt;
&lt;p&gt;At its core, SQLite&amp;#39;s full-text search engine is a virtual table.&lt;/p&gt;
&lt;p&gt;The table definition used by Litesearch in ActiveRecord mode looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;CREATE VIRTUAL TABLE #{name} USING FTS5(#{col_names}, content=&amp;#39;&amp;#39;, contentless_delete=1, tokenize=&amp;#39;#{tokenizer_sql}&amp;#39;)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;name&lt;/code&gt; is the index name (it defaults to &lt;code&gt;&amp;quot;#{table_name}_search_idx&amp;quot;&lt;/code&gt;), and &lt;code&gt;col_names&lt;/code&gt; are the fields we set in our Litesearch schema definition.&lt;/p&gt;
&lt;p&gt;We will now briefly look at &lt;strong&gt;tokenizers&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Tokenizers&lt;/h2&gt;
&lt;p&gt;To allow for efficient indexing, a full-text search engine employs a helper utility to split the payload into tokens: a &lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Lexical_analysis#Tokenization&quot;&gt;tokenizer&lt;/a&gt;&lt;/strong&gt;. &lt;a href=&quot;https://sqlite.org/fts5.html#tokenizers&quot;&gt;FTS5 has three built-in tokenizers&lt;/a&gt; you can choose from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;unicode61&lt;/strong&gt; (default): All punctuation and whitespace characters (i.e. &amp;quot;,&amp;quot;, &amp;quot;.&amp;quot; etc.) are considered separators. Text is split at those characters, and the resulting list of connected characters (usually, words) are the tokens. In the wild, you might encounter the &lt;code&gt;remove_diacritics&lt;/code&gt; option. This option specifies how to treat glyphs added to letters, like &amp;quot;á&amp;quot;, &amp;quot;à&amp;quot;, etc. The default is to remove these &amp;quot;diacritics&amp;quot;, so these characters are regarded as equivalent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ascii&lt;/strong&gt;: Similar to unicode61, but all non-ASCII characters are always considered token characters. There is no &lt;code&gt;remove_diacritics&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;porter&lt;/strong&gt;: A tokenizer that employs &lt;a href=&quot;https://tartarus.org/martin/PorterStemmer/&quot;&gt;porter stemming&lt;/a&gt; for tokenization. This essentially means that you can do similarity searches, i.e., &amp;quot;search&amp;quot;, &amp;quot;searchable&amp;quot;, and &amp;quot;searching&amp;quot; will be considered related.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;FTS5 Search Interface&lt;/h3&gt;
&lt;p&gt;To enable a convenient experience, Litesearch exposes a &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/92be782b681b3476e30b2fb216829728576bae33/lib/litestack/litesearch/model.rb#L123-L124&quot;&gt;&lt;code&gt;search&lt;/code&gt; class method&lt;/a&gt;. Essentially, this method joins the model&amp;#39;s table to the associated search index and issues a &lt;code&gt;MATCH&lt;/code&gt; query. Results are then ordered according to the search rank and returned:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def search(term)
  self.select(
    &amp;quot;#{table_name}.*&amp;quot;
  ).joins(
    &amp;quot;INNER JOIN #{index_name} ON #{table_name}.id = #{index_name}.rowid AND rank != 0 AND #{index_name} MATCH &amp;quot;, Arel.sql(&amp;quot;&amp;#39;#{term}&amp;#39;&amp;quot;)
  ).select(
    &amp;quot;-#{index_name}.rank AS search_rank&amp;quot;
  ).order(
    Arel.sql(&amp;quot;#{index_name}.rank&amp;quot;)
  )
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Currently, Litesearch doesn&amp;#39;t expose more of FTS5&amp;#39;s search syntax, but you can learn more about it &lt;a href=&quot;https://sqlite.org/fts5.html#full_text_query_syntax&quot;&gt;in FTS5&amp;#39;s documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;This concludes our series on LiteStack. In this post, we discovered Litesearch, the full-text search engine built into LiteStack. We learned how to configure an ActiveRecord model to expose search fields and other options to an SQLite text search index.&lt;/p&gt;
&lt;p&gt;We then flexed our Hotwire muscles to build a simple reactive search interface into our UI.&lt;/p&gt;
&lt;p&gt;Finally, we explored some of the inner workings of full-text search in SQLite to get a better understanding of what powers it, its benefits, and its limitations.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Deep Dive Into RSpec Tests in Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/02/07/a-deep-dive-into-rspec-tests-in-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2024/02/07/a-deep-dive-into-rspec-tests-in-ruby-on-rails.html</id>
    <published>2024-02-07T00:00:00+00:00</published>
    <updated>2024-02-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the second and final part of our series, we&#039;ll explore using RSpec for tests in your Rails application.</summary>
    <content type="html">&lt;p&gt;In our last post, we looked at the basics of RSpec and explored how well it works with Behavior Driven Development in Ruby.&lt;/p&gt;
&lt;p&gt;Now, we will look at specific types of RSpec tests for different parts of a Ruby on Rails application.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive straight in!&lt;/p&gt;
&lt;h2&gt;Unit, Controller, and Integration Tests in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;A Ruby on Rails application is composed of several layers. As the framework is built around Models, Views, and Controllers, we might think of those three as the only layers of an application. Yet, often, that&amp;#39;s barely enough to describe a Ruby on Rails application.&lt;/p&gt;
&lt;p&gt;Mailers, Jobs, and Helpers are secondary layers we don&amp;#39;t want to miss. When it comes to testing, it&amp;#39;s important to remember that these components are largely impacted by how our code has been designed. Using &lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID&quot;&gt;SOLID principles&lt;/a&gt; — particularly Single Responsibility — helps keep our code straightforward and more testable.&lt;/p&gt;
&lt;p&gt;So, we usually aim to keep things simple in views, controllers, models, jobs, and mailers: we want to assemble and use the bricks of code we have designed and tested separately already. Once we enter the realm of views and controllers, testing becomes much more complex, and slower too.&lt;/p&gt;
&lt;h3&gt;Categories of Ruby Tests&lt;/h3&gt;
&lt;p&gt;Usually, we split tests into these categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unit tests&lt;/strong&gt; (models and other plain old Ruby objects — POROs): Ensure class methods are working as expected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controller tests:&lt;/strong&gt; Test the expected outcome of controller actions (rendering the right template, redirections, and flash messages).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View tests:&lt;/strong&gt; (Used rarely) to test specific elements or content in views.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Helper tests:&lt;/strong&gt; For when you use a complex helper method.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature or system tests:&lt;/strong&gt; Simulate user interactions in the browser — this is where Capybara will help.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request tests:&lt;/strong&gt; Test an application&amp;#39;s response to various HTTP verbs without browser overhead. This is faster than feature tests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mailer and jobs tests:&lt;/strong&gt; Specific tests are needed for this secondary layer to ensure emails are sent properly with the right content and jobs trigger the appropriate work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routing tests:&lt;/strong&gt; Complex routing is a code smell. Testing routes will help you avoid trouble when you have too many routes or routes that are too complex.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What Kinds of Tests Should We Write?&lt;/h3&gt;
&lt;p&gt;Do we need to write all of these tests? It depends on your culture and team, but these might be good starting points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unit tests:&lt;/strong&gt; Write focused and (as much as possible) database-dependent tests for your models&amp;#39; public methods and other POROs you craft into your application; they should run fast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controller tests:&lt;/strong&gt; Write tests that help ensure controller actions are routable and respond as expected for each context they could be in. These should not aim to test the whole response or complex scenarios made of multiple requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request tests:&lt;/strong&gt; Test request scenarios from a machine client (think API controllers), typically: &amp;quot;for a given HTTP request context (HTTP verb, path, and parameters), what HTTP response should we get?&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System tests:&lt;/strong&gt; Test scenarios of human user requests. Typically, this is where we want to test complex scenarios with multiple clicks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mailer and job tests:&lt;/strong&gt; These should only test that a mailer and job are doing the specific &amp;quot;mailer&amp;quot; and &amp;quot;job&amp;quot; work properly. In particular, for jobs, most of the &amp;quot;work&amp;quot; should be done through classes and methods tested separately.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Controller tests are often replaced by a good layer of request and system tests, as they ultimately serve the same purpose. Too many tests will cause fatigue in your team or slow down your whole test suite for little benefit. It&amp;#39;s up to you. The point is to test your controller layer: figure out which way is best in your case.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;#39;s see how unit, controller, and integration (both request and system) tests look.&lt;/p&gt;
&lt;h2&gt;Using RSpec and Ruby on Rails for Testing&lt;/h2&gt;
&lt;p&gt;As pointed out in &lt;a href=&quot;https://blog.appsignal.com/2024/01/24/behaviour-driven-development-in-ruby-with-rspec.html&quot;&gt;our previous post&lt;/a&gt;, we can use the &lt;code&gt;rspec-rails&lt;/code&gt; gem to integrate RSpec into a Ruby on Rails application&amp;#39;s code base.&lt;/p&gt;
&lt;h2&gt;Unit Tests&lt;/h2&gt;
&lt;p&gt;Unit tests are at the base of the &lt;a href=&quot;https://www.headspin.io/blog/the-testing-pyramid-simplified-for-one-and-all&quot;&gt;testing pyramid&lt;/a&gt;, so there will be numerous unit tests in a codebase. They need to be fast.&lt;/p&gt;
&lt;p&gt;Remember: tests (with RSpec) are focused on testing code behavior, not implementation. That should help to write fast tests. In the case of Ruby on Rails projects, models are a big source of unit tests. As models are related to database access (either read or write access), those tests can have a performance impact. So be careful about reading and writing to the database for those tests. In some cases, you won&amp;#39;t be able to avoid calling &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;save&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, or &lt;code&gt;find&lt;/code&gt;, but you can in most. You should be able to rely on &lt;code&gt;new&lt;/code&gt; instead of &lt;code&gt;create&lt;/code&gt;, for example.&lt;/p&gt;
&lt;p&gt;Here is what an RSpec unit test for a Ruby on Rails model looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# spec/models/user.rb
require &amp;#39;rails_helper&amp;#39; # a file generated with rspec-rails containing configuration for rspec in a rails context

RSpec.describe User, type: :model do
  describe &amp;#39;#valid_email?&amp;#39; do
    subject(:user) { User.new(email: email) }

    context &amp;#39;when email is valid&amp;#39; do
      let(:email) { Faker::Internet.email }

      it { expect(user.valid_email?).to be(true) }
    end

    context &amp;#39;when email is not valid&amp;#39; do
      let(:email) { &amp;#39;bob@example&amp;#39; }

      it &amp;#39;returns false&amp;#39; do
        expect(user.valid_email?).to be(false) }
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While those two examples are written a bit differently, they basically do the same thing. One has no description and reads pretty nicely still; the other one has a description but carries a bit of a duplication, don&amp;#39;t you think?&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Note that we use &lt;code&gt;User.new&lt;/code&gt; to instantiate a user. This call doesn&amp;#39;t require a read or write to the database. Since our &lt;code&gt;valid_email?&lt;/code&gt; method only works with the instance&amp;#39;s attributes, it won&amp;#39;t slow the test down either.&lt;/p&gt;
&lt;p&gt;Those two are good unit tests: simple, focused, and fast.&lt;/p&gt;
&lt;p&gt;Unit tests are not just for Ruby on Rails models. They are also good for any classes you build around models and the rest of an application&amp;#39;s architecture. We have specified the test &lt;code&gt;type&lt;/code&gt; in the first line (&lt;code&gt;RSpec.describe User, type: :model&lt;/code&gt;), but we can totally avoid that if we write tests for a plain Ruby class.&lt;/p&gt;
&lt;h2&gt;Controller and Request Specs&lt;/h2&gt;
&lt;p&gt;Up until recently, controller tests were the main way to test controllers. Nowadays, we tend to rely on request specs instead. Similarly to controller tests, they are designed to test an application from a &lt;em&gt;machine client&lt;/em&gt;. They allow us to test one, or multiple, controller action/s. They are tests, so we need to define a context, then our expectation. As they are functional tests, we have to define a context composed of a given HTTP verb (get, post, put, ...), a path (&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/users&lt;/code&gt;, ...), parameters, and (potentially) a body. The expectation is then focused on the response (HTTP status code, response/s header/s, and body).&lt;/p&gt;
&lt;p&gt;With request specs, it&amp;#39;s not a matter of UI or Javascript.&lt;/p&gt;
&lt;p&gt;A simple request spec will look like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;quot;rails_helper&amp;quot;

RSpec.describe &amp;quot;User management&amp;quot;, type: :request do
  it &amp;quot;does not render the incorrect template&amp;quot; do
    get &amp;quot;/users/new&amp;quot;
    expect(response).to_not render_template(:show)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s simple, but you can see the context in the part doing the request: &lt;code&gt;get &amp;quot;/users/new&amp;quot;&lt;/code&gt;. In turn, the expectation itself is centered on the response. We see a new matcher here, allowing us to test if a specific template is rendered. Another one (&lt;code&gt;redirect_to&lt;/code&gt;) allows us to test that we are properly redirected.&lt;/p&gt;
&lt;p&gt;Request specs can also be used to test more complex scenarios.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;quot;rails_helper&amp;quot;

RSpec.describe &amp;quot;User management&amp;quot;, type: :request do

  it &amp;quot;creates a user and redirects to the user&amp;#39;s page&amp;quot; do
    get &amp;quot;/users/new&amp;quot;
    expect(response).to render_template(:new)

    post &amp;quot;/users&amp;quot;, params: { user: { first_name: &amp;quot;John&amp;quot;, last_name: &amp;quot;Doe&amp;quot;, email: &amp;quot;john@example.org&amp;quot; } }

    expect(response).to redirect_to(assigns(:user))
    follow_redirect!

    expect(response).to render_template(:show)
    expect(response.body).to include(&amp;quot;User John Doe (john@example.org) created.&amp;quot;)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, request specs are better adapted to test behavior from a machine client&amp;#39;s perspective. So, request specs are best used to test an API backend&amp;#39;s controller parts.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;quot;rails_helper&amp;quot;

RSpec.describe &amp;quot;User management&amp;quot;, type: :request do

  it &amp;quot;creates a user and returns the user&amp;#39;s details&amp;quot; do
    headers = { &amp;quot;CONTENT_TYPE&amp;quot; =&amp;gt; &amp;quot;application/json&amp;quot; }
    post &amp;quot;/users&amp;quot;, params: { user: { first_name: &amp;quot;John&amp;quot;, last_name: &amp;quot;Doe&amp;quot;, email: &amp;quot;john@example.org&amp;quot; } }

    expect(response.content_type).to eq(&amp;quot;application/json; charset=utf-8&amp;quot;)
    expect(response).to have_http_status(:created)
    expect(response.body).to include() # TODO
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While we haven&amp;#39;t included any in this example, we can also express different contexts to test behaviors. The RSpec vocabulary and grammar still applies fully in that kind of spec. We merely use a bit more to handle the parts specific to making an HTTP request and its response.&lt;/p&gt;
&lt;h2&gt;System Specs&lt;/h2&gt;
&lt;p&gt;System specs are more complete integration tests than request ones. They are used to test an application in a real or headless browser, and require a driver for the browser (the default is Selenium; this can be changed).&lt;/p&gt;
&lt;p&gt;System specs are similar to request specs in concept, but with improved grammar to focus on a user&amp;#39;s actions in the browser rather than the requests being made. Here is an example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;quot;rails_helper&amp;quot;

RSpec.describe &amp;quot;Account management&amp;quot;, type: :system do
  it &amp;quot;enables me to create an account&amp;quot; do
    visit &amp;quot;/account/new&amp;quot;

    fill_in &amp;quot;Name&amp;quot;, with: &amp;quot;Acm&amp;#39;e Ltd.&amp;quot;
    fill_in &amp;quot;Address&amp;quot;, with: &amp;quot;6 Av. Elysian fields&amp;quot;
    click_button &amp;quot;Create Account&amp;quot;

    expect(page).to have_text(&amp;quot;Account successfully created.&amp;quot;)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that here, the context (verb, path, parameters) is defined underneath but still there: we &lt;em&gt;visit&lt;/em&gt; a page, fill out a form (which sets parameters), and then click a button (trigger a POST request). Finally, the expectation is defined as the content of the page rendered. Indirectly, it&amp;#39;s still about testing the response&amp;#39;s content, although the browser interprets that.&lt;/p&gt;
&lt;p&gt;Because system specs use and drive the complete stack, they are a lot slower than other specs. As such, they should be limited in number and potentially run at particular times within the CI pipeline.&lt;/p&gt;
&lt;h2&gt;Complimentary Test Types in Ruby&lt;/h2&gt;
&lt;p&gt;As mentioned, Ruby on Rails applications are composed of a few more components with specific roles: mailers, jobs, serializers, and decorators. Those require tests as well — ones closer to unit than integration tests. It can be useful to test out specifics regarding mailers and jobs.&lt;/p&gt;
&lt;h3&gt;Mailer Specs&lt;/h3&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://github.com/expectedbehavior/active_mailer&quot;&gt;ActiveMailer&lt;/a&gt;, Ruby on Rails has a simple way to abstract interactions with a third party that sends emails. Thus, instead of testing email sends, you can focus on testing what matters: behavior. In the case of emails, what matters is the subject, from and to addresses, and the body of the response.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;quot;rails_helper&amp;quot;

RSpec.describe Notifications, type: :mailer do
  describe &amp;quot;notify&amp;quot; do
    let(:mail) { Announcements.account_renewal }

    it &amp;quot;prepares the email headers properly&amp;quot; do
      expect(mail.subject).to eq(&amp;quot;Renewal&amp;quot;)
      expect(mail.to).to eq([&amp;quot;company@example.org&amp;quot;])
      expect(mail.from).to eq([&amp;quot;bot@example.com&amp;quot;])
    end

    it &amp;quot;renders the body&amp;quot; do
      expect(mail.body.encoded).to match(&amp;quot;A customer has renewed their account.&amp;quot;)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Job Specs&lt;/h3&gt;
&lt;p&gt;Similarly, job specs allow you to test specific behavior around jobs: if they have been enqueued, performed, etc. This includes testing arguments using &lt;a href=&quot;https://github.com/collectiveidea/delayed_job&quot;&gt;Delayed Job&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can be tempted to test ActiveJob&amp;#39;s behavior. We do know that calling &lt;code&gt;perform&lt;/code&gt; and &lt;code&gt;perform_later&lt;/code&gt; bill queues a job; what we want to know is if, and when, that happens.&lt;/p&gt;
&lt;p&gt;So, you should test if a job is queued in contexts requiring it. Then, test that a job properly calls upon code that has its own unit tests.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe Building do
  subject(:building) { Building.new(name: &amp;#39;Bank&amp;#39;) }

  describe &amp;#39;#setup&amp;#39; do
    it &amp;#39;triggers the preparation job&amp;#39; do
      ActiveJob::Base.queue_adapter = :test
      expect { building.setup }.to have_enqueued_job.with(&amp;#39;setup&amp;#39;).on_queue(&amp;#39;low&amp;#39;)
    end

   # alternative
   it &amp;#39;triggers the preparation job&amp;#39; do
     ActiveJob::Base.queue_adapter = :test
     expect(Building::PreparationJob).to have_been_enqueued.with(&amp;#39;setup&amp;#39;).exactly(:once)
   end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This expresses and tests a job&amp;#39;s enqueued behavior with certain parameters when a specific method is called.&lt;/p&gt;
&lt;h2&gt;A Note on Concerns in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Ruby on Rails has a way to factorize code used in multiple models or controllers through a &lt;a href=&quot;https://blog.appsignal.com/2020/09/16/rails-concers-to-concern-or-not-to-concern.html&quot;&gt;disguised Ruby module called a concern&lt;/a&gt;. Concerns are a great way to avoid duplication of code. Make sure you test the content of concerns. Proper unit tests should also cover models. In the case of controllers, those are tested through request and system tests.&lt;/p&gt;
&lt;h2&gt;Some Thoughts on Testing with RSpec for Ruby&lt;/h2&gt;
&lt;p&gt;It&amp;#39;s worth reiterating that your first layer of testing should be unit tests. Tests for models and other POROs should represent the vast majority of your tests in a code base.&lt;/p&gt;
&lt;p&gt;Tests should run fast and avoid database and external services access as much as possible. To test the controller layer, request and system specs are best, respectively, for machine or human-driven activity on controller actions. They are slower than unit tests due to their inherent complexity, so there should be less of them than unit tests.&lt;/p&gt;
&lt;p&gt;Finally, components like mailers and jobs should be used wisely and in their specific context, not to test the behavior of the lower library (like ActiveMailer and ActiveJob).&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;As we saw in part one of this series, we can factorize a lot of code and avoid duplication by making use of RSpec&amp;#39;s basics: &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; hooks, &lt;code&gt;let&lt;/code&gt;, and a proper structure built with &lt;code&gt;describe&lt;/code&gt; and &lt;code&gt;context&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this part, we took a deep dive into testing with RSpec for Ruby, focusing on unit, controller, and integration tests.&lt;/p&gt;
&lt;p&gt;We have explored how it&amp;#39;s usually easy to keep unit tests small, while minimizing request and system tests is more difficult. So, after you have written (and made green) system and request specs, don&amp;#39;t hesitate to spend some time slimming them down. This will keep them readable and easier to maintain.&lt;/p&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Behaviour Driven Development in Ruby with RSpec</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/01/24/behaviour-driven-development-in-ruby-with-rspec.html"/>
    <id>https://blog.appsignal.com/2024/01/24/behaviour-driven-development-in-ruby-with-rspec.html</id>
    <published>2024-01-24T00:00:00+00:00</published>
    <updated>2024-01-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll learn about RSpec and how it helps with Behaviour Driven Development in Ruby.</summary>
    <content type="html">&lt;p&gt;RSpec is a library for writing and running tests in Ruby applications. As its landing page states, RSpec is: &amp;quot;Behaviour Driven Development for Ruby. Making TDD productive and fun&amp;quot;. We will return to that last part later.&lt;/p&gt;
&lt;p&gt;This post, the first of a two-part series, will focus on introducing RSpec and exploring how RSpec especially helps with Behaviour Driven Development in Ruby.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive in!&lt;/p&gt;
&lt;h2&gt;The History of RSpec&lt;/h2&gt;
&lt;p&gt;RSpec started in 2005 and, through several iterations, reached version 3.0 in 2014. Version 3.12 has been available for almost a year. Its original author was Steven Baker, but the mantle has passed to several maintainers through the years: David Chelimsky, Myron Marston, Jon Rowe, and Penelope Phippen. Many contributors have also been part of the project.&lt;/p&gt;
&lt;p&gt;When you read RSpec&amp;#39;s good practices, you see this phrase early on: &amp;quot;focus on testing the behavior, not the implementation&amp;quot;. That&amp;#39;s not a strategy specific to RSpec; it&amp;#39;s good advice for anyone writing tests.
In more practical terms, you should focus your tests on how the code you are testing behaves, not how it works.&lt;/p&gt;
&lt;p&gt;For example, to test that a user&amp;#39;s email address is valid, you should test that the &lt;code&gt;validate_email&lt;/code&gt; method returns &lt;code&gt;false&lt;/code&gt; when the email address is invalid. You are not testing a specific implementation but rather how that code reacts (i.e., behaves) when handling different strings that should be email addresses.&lt;/p&gt;
&lt;p&gt;Behavior interests us: how the code acts and defines how our application will work. Furthermore, this approach simply lets us know whether things work (or not), and we can then be more relaxed to either fix or refactor the implementation. Our tests will tell us if the code&amp;#39;s behavior has changed; we will know if our changes have been successful or not in the most direct way possible.&lt;/p&gt;
&lt;p&gt;The inner workings of the code don&amp;#39;t interest us so much. Of course, we don&amp;#39;t want a bad implementation, but measuring a good implementation is much harder than knowing if the code does what it&amp;#39;s expected to do or not.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at how to install RSpec next.&lt;/p&gt;
&lt;h3&gt;Installing RSpec for Ruby&lt;/h3&gt;
&lt;p&gt;RSpec comes as a gem, &lt;code&gt;rspec-core&lt;/code&gt;, so you can add it to a test group in your Gemfile. It&amp;#39;s probably best you also add &lt;code&gt;rspec&lt;/code&gt;. It&amp;#39;s a meta gem that includes &lt;code&gt;rspec-core&lt;/code&gt;, &lt;code&gt;rspec-expectations&lt;/code&gt;, and &lt;code&gt;rspec-mocks&lt;/code&gt;. You can also add the &lt;code&gt;rspec-rails&lt;/code&gt; one in a Ruby on Rails project.&lt;/p&gt;
&lt;p&gt;Tests usually live in the &lt;code&gt;spec/&lt;/code&gt; folder at the root of your project, and you can launch them by providing a path to a file or a directory: &lt;code&gt;rspec spec/models/*rb&lt;/code&gt;, for example.&lt;/p&gt;
&lt;p&gt;Now let&amp;#39;s turn to how the RSpec DSL is set up to help with behavior testing.&lt;/p&gt;
&lt;h2&gt;RSpec DSL: How it Helps with Testing Behavior&lt;/h2&gt;
&lt;p&gt;RSpec&amp;#39;s whole Domain Specific Language (DSL) is completely worked around behavior testing, giving you a direct way to describe the behavior you expect from your code within different contexts. A few parts could be smoother, but overall, tests in RSpec read directly as English, much like a good piece of Ruby code.&lt;/p&gt;
&lt;p&gt;Tests in RSpec are not written as classes, with methods taking center stage as tests. Instead, tests are written as Ruby blocks (ever used &lt;code&gt;do .. end&lt;/code&gt;?), which, thanks to the method name we pass to the block as an argument, makes things very easy to read.&lt;/p&gt;
&lt;p&gt;The primary method used in RSpec tests is &lt;code&gt;describe&lt;/code&gt;. &lt;code&gt;describe&lt;/code&gt; will contain one or more tests and can even contain more &lt;code&gt;describe&lt;/code&gt; calls. The second method is &lt;code&gt;it&lt;/code&gt;. The &lt;code&gt;it&lt;/code&gt; blocks are called examples and contain the actual assumptions; they are where the testing happens. Finally, RSpec relies on &amp;quot;expectations&amp;quot; within the &lt;code&gt;it&lt;/code&gt; blocks. Using the &lt;code&gt;expect&lt;/code&gt; method, we define how the subject of our test is expected to behave.&lt;/p&gt;
&lt;p&gt;Now we can start writing some simple tests.&lt;/p&gt;
&lt;h2&gt;Simplest Tests in RSpec for Ruby: &lt;code&gt;describe&lt;/code&gt; and &lt;code&gt;it&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s imagine a &lt;code&gt;User&lt;/code&gt; class. We want a &lt;code&gt;name&lt;/code&gt; method that will output first and last names together if both are present (or just one, if one is missing). Those are the different behaviors we want to test.&lt;/p&gt;
&lt;p&gt;Here is how the simplest of those contexts look expressed as an RSpec test.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# first call to describe, as topmost one, its description or title is used to tell what we are testing, here the User class.
# this title can be a string or a class name
describe User do

  # we are adding a second describe to regroup the tests focused on the `name` method
  describe &amp;#39;#name&amp;#39; do

    # our first example ! Note the description focusing on the behavior
    it &amp;#39;returns the complete name&amp;#39; do

      # we define a user variable by instantiating a user
      user = User.new(first_name: &amp;#39;John&amp;#39;, last_name: &amp;#39;Doe&amp;#39;)

      # and here comes the expectation
      expect(user.name).to eq(&amp;#39;John Doe&amp;#39;)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at the general structure: we start by describing the focus of our test in the &lt;code&gt;User&lt;/code&gt; class, the method we are testing (&lt;code&gt;name&lt;/code&gt;), and then present an expected behavior.&lt;/p&gt;
&lt;p&gt;Note how the expectation is written: we &lt;strong&gt;expect&lt;/strong&gt; the value returned by &lt;code&gt;user.name&lt;/code&gt; &lt;strong&gt;to equal&lt;/strong&gt; &lt;code&gt;&amp;#39;John Doe&amp;#39;&lt;/code&gt;. The &lt;code&gt;eq&lt;/code&gt; method is a matcher. It allows us to match the tested value (on the left) and the expected one (on the right). The &lt;code&gt;expect&lt;/code&gt; part is always followed with &lt;code&gt;to&lt;/code&gt; or &lt;code&gt;not_to&lt;/code&gt; to dictate how the matcher that follows will be used.&lt;/p&gt;
&lt;h2&gt;Handling Multiple Contexts&lt;/h2&gt;
&lt;p&gt;While this first test shows us how it&amp;#39;s done, it needs to catch up to what we want. It only handles one case if both first and last names are present. Let&amp;#39;s see how we can test another case if the first name is absent.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe User do

  describe &amp;#39;#name&amp;#39; do
    it &amp;#39;returns the complete name when both first and last name are present&amp;#39; do
      user = User.new(first_name: &amp;#39;John&amp;#39;, last_name: &amp;#39;Doe&amp;#39;)
      expect(user.name).to eq(&amp;#39;John Doe&amp;#39;)
    end

    it &amp;#39;returns only the last name when the first name is missing&amp;#39; do
      user = User.new(last_name: &amp;#39;Doe&amp;#39;)
      expect(user.name).to eq(&amp;#39;Doe&amp;#39;)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;This looks more realistic, but we still need to test another case. The description is also relatively verbose and repetitive. We could add another layer of description between the &lt;code&gt;describe &amp;#39;.name&amp;#39;&lt;/code&gt; call and the example for each one. Thankfully, though, RSpec gives us a more obvious synonym for &lt;code&gt;describe&lt;/code&gt; to express what we need to express for different contexts: &lt;code&gt;context&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe User do

  describe &amp;#39;#name&amp;#39; do
    context &amp;#39;when both first and last name are present&amp;#39; do
      it &amp;#39;returns the complete name&amp;#39; do
        user = User.new(first_name: &amp;#39;John&amp;#39;, last_name: &amp;#39;Doe&amp;#39;)
        expect(user.name).to eq(&amp;#39;John Doe&amp;#39;)
      end
    end

    context &amp;#39;when the first name is missing&amp;#39; do
      it &amp;#39;returns only the last name&amp;#39; do
        user = User.new(last_name: &amp;#39;Doe&amp;#39;)
        expect(user.name).to eq(&amp;#39;Doe&amp;#39;)
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thanks to this, the whole file reads even more quickly and gives us, without much thinking, an understanding of exactly which behavior we are testing within different contexts.&lt;/p&gt;
&lt;h2&gt;Defining the Subject of Tests&lt;/h2&gt;
&lt;p&gt;We can use the &lt;code&gt;subject&lt;/code&gt; method to make the subject of our tests obvious.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe User do

  describe &amp;#39;#name&amp;#39; do
    subject { user.name }
    context &amp;#39;when both first and last name are present&amp;#39; do
      let(:user) { User.new(first_name: &amp;#39;John&amp;#39;, last_name: &amp;#39;Doe&amp;#39;) }

      it &amp;#39;returns the complete name&amp;#39; do
        expect(subject).to eq(&amp;#39;John Doe&amp;#39;)
      end
    end

    context &amp;#39;when only the first name is present&amp;#39; do
      let(:user) { User.new(first_name: &amp;#39;John&amp;#39;) }

      it &amp;#39;returns the complete name&amp;#39; do
        expect(subject).to eq(&amp;#39;John&amp;#39;)
      end
    end
    # ... other contexts
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is especially handy to avoid repetition and add clarity.&lt;/p&gt;
&lt;h2&gt;Handling Complex Setup (Before, After Hooks) in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;In many cases, we need a bit more to prepare a context. Let&amp;#39;s take, as an example, a class method on a Ruby on Rails model named &lt;code&gt;latest_three&lt;/code&gt;. It&amp;#39;s expected to return the last three users created in the database. If we have less than that, we should get whatever users we have. By omitting the topmost &lt;code&gt;describe&lt;/code&gt;, here is how a test might look.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# note that we are using the &amp;#39;::method_name&amp;#39; here to refer to a class method, &amp;#39;#method_name&amp;#39; is reserved to refer to an instance method&amp;#39;s name
describe &amp;#39;::latest_three&amp;#39; do
  context &amp;#39;when more than three users are present&amp;#39; do
    it &amp;#39;returns three users&amp;#39; do
      3.times { User.create(first_name: Faker::Name.first_name, last_name: Faker::Name.last_name) }

      expect(User.latest_three.size).to eq(3)
    end
  end

  context &amp;#39;when no users are present&amp;#39; to
    it &amp;#39;returns an empty collection&amp;#39; do
      expect(User.latest_three.empty?).to be(true)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are unfamiliar with &lt;code&gt;Faker&lt;/code&gt;, it&amp;#39;s a Ruby library used to generate fake data such as names and dates through handy methods.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These two tests look ok, but the creation of the data doesn&amp;#39;t belong to the example. It&amp;#39;s important for the specific context, though: we need that data created &lt;strong&gt;before&lt;/strong&gt; the example is run. To do so, we can use a &lt;code&gt;before&lt;/code&gt; block. Those blocks are run before the tests that follow them (in each block&amp;#39;s context), thus giving us a perfect opportunity to set up our data.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;#39;::latest_three&amp;#39; do
  context &amp;#39;when more than three users are present&amp;#39; do
    before do
      4.times { User.create(first_name: Faker::Name.first_name, last_name: Faker::Name.last_name) }
    end

    it &amp;#39;returns three users&amp;#39; do
      expect(User.latest_three.size).to eq(3)
    end
  end

  context &amp;#39;when no users are present&amp;#39; to
    it &amp;#39;returns an empty collection&amp;#39; do
      expect(User.latest_three.empty?).to be(true)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once again, I hope this shows how well thought-through the RSpec DSL is. Doesn&amp;#39;t it read nicely and give us a good understanding of each context and the behavior we expect?&lt;/p&gt;
&lt;p&gt;If we were tempted to destroy data or execute some other form of cleanup &lt;strong&gt;after&lt;/strong&gt; a context, we could do so through an &lt;code&gt;after&lt;/code&gt; block. This is especially useful if you are writing tests using a database without the comfort of built-in automatic database cleanup between test runs.&lt;/p&gt;
&lt;h2&gt;Avoiding Repetition with &lt;code&gt;let&lt;/code&gt; in RSpec for Ruby&lt;/h2&gt;
&lt;p&gt;We still lack a few more concepts to be able to write real-world tests. Let&amp;#39;s take the case of email validation again.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;#39;#valid_email?&amp;#39; do
  context &amp;#39;when email does not contain an @&amp;#39; do
    subject(:user) { User.new(email: &amp;#39;bob&amp;#39;) }

    it { expect(user.valid_email?).to be(false) }
  end

  context &amp;#39;when email does not have a tld&amp;#39; do
    subject(:user) { User.new(email: &amp;#39;bob@appsignal&amp;#39;) }

    it { expect(user.valid_email?).to be(false) }
  end

  context &amp;#39;when email is valid&amp;#39; do
    subject(:user) { User.new(email: &amp;#39;bob@example.org&amp;#39;) }

    it { expect(user.valid_email?).to be(true) }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are several repetitions, to the point of blurring the actual tests. We notice that the only thing that changes, in the setup of each context, is the actual value of the email address. Couldn&amp;#39;t we use a variable to do this?&lt;/p&gt;
&lt;p&gt;We can use &lt;code&gt;let&lt;/code&gt; blocks. They allow us to define a memoized helper method. The value is cached across multiple calls in the relative context. &lt;code&gt;let&lt;/code&gt;&amp;#39;s syntax is similar to the one we saw for the &lt;code&gt;subject&lt;/code&gt; block: first, we pass a name for the helper, then a block to be evaluated. That block is lazy-evaluated. If we don&amp;#39;t call it, it will never be evaluated.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;#39;#valid_email?&amp;#39; do
  subject(:user) { User.new(email: email) }

  context &amp;#39;when email does not contain an @&amp;#39; do
    let(:email) { &amp;#39;bob&amp;#39; }

    it { expect(user.valid_email?).to be(false) }
  end

  context &amp;#39;when email does not have a tld&amp;#39; do
    let(:email) { &amp;#39;bob@appsignal&amp;#39; }

    it { expect(user.valid_email?).to be(false) }
  end

  context &amp;#39;when email is valid&amp;#39; do
    let(:email) { &amp;#39;bob@example.org&amp;#39; }

    it { expect(user.valid_email?).to be(true) }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the &lt;code&gt;subject&lt;/code&gt; is moved up in the structure too: it will be evaluated within each context and thus use each context&amp;#39;s &lt;code&gt;email&lt;/code&gt; value. Here we can see the purpose of &lt;code&gt;subject&lt;/code&gt; within a &lt;code&gt;describe&lt;/code&gt; with multiple contexts: we define the subject of the test early to make it obvious. We can then focus on expressing each different context we want to check the subject behavior in.&lt;/p&gt;
&lt;h3&gt;A Note on &lt;code&gt;let&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;let&lt;/code&gt; is lazily defined. In the above example, &lt;code&gt;email&lt;/code&gt; won&amp;#39;t be instantiated and set until it&amp;#39;s called. Once it is invoked, though, it&amp;#39;s set. In effect, it&amp;#39;s just like a memoized helper method.&lt;/p&gt;
&lt;p&gt;Yet, in some cases, you might want to set the value associated with a &lt;code&gt;let&lt;/code&gt; before the examples run. To do so, you can use &lt;code&gt;let!&lt;/code&gt;. With &lt;code&gt;let!&lt;/code&gt;, the defined memoized helper method is called within an implicit &lt;code&gt;before&lt;/code&gt; hook for each example. In other words, the value associated with the &lt;code&gt;let!&lt;/code&gt; is eagerly defined before the example is run.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s create a user in our context before we run our example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;quot;#count_users&amp;quot; do
  let(:account) { Account.create(name: &amp;#39;Acc Ltd&amp;#39;) }
  let!(:user) { User.create(name: &amp;#39;Jane&amp;#39;, account: account) }

  it &amp;quot;counts the users in the account&amp;quot; do
    expect(account.count_users).to eq(1)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents us from additional setup or even a call to &lt;code&gt;user&lt;/code&gt; within the before hook to get the value memoized.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;let&lt;/code&gt; Vs Instance Variables in RSpec for Ruby&lt;/h3&gt;
&lt;p&gt;Some developers might be tempted to rely on instance variables through a &lt;code&gt;describe&lt;/code&gt; or &lt;code&gt;context&lt;/code&gt; and their &lt;code&gt;before&lt;/code&gt; hooks:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;quot;#count_users&amp;quot; do
  before do
    @account = Account.create(name: &amp;#39;Acc Ltd&amp;#39;)
    @user = User.create(name: &amp;#39;Jane&amp;#39;, account: @account)
  end

  it &amp;quot;counts the users in the account&amp;quot; do
    expect(@account.count_users).to eq(1)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not very practical. It adds dependencies and state sharing between contexts, weakens isolation, and is more difficult to debug.&lt;/p&gt;
&lt;p&gt;The additional issue is that, if you were to make a call to an instance variable that has not been initialized, you&amp;#39;d get a &lt;code&gt;nil&lt;/code&gt; value in return. That&amp;#39;s in contrast to the exception you&amp;#39;d get if you were to call a local variable that doesn&amp;#39;t exist (raising a &lt;code&gt;NameError&lt;/code&gt; exception).&lt;/p&gt;
&lt;p&gt;So, when writing tests with RSpec, &lt;code&gt;let&lt;/code&gt; is preferred, and &lt;code&gt;let!&lt;/code&gt; is to be used when you need an eager evaluation. Other methods to handle variables are not recommended.&lt;/p&gt;
&lt;h2&gt;Matchers in RSpec for Ruby&lt;/h2&gt;
&lt;p&gt;If &lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;context&lt;/code&gt;, and &lt;code&gt;it&lt;/code&gt; are very important to the structure of RSpec tests, the key part to making actual tests is matchers.&lt;/p&gt;
&lt;p&gt;We have only seen a few, mainly &lt;code&gt;be()&lt;/code&gt; and &lt;code&gt;eq()&lt;/code&gt;. Those two are the simplest ones and are very handy. Here is a list of the others you should know about as a start:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;eq&lt;/code&gt;: test the equality of two objects (actually, their equivalence, the same as &lt;code&gt;==&lt;/code&gt;); &lt;code&gt;expect(1).to eq(1.0) # is true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eql&lt;/code&gt;: test the equality of two objects (if they are identical, not just equivalent); &lt;code&gt;expect(1).to eql(1.0) # is false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;be&lt;/code&gt;: test for object identity; &lt;code&gt;be(true)&lt;/code&gt;, &lt;code&gt;be(false)&lt;/code&gt; ...&lt;/li&gt;
&lt;li&gt;&lt;code&gt;be_nil&lt;/code&gt;: test if an object is &lt;code&gt;nil&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;be &amp;lt;= X&lt;/code&gt;: test if a number is less or equal to a value (X); also works with &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;==&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;be_instance_of&lt;/code&gt;: test if an object is an instance of a specific class; &lt;code&gt;expect(user.name).to be_instance_of(String)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;include&lt;/code&gt;: test if an object is part of a collection; &lt;code&gt;expect([&amp;#39;a&amp;#39;, &amp;#39;b&amp;#39;]).to include(&amp;#39;a&amp;#39;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;be_empty&lt;/code&gt;: test if a collection is empty; &lt;code&gt;expect([]).to be_empty&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start_with&lt;/code&gt;, &lt;code&gt;end_with&lt;/code&gt;: test if a string or array starts (or ends) with the expected elements; &lt;code&gt;expect(&amp;#39;Brian is in the kitchen&amp;#39;).to start_with(&amp;#39;Brian&amp;#39;)&lt;/code&gt;, &lt;code&gt;expect([1, 2]).not_to start_with(&amp;#39;0&amp;#39;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match&lt;/code&gt;: test if a string matches a regular expression; &lt;code&gt;expect(user.name).to match(/[a-zA-Z0-9]*/)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;respond_to&lt;/code&gt;: test if an object responds to a particular method; &lt;code&gt;expect(user).to respond_to(:name)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;have_attributes&lt;/code&gt;: test if an object has a specific attribute; &lt;code&gt;expect(user).to have_attributes(age: 42)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;have_key&lt;/code&gt;: test if a key is present within a hash; &lt;code&gt;expect({ a: 1, b: 2 }).to have_key(:a)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;raise_error&lt;/code&gt;: test if a block of code raises an error; &lt;code&gt;expect { user.name }.to raise_error(ArgumentError)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;change&lt;/code&gt;: test that a block of code changes the value of an object or one of its attributes; &lt;code&gt;expect { User.create }.to change(User.count).by(1)&lt;/code&gt;, &lt;code&gt;expect { user.activate! }.to change(user, :is_active).from(false).to(true)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;to_all&lt;/code&gt;: test that all items in a collection match a given matcher; &lt;code&gt;expect([nil, nil, nil]).to all(be_nil)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match_array&lt;/code&gt;: test that one array has the same items as the expected one (the order isn&amp;#39;t of importance); &lt;code&gt;expect([1, 3, 2]).to match_array([2, 1, 3])&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can already write most of the tests you&amp;#39;ll ever need with those matchers. To read more about matchers, you can check out &lt;a href=&quot;https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/&quot;&gt;RSpec&amp;#39;s documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A Few Thoughts&lt;/h2&gt;
&lt;p&gt;As you have seen, we have yet to write a line of actual code; we just wrote tests. That might be the most crucial point of this article: RSpec&amp;#39;s DSL and structure allow you to write your test first from the behavior point of view. When you start to work on a new class, you can first express the behavior as an RSpec example within a given context. Then, simply rely on the guard rails to make your implementation a reality.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s actually how TDD works. We are not writing tests just for the sake of tests. Instead, we write tests to express the behavior we want to see from the code. In effect, those tests are merely a transcription (through RSpec DSL) of the behavior expected for a feature.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;To summarize what we have covered in this article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RSpec is a library that gives us a powerful DSL to express and test the behavior of code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;describe&lt;/code&gt; is the main element to structure tests in each file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;context&lt;/code&gt; is equivalent to &lt;code&gt;describe&lt;/code&gt;, but is used to separate different contexts for testing code behavior&lt;/li&gt;
&lt;li&gt;&lt;code&gt;it&lt;/code&gt; allows us to define examples: the blocks within which tests happen&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;expectations&lt;/strong&gt; define the actual tests with &lt;strong&gt;matchers&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;let&lt;/code&gt; and &lt;code&gt;let!&lt;/code&gt; allow us to define memoized helpers through custom-named blocks to avoid repetitions; &lt;code&gt;let!&lt;/code&gt; is eagerly loaded&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subject&lt;/code&gt; allows us to clearly define what is being tested and can be named&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the next post, we will look at specific types of tests for different parts of a Ruby on Rails application.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Speed Up Your Ruby on Rails Application with LiteCache</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/01/17/speed-up-your-ruby-on-rails-application-with-litecache.html"/>
    <id>https://blog.appsignal.com/2024/01/17/speed-up-your-ruby-on-rails-application-with-litecache.html</id>
    <published>2024-01-17T00:00:00+00:00</published>
    <updated>2024-01-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the fifth part of this series, we&#039;ll use LiteCache to speed up our Rails app.</summary>
    <content type="html">&lt;p&gt;In this series, we have looked at the &amp;quot;musts&amp;quot; (databases) and &amp;quot;shoulds&amp;quot; (asynchronous jobs, websockets) of a web application. Now we turn to one of the &amp;quot;coulds&amp;quot; (that is nonetheless recommended for scaling businesses): &lt;em&gt;caching&lt;/em&gt;. In particular, we mean caching &lt;em&gt;HTML fragments&lt;/em&gt; and other snippets of data, as referred to in &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html&quot;&gt;Rails Guides&lt;/a&gt;. We are &lt;em&gt;not&lt;/em&gt; concerned with HTTP or SQL query caching.&lt;/p&gt;
&lt;p&gt;In this part, we&amp;#39;ll see how to speed up our Rails app using LiteCache. But first, we&amp;#39;ll touch on Russian doll caching and how it comes in handy.&lt;/p&gt;
&lt;h2&gt;Russian Doll Caching in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;At first glance, it might seem counterintuitive to employ the same technology for the main database as well as the cache. After all, what speed improvements will that engender? This is where a technique called &amp;quot;Russian doll caching&amp;quot; comes in, which we will briefly shine a light on now.&lt;/p&gt;
&lt;p&gt;In one sentence, the gist of this caching approach boils down to:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fastest database query is no query.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What do I mean by that? Let&amp;#39;s turn to the &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#fragment-caching&quot;&gt;Rails Guides definition of fragment caching&lt;/a&gt; first:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In other words, when you wrap a piece of view code in a &lt;code&gt;cache&lt;/code&gt; helper, the &lt;em&gt;rendered&lt;/em&gt; HTML fragment is put into the cache store, with a unique, expirable cache key. This key is constructed to expire whenever either the entities of which the cache key is composed, or the underlying view template, change.&lt;/p&gt;
&lt;h3&gt;An Example&lt;/h3&gt;
&lt;p&gt;That may sound very unwieldy. Let&amp;#39;s look at an example in the context of our app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/prediction/_prediction.html.erb --&amp;gt;

+ &amp;lt;% cache prediction do %&amp;gt;
    &amp;lt;div id=&amp;quot;&amp;lt;%= dom_id(prediction) %&amp;gt;&amp;quot;&amp;gt;
      &amp;lt;%= turbo_stream_from prediction %&amp;gt;

      &amp;lt;% if prediction.prediction_image.present? %&amp;gt;
        &amp;lt;%= image_tag prediction.data_url %&amp;gt;
      &amp;lt;% else %&amp;gt;
        &amp;lt;sl-spinner style=&amp;quot;font-size: 8rem;&amp;quot;&amp;gt;&amp;lt;/sl-spinner&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;/div&amp;gt;
+ &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we have wrapped our &lt;code&gt;_prediction&lt;/code&gt; partial in a &lt;code&gt;cache&lt;/code&gt; block. This generates a cache key in the fashion of:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;view path                     template digest                  identifier
|                             |                                |
views/predictions/_prediction:9695008de61cf58325bbf974443f54bc/predictions/3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, this key comprises:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The view path, containing the &lt;code&gt;cache&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;A digest of the template (i.e., if you change the partial, the cache entry will be invalidated).&lt;/li&gt;
&lt;li&gt;A unique identifier — in other words, our &lt;code&gt;prediction&lt;/code&gt; — see the &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/Integration.html#method-i-cache_key&quot;&gt;ActiveRecord documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stored with this key is a timestamp (the &lt;code&gt;updated_at&lt;/code&gt; column of our &lt;code&gt;prediction&lt;/code&gt;) and the HTML fragment, in our case also the complete image&amp;#39;s data URL. Whenever this partial is rendered again, and a matching cache key is found, rendering is bypassed. Instead, the stored HTML is returned.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Making Use of Russian Doll Caching&lt;/h3&gt;
&lt;p&gt;What&amp;#39;s that &amp;quot;Russian doll&amp;quot; piece about, though? To answer this, let&amp;#39;s jump a layer higher into a view that renders this &lt;code&gt;_prediction&lt;/code&gt; partial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/prompts/_prompt.html.erb --&amp;gt;
+ &amp;lt;% cache prompt do %&amp;gt;
    &amp;lt;sl-card class=&amp;quot;card-header card-prompt&amp;quot; id=&amp;quot;&amp;lt;%= dom_id prompt %&amp;gt;&amp;quot;&amp;gt;
      &amp;lt;div slot=&amp;quot;header&amp;quot;&amp;gt;
        &amp;lt;!-- header content omitted --&amp;gt;
      &amp;lt;/div&amp;gt;

      &amp;lt;%= turbo_stream_from :predictions %&amp;gt;
      &amp;lt;div class=&amp;quot;grid&amp;quot;&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;%= image_tag prompt.data_url %&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div id=&amp;quot;&amp;lt;%= dom_id(prompt, :predictions) %&amp;gt;&amp;quot;&amp;gt;
          &amp;lt;%= render prompt.predictions %&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;

      &amp;lt;div slot=&amp;quot;footer&amp;quot;&amp;gt;
        &amp;lt;%= prompt.description.presence || &amp;quot;No Description&amp;quot; %&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/sl-card&amp;gt;
+ &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can apply the same technique to the &lt;code&gt;_prompt&lt;/code&gt; partial, which, in turn, renders a &lt;code&gt;_prediction&lt;/code&gt; partial for every prediction associated with it. This will result in a single HTML fragment comprising all the child fragments. &lt;strong&gt;We just saved one SQL query for each prediction!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There&amp;#39;s a catch, though: When the list of predictions associated with a prompt changes (for example, a new one is added), the top fragment doesn&amp;#39;t know about this and will serve &lt;em&gt;stale content&lt;/em&gt; (without the newly added image).&lt;/p&gt;
&lt;p&gt;In other words, we have to &lt;em&gt;expire&lt;/em&gt; its cache key and construct a fresh fragment. This is where the timestamp stored with each cache entry comes in handy. We can invalidate the cache simply by updating this timestamp. In ActiveRecord, luckily, there&amp;#39;s a shorthand for this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/prediction.rb
  class Prediction &amp;lt; ApplicationRecord
    # callbacks omitted

-   belongs_to :prompt
+   belongs_to :prompt, touch: true

    # methods omitted
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the &lt;code&gt;touch&lt;/code&gt; flag on the &lt;code&gt;belongs_to&lt;/code&gt; association, every time a child record (a prediction) is updated, deleted, or added, the &lt;code&gt;updated_at&lt;/code&gt; timestamp of the parent (a prompt) is refreshed. This marks the cache fragment as stale, and it is reconstructed upon the next render cycle.&lt;/p&gt;
&lt;p&gt;The term &amp;quot;Russian doll caching&amp;quot; in this context refers to the fact that all the valid child fragments can still be pulled from the cache, thus speeding up the rendering process.&lt;/p&gt;
&lt;p&gt;Now that we have reviewed how fragment caching works and what benefits it yields, let&amp;#39;s discuss how to enable and configure LiteCache.&lt;/p&gt;
&lt;h2&gt;LiteCache Configuration in Rails&lt;/h2&gt;
&lt;p&gt;In &lt;code&gt;config/environments/development.rb&lt;/code&gt;, add this configuration snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;config.cache_store = :litecache, {
  path: Litesupport.root.join(&amp;quot;cache.sqlite3&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will resolve the root database path depending on your environment (in this case, &lt;code&gt;db/development&lt;/code&gt;) and create a &lt;code&gt;cache.sqlite3&lt;/code&gt; file there. There are more configuration options worth considering, though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;size&lt;/code&gt;: the total allowed size of the cache database (the default being 128MB)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expiry&lt;/code&gt;: cache record expiry in days (the default is one month)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mmap_size&lt;/code&gt;: how large a portion of the database to hold in memory (default: 128MB)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;min_size&lt;/code&gt;: the minimum size of the database&amp;#39;s journal (default: 32KB)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sleep_interval&lt;/code&gt;: duration of sleep between cleanup runs (default: one second)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;metrics&lt;/code&gt;: boolean flag to indicate whether to gather metrics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The ones you will likely want to tweak to your liking are &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;expiry&lt;/code&gt;, and (potentially) &lt;code&gt;sleep_interval&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To enable caching in development, you have to run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails dev:cache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although LiteCache has many benefits, it comes with some drawbacks too. Let&amp;#39;s first look at some optimizations, and then a few of LiteCache&amp;#39;s limitations.&lt;/p&gt;
&lt;h2&gt;Optimizations and Limitations of LiteCache for Your Ruby App&lt;/h2&gt;
&lt;p&gt;LiteCache connects to the SQLite database in a way that&amp;#39;s optimized for use as a cache store. First, it&amp;#39;s important to reiterate that LiteCache is configured to use a &lt;em&gt;separate cache database&lt;/em&gt;, so all these optimizations only affect an isolated environment.&lt;/p&gt;
&lt;p&gt;With this disclaimer in place, let&amp;#39;s look at how LiteCache optimally utilizes SQLite.&lt;/p&gt;
&lt;p&gt;First, LiteCache sets the &lt;a href=&quot;https://www.sqlite.org/pragma.html#pragma_synchronous&quot;&gt;pragma statement&lt;/a&gt; &lt;code&gt;synchronous&lt;/code&gt; to 0, so there is no sync after a commit to the database. This results in a tremendous speedup at the expense of data safety. In very rare cases, such as a power loss or operating system crashes, data loss might occur. However, considering that cache entries are seen as ephemeral in most cases, this is a sensible tradeoff. Needless to say, you can also override this setting in your configuration.&lt;/p&gt;
&lt;p&gt;LiteCache also uses a &lt;em&gt;least recently used (LRU)&lt;/em&gt; eviction policy with a &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/1318e52101e874254490b43033580191466ba6a7/lib/litestack/litecache.sql.yml#L13-L14&quot;&gt;special index&lt;/a&gt;, but it delays updating it. Instead, it will buffer the updates in memory, and flush them as a single transaction every few seconds.&lt;/p&gt;
&lt;p&gt;What about limitations? At the time of writing this article, one crucial piece of the &lt;code&gt;ActiveSupport::Cache&lt;/code&gt; interface isn&amp;#39;t implemented yet: The ability to do &lt;strong&gt;multiple reads, writes, and deletes&lt;/strong&gt; against the cache database. Why is that so important? Because other cache backends like Redis demonstrate how significant speedups can be achieved by batching these operations. Indeed, the implicit performance gain built into rendering a collection of partials is the most impressive feat of Rails fragment caching.&lt;/p&gt;
&lt;p&gt;Speaking of performance speed, let&amp;#39;s now quickly look at some benchmarks before we wrap up.&lt;/p&gt;
&lt;h2&gt;Benchmarks: LiteCache Vs. Redis&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/master/BENCHMARKS.md&quot;&gt;benchmarks for LiteCache&lt;/a&gt; are impressive, with a small caveat. While LiteCache outperforms a local Redis installation &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/master/BENCHMARKS.md#read&quot;&gt;for every read operation&lt;/a&gt;, it seems like there&amp;#39;s still &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/master/BENCHMARKS.md#write&quot;&gt;room for improvement&lt;/a&gt;, especially for large write payloads.&lt;/p&gt;
&lt;p&gt;Considering the increased benefits of caching large HTML fragments, this is a worthwhile limitation that will hopefully be tackled in the future.&lt;/p&gt;
&lt;h2&gt;Up Next: Built-In Full-Text Search with LiteSearch&lt;/h2&gt;
&lt;p&gt;In this post, we illuminated Russian doll caching as a technique to speed up Rails applications by avoiding unnecessary database calls.&lt;/p&gt;
&lt;p&gt;Through practical examples, we’ve seen how nested cache fragments operate in harmony — each layer independent, yet interconnected — thus ensuring efficient rendering.&lt;/p&gt;
&lt;p&gt;We also delved into the practicalities of configuring LiteCache to your liking and looked at important built-in optimizations. With that in mind, the missing support for &lt;em&gt;multiple&lt;/em&gt; read and write operations is bearable, and perhaps is a feature that might soon arrive on the horizon.&lt;/p&gt;
&lt;p&gt;In the next and final post of this series, we will take a tour through the latest addition to LiteStack: A SQLite-based full-text search engine called &lt;em&gt;LiteSearch&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Keep your Ruby Code Maintainable with Draper</title>
    <link rel="alternate" href="https://blog.appsignal.com/2024/01/10/keep-your-ruby-code-maintainable-with-draper.html"/>
    <id>https://blog.appsignal.com/2024/01/10/keep-your-ruby-code-maintainable-with-draper.html</id>
    <published>2024-01-10T00:00:00+00:00</published>
    <updated>2024-01-10T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s dive into what Decorator is, how to use it, and explore using Decorators in Draper for a Ruby application.</summary>
    <content type="html">&lt;p&gt;Design patterns can help to simplify your codebase so you don&amp;#39;t need to reinvent the wheel.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll go into how to use Draper. But first, we will start with an overview of the decorator pattern and how to use it with Ruby&amp;#39;s standard library.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;The Decorator Pattern for Ruby&lt;/h2&gt;
&lt;p&gt;According to &lt;a href=&quot;https://refactoring.guru/design-patterns/decorator&quot;&gt;Refactoring.Guru&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Decorator can also be called a wrapper. The idea is that you initialize an instance of a decorator class by passing the object you want to decorate to that decorator class initializer.&lt;/p&gt;
&lt;p&gt;The new returned object can expose the same methods the original object did, plus a few more, or alter the original ones. In most cases (and in Draper&amp;#39;s case), we use this pattern to delegate calls to the embedded object.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dig into the Ruby standard library to look at the &lt;code&gt;SimpleDelegator&lt;/code&gt; class, itself a child of the &lt;code&gt;Delegator&lt;/code&gt; class. It&amp;#39;s a great way to see the decorator&amp;#39;s side of the pattern.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Company
  def founded_on
    Date.new(1981, 9, 10)
  end
end

class CompanyDecorator &amp;lt; SimpleDelegator
  def founding_year
    founded_on.year
  end
end

decorated_company = CompanyDecorator.new(Company.new)
decorated_company.founding_year  #=&amp;gt; 1981
decorated_company.__getobj__  #=&amp;gt; #&amp;lt;Company: ...&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One thing to note: the &lt;code&gt;decorated_company&lt;/code&gt; object has direct access to the public methods of the object it decorates.&lt;/p&gt;
&lt;p&gt;And here lies the principle behind the decorator: the &lt;code&gt;founded_on&lt;/code&gt; method, which can be an attribute pulled from the database, provides a key value for the company.&lt;/p&gt;
&lt;p&gt;To display the year the company was founded, we might be tempted to use &lt;code&gt;company.founded_on.year&lt;/code&gt;. This can work, yet we will slowly riddle the codebase with such calls. That is not practical; if we were to change the math or presentation of that value, we would need to change every single instance of that piece of code.&lt;/p&gt;
&lt;p&gt;This kind of presentation code doesn&amp;#39;t belong in the &lt;code&gt;Company&lt;/code&gt; class; in Ruby on Rails, it doesn&amp;#39;t belong in models either. If you have seen a few Rails projects, you probably have seen views littered with such presentation code. Decorators, delegators, or presenters are a better place for such presentation logic.&lt;/p&gt;
&lt;h2&gt;Using Decorators in Ruby on Rails Views, Models, and Controllers&lt;/h2&gt;
&lt;p&gt;Most, if not all, web applications need to display attributes of objects in different ways: names, dates, times, prices, numerical values, and coordinates. These uses are all over the place. Sometimes, we copy this kind of code in every view, use helper methods, or fill the model with methods that are just presenting data. Delegators are handy in moving all this logic into focused places.&lt;/p&gt;
&lt;p&gt;Helper methods are usually spread around in different places. They might be grouped, but only carry a little meaning beyond the simple method&amp;#39;s name. While models sound more appropriate, adding presentation methods tends to fatten them for little purpose. Any business logic in models or ActiveRecord-related code (associations, scopes, etc.) will be muddied by the presence of those methods.&lt;/p&gt;
&lt;p&gt;In short, in Ruby on Rails, decorators allow us to keep responsibilities limited in the model, the view, the decorator, and the controller. Here is how it could work (note that we have simplified the code a bit to stay concise):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/company.rb
class Company &amp;lt; ApplicationRecord
  def founded_on
    Date.new(1981, 9, 10)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/decorators/company_decorator.rb
class CompanyDecorator &amp;lt; SimpleDelegator
  def founding_year
    founded_on.year
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/companies_controller.rb
class CompaniesController &amp;lt; ApplicationController
  def show
    company = Company.find(params[:id])
    decorated_company = CompanyDecorator.new(company)

    render :show, locals: { company: decorated_company }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/companies/show.html.erb

&amp;lt;h1&amp;gt;&amp;lt;%= company.name %&amp;gt;&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;Founded in &amp;lt;%= company.founding_year %&amp;gt;&amp;lt;/h2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Using Draper in Your Ruby App&lt;/h2&gt;
&lt;p&gt;Draper is a bit more advanced than &lt;code&gt;SimpleDelegator&lt;/code&gt;. To install Draper, simply add the gem to the application&amp;#39;s Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;draper&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And run &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Decorators are usually written in the &lt;code&gt;app/decorators&lt;/code&gt; folder; you can also use namespaces within that folder. Namespaces and the decorator&amp;#39;s name should mirror the related models for the best results.&lt;/p&gt;
&lt;p&gt;Draper includes a couple of Ruby on Rails generators too. So when generating resources (&lt;code&gt;rails generate resource company&lt;/code&gt;), the related decorator will be created automatically.&lt;/p&gt;
&lt;p&gt;If you prefer to do it by hand, you can also call the &lt;code&gt;decorator&lt;/code&gt; generator with the model&amp;#39;s name: &lt;code&gt;rails generate decorator Company&lt;/code&gt;. By following this, you can rely on a bit of magic within your code to call &lt;code&gt;decorate&lt;/code&gt; on the &lt;code&gt;Company&lt;/code&gt; class instance and get the &lt;code&gt;CompanyDecorator&lt;/code&gt; instance for that company.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;decorated_company = Company.find(params[:id]).decorate
# equivalent to
company = Company.find(params[:id])
decorated_company = CompanyDecorator.new(company)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Draper also works with collections through the decorator&amp;#39;s &lt;code&gt;decorate_collection&lt;/code&gt; class method or the &lt;code&gt;decorate&lt;/code&gt; method on a collection of objects.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;decorated_companies = CompanyDecorator.decorate_collection(filtered_companies)
# or
decorated_companies = Company.recent.decorate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The decorator is similar to the one we saw with &lt;code&gt;SimpleDelegator&lt;/code&gt;; it relies on &lt;code&gt;Draper::Decorator&lt;/code&gt; instead. Methods of the decorated object are not directly accessible. Instead, we must call them through the &lt;code&gt;object&lt;/code&gt; or &lt;code&gt;model&lt;/code&gt; alias.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CompanyDecorator &amp;lt; Draper::Decorator
   def founding_year
    object.founded_on.year
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, if we want to directly call public methods of the decorated object with the decorator instance, we can define a list of such methods by using the &lt;code&gt;delegate&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CompanyDecorator &amp;lt; Draper::Decorator
  delegate :name

  def founding_year
    object.founded_on.year
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That way, we can use &lt;code&gt;decorated_company.name&lt;/code&gt;. This approach is also handy to limit the exposure of the object that&amp;#39;s being decorated from the view.&lt;/p&gt;
&lt;h2&gt;Example Draper Use Case: Rails View, Controller, and Model&lt;/h2&gt;
&lt;p&gt;A classic Draper use case relates to a User, Account, or Post model: they are often part of the core domain. The sheer quantity of business logic and presentation logic in those models can cause senior developers to frown and grab a new cup of coffee.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take a User model use case: always a good magnet for plenty of excess fat.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  # table would have the following columns
  #
  # first_name, String
  # last_name, String
  # date_of_birth, DateTime
  # company_id, Uuid
  # street_name, String
  # street_number, String
  # city, String
  # zipcode, String
  # country, String

  def age
    DateTime.current.years_ago(date_of_birth.year).year # Note: this is Ruby on Rails specific
  end

  def company_name
    company.name
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;None of those methods add any value to the model. They are misplaced or purely related to the presentation of data.&lt;/p&gt;
&lt;p&gt;The view might look something like the following.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/models/users/show.html.erb

&amp;lt;h1&amp;gt;&amp;lt;%= &amp;quot;#{user.first_name} #{user.last_name}&amp;quot; %&amp;gt;&amp;lt;/h1&amp;gt;
&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;Born &amp;lt;%= user.age %&amp;gt; years ago&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;Address: &amp;lt;%= &amp;quot;#{user.street_number} #{user.street_name}&amp;quot; %&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;City: &amp;lt;%= &amp;quot;#{user.zipcode} #{user.city} - #{user.country}&amp;quot; %&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;Company: &amp;lt;%= user.company_name %&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The controller is something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UsersController &amp;lt; ApplicationController
  def show
    user = User.find(params[:id])

    render :show, locals: { user: user }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, another view, within the cart display (for example), would make use of an address partial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/carts/_address.html.erb
&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;%= &amp;quot;#{user.street_number} #{user.street_name}&amp;quot; %&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;%= &amp;quot;#{user.zipcode} #{user.city} - #{user.country}&amp;quot; %&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A decorator can make things cleaner.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UserDecorator &amp;lt; Draper::Decorator
  delegate :company_name

  def age
    ((Time.now - object.date_of_birth.to_i) / 60 / 60 / 24 / 365).to_i
  end

  def address_line
    &amp;quot;#{object.street_number} #{object.street_name}&amp;quot;
  end

  def city_line
    &amp;quot;#{object.zipcode} #{object.city} - #{object.country}&amp;quot;
  end

  def name
    &amp;quot;#{object.first_name} #{object.last_name}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The controller then looks like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def show
  user = User.find(params[:id])

  render :show, locals: { user: user.decorate }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the view is simpler.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/models/users/show.html.erb

&amp;lt;h1&amp;gt;&amp;lt;%= user.name %&amp;gt;&amp;lt;/h1&amp;gt;
&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;Born &amp;lt;%= user.age %&amp;gt; years ago&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;Address: &amp;lt;%= user.street_line %&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;City: &amp;lt;%= user.city_line %&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;Company: &amp;lt;%= user.company_name %&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, it&amp;#39;s not so different either.&lt;/p&gt;
&lt;h2&gt;Decorators: When Not to Use Them in Your Ruby on Rails App&lt;/h2&gt;
&lt;p&gt;Decorators can be overused and end up poisoning your code base. You might stuff too much in them, causing complex queries and an overhead. Decorators could also end up mixed in with helper methods and undecorated classes. All in all, you may overuse Draper.&lt;/p&gt;
&lt;p&gt;Decorators, like any class, need to be focused and stick to one responsibility: presentation logic. If a method doesn&amp;#39;t fit, maybe it belongs somewhere else. Complex queries might appear when the &lt;code&gt;company_name&lt;/code&gt; method or something similar pulls data from a model and its associates.&lt;/p&gt;
&lt;p&gt;A similar issue can emerge when you decorate too many objects at once. After all, you basically double the number of objects if you decorate a whole collection. So, be aware of this and decorate when needed.&lt;/p&gt;
&lt;p&gt;Ruby on Rails developers tend to use a lot of helper methods. Some helper methods make sense but lack the object-oriented angle, in my opinion, and end up causing confusion. Relying on decorators will help limit the number of helper methods, but if you do use some, keep it light.&lt;/p&gt;
&lt;p&gt;Testing Ruby code is almost a standard, it&amp;#39;s definitely a good practice, and it also applies to Decorators. This will help, just like for any other class.&lt;/p&gt;
&lt;h2&gt;Preparing a Whole View with Decorator&lt;/h2&gt;
&lt;p&gt;There is a form of Decorator that is a bit more advanced: the view model, which can be done with Draper, SimpleDelegator, or even a plain old Ruby object (PORO). The concept is as follows: you create classes to prepare the data for a view from one or more objects. If we&amp;#39;re talking just one object, we have something similar to what we have seen. If it&amp;#39;s more than one, then it gets more crunchy.&lt;/p&gt;
&lt;p&gt;The idea is to gather the whole responsibility of &amp;quot;data presentation&amp;quot; into one class, even for different objects at once. This is a case where we might cause complex queries, so don&amp;#39;t hesitate to use &lt;code&gt;includes&lt;/code&gt; and &lt;code&gt;preload&lt;/code&gt; methods with queries to eager load associations as needed.&lt;/p&gt;
&lt;p&gt;That way, instead of passing several objects to the view from the controller, you can prepare just one, and then access the data — ready to be used — from properly named methods.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we ran through the basics of Decorator and how to use it, before diving into how to use Decorators in Draper for a Ruby on Rails application. We finally touched on when to avoid using decorators and how to prepare a whole view with Decorator.&lt;/p&gt;
&lt;p&gt;Draper is one of those gems you&amp;#39;ll wish you had discovered before. It gives wings to Decorators. It&amp;#39;s also the kind of nicely integrated library that will not only help you keep your code clean by setting clear limits between responsibilities, but also provide you with custom grammar and magic that fits the Ruby on Rails ecosystem like a glove.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>AppSignal’s Top 5 Ruby Posts in 2023</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/12/20/appsignals-top-5-ruby-posts-in-2023.html"/>
    <id>https://blog.appsignal.com/2023/12/20/appsignals-top-5-ruby-posts-in-2023.html</id>
    <published>2023-12-20T00:00:00+00:00</published>
    <updated>2023-12-20T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s look back at our top 5 Ruby posts this year.</summary>
    <content type="html">&lt;p&gt;As the year draws to an end, we&amp;#39;re excited to share our top five most-read Ruby articles of 2023!&lt;/p&gt;
&lt;h2&gt;Top 5 Ruby Blog Posts in 2023 💎&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2023/03/01/making-the-most-of-your-logs-in-rails.html&quot;&gt;Making the Most of Your Logs in Rails&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&amp;#39;s easy to get going with logging with Rails, but not so easy to master it. Discover how you can get the most from your logs.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2023/05/10/organize-business-logic-in-your-ruby-on-rails-application.html&quot;&gt;Organize Business Logic in Your Ruby on Rails Application&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the first part of this two-part series, we run through popular methods to organize your business logic.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2023/07/26/an-introduction-to-metaprogramming-in-ruby.html&quot;&gt;An Introduction to Metaprogramming in Ruby&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Explore some of the fundamentals of metaprogramming in Ruby.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2023/09/27/an-introduction-to-litestack-for-ruby-on-rails.html&quot;&gt;An Introduction to LiteStack for Ruby on Rails&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the first part of this series, we set up an example Rails application and introduce the basics of LiteStack.&lt;/p&gt;
&lt;p&gt;Without further ado, our top-performing Ruby post in 2023 was, perhaps unsurprisingly:&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://blog.appsignal.com/2023/02/15/whats-new-in-rails-7-1.html&quot;&gt;What&amp;#39;s New in Rails 7.1&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this post, we discuss some noteworthy additions coming to Rails 7.1.&lt;/p&gt;
&lt;h2&gt;Season&amp;#39;s Greetings ❆ ⛄&lt;/h2&gt;
&lt;p&gt;Have a wonderful festive break, and we&amp;#39;ll see you in the new year!&lt;/p&gt;
&lt;p&gt;If you haven’t already, don’t forget to &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter&lt;/a&gt;, so you never miss an upcoming post.&lt;/p&gt;
&lt;p&gt;See you in the new year!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Stream Updates to Your Users with LiteCable for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/12/13/stream-updates-to-your-users-with-litecable-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2023/12/13/stream-updates-to-your-users-with-litecable-for-ruby-on-rails.html</id>
    <published>2023-12-13T00:00:00+00:00</published>
    <updated>2023-12-13T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the fourth part of this series, we&#039;ll use LiteCable for WebSockets and stream updates to users via Turbo Streams.</summary>
    <content type="html">&lt;p&gt;So far in this series, we have been exploring the capabilities of SQLite for classic HTTP request/response type usage. In this post, we will push the boundary further by also using SQLite as a Pub/Sub adapter for ActionCable, i.e., WebSockets.&lt;/p&gt;
&lt;p&gt;This is no small feat: WebSocket adapters need to handle thousands of concurrent connections performantly. The emergence of alternatives to ActionCable — read AnyCable — bears witness to the fact that this is a pressing concern for modern web applications. We&amp;#39;ll take a look at how SQLite performs under these conditions.&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s set up our app to broadcast streaming updates to users via Turbo Streams.&lt;/p&gt;
&lt;h2&gt;Configure Your Ruby on Rails App to Use LiteCable for Websockets&lt;/h2&gt;
&lt;p&gt;Configuring your application to use &lt;em&gt;LiteCable&lt;/em&gt; for WebSocket connections is as easy as specifying the adapter in &lt;code&gt;config/cable.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;development:
  adapter: litecable

test:
  adapter: test

staging:
  adapter: litecable

production:
  adapter: litecable
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Preparing Our Rails App for Live Updates&lt;/h2&gt;
&lt;p&gt;Before we dive deep into how to broadcast model updates using Turbo Rails, we need to rework the mechanics of creating a prediction.&lt;/p&gt;
&lt;p&gt;First, we&amp;#39;ll create an empty prediction in &lt;code&gt;GenerateImageJob&lt;/code&gt; to display a placeholder in our &lt;code&gt;_prompt&lt;/code&gt; partial. This has the added benefit of forwarding the actual prediction&amp;#39;s SGID to the webhook. Note, though, that we also have to pass the account&amp;#39;s SGID, because the incoming webhook doesn&amp;#39;t have any session information about the currently active user.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/jobs/generate_image_job.rb

  class GenerateImageJob &amp;lt; ApplicationJob
    include Rails.application.routes.url_helpers

    queue_as :default

    def perform(prompt:)
+     empty_prediction = prompt.predictions.create

      model = Replicate.client.retrieve_model(&amp;quot;stability-ai/stable-diffusion-img2img&amp;quot;)
      version = model.latest_version
      version.predict({prompt: prompt.title, image: prompt.data_url},
        replicate_rails_url(host: Rails.application.config.action_mailer.default_url_options[:host],
-         params: {sgid: prompt.to_sgid.to_s}))
+         params: {prediction: empty_prediction.to_sgid.to_s,
+         account: prompt.account.to_sgid.to_s}))
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In parallel, in our &lt;code&gt;ReplicateWebhook&lt;/code&gt;, we can locate and simply update the prediction. Note that we have to set the &lt;code&gt;Current.account&lt;/code&gt; because &lt;code&gt;Prompt&lt;/code&gt; is scoped to an account and would otherwise end up empty (due to the way &lt;code&gt;AccountScoped&lt;/code&gt; is set up).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # config/initializers/replicate.rb

  class ReplicateWebhook
    def call(prediction)
      query = URI(prediction.webhook).query

-     sgid = CGI.parse(query)[&amp;quot;sgid&amp;quot;].first
+     prediction_sgid = CGI.parse(query)[&amp;quot;prediction&amp;quot;].first
+     account_sgid = CGI.parse(query)[&amp;quot;account&amp;quot;].first

-     prompt = GlobalID::Locator.locate_signed(sgid)
+     located_prediction = GlobalID::Locator.locate_signed(sgid)
+     Current.account = GlobalID::Locator.locate_signed(account_sgid)

-     prompt.predictions.create(
+     located_prediction.update(
        prediction_image: URI.parse(prediction.output.first).open.read,
        replicate_id: prediction.id,
        replicate_version: prediction.version,
        logs: prediction.logs
      )
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;This change entails that the created prediction is empty, i.e., has no prediction image (obviously). Let&amp;#39;s cater for this by adding a conditional to our &lt;code&gt;_prompt.html.erb&lt;/code&gt; partial. When the image is missing, we display a spinner:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/prompts/_prompt.html.erb --&amp;gt;

  &amp;lt;p&amp;gt;
    &amp;lt;strong&amp;gt;Generated images:&amp;lt;/strong&amp;gt;
    &amp;lt;% prompt.predictions.each do |prediction| %&amp;gt;
+     &amp;lt;% if prediction.prediction_image.present? %&amp;gt;
        &amp;lt;%= image_tag prediction.data_url %&amp;gt;
+     &amp;lt;% else %&amp;gt;
+       &amp;lt;sl-spinner style=&amp;quot;font-size: 8rem;&amp;quot;&amp;gt;&amp;lt;/sl-spinner&amp;gt;
+     &amp;lt;% end %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great, we&amp;#39;re done preparing our app to deliver live updates. Let&amp;#39;s implement Turbo-Rails model broadcasts to finish this proof of concept.&lt;/p&gt;
&lt;h2&gt;Delivering Prediction Updates Live with Turbo-Rails&lt;/h2&gt;
&lt;p&gt;To test the WebSocket capabilities of LiteStack, we are going to use &lt;code&gt;Turbo::Broadcastable&lt;/code&gt;. We&amp;#39;d like to show the spinner and the generated image once it has been created.&lt;/p&gt;
&lt;p&gt;The way to do that is quite idiomatic: We tie this to &lt;code&gt;after_create_commit&lt;/code&gt; and &lt;code&gt;after_update_commit&lt;/code&gt; model callbacks invoking one of &lt;code&gt;Turbo::Broadcastable&lt;/code&gt;&amp;#39;s &lt;a href=&quot;https://www.rubydoc.info/github/hotwired/turbo-rails/Turbo/Broadcastable&quot;&gt;broadcast methods&lt;/a&gt;. Before we can do that, though, let&amp;#39;s separate out a model partial for &lt;code&gt;Prediction&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/predictions/_prediction.html.erb --&amp;gt;
&amp;lt;%= turbo_stream_from prediction %&amp;gt;

&amp;lt;div id=&amp;quot;&amp;lt;%= dom_id(prediction) %&amp;gt;&amp;quot;&amp;gt;
  &amp;lt;% if prediction.prediction_image.present? %&amp;gt;
    &amp;lt;%= image_tag prediction.data_url %&amp;gt;
  &amp;lt;% else %&amp;gt;
    &amp;lt;sl-spinner style=&amp;quot;font-size: 8rem;&amp;quot;&amp;gt;&amp;lt;/sl-spinner&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Observe that I added a &lt;code&gt;turbo_stream_from&lt;/code&gt; tag to the partial, containing the stream identifier and subscribing to the channel. We can now simply call &lt;code&gt;render&lt;/code&gt; from the prompt partial and add another &lt;code&gt;turbo_stream_from&lt;/code&gt; to listen for changes to the prediction list:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/prompts/_prompt.html.erb --&amp;gt;

  &amp;lt;p&amp;gt;
    &amp;lt;strong&amp;gt;Generated images:&amp;lt;/strong&amp;gt;
-   &amp;lt;% prompt.predictions.each do |prediction| %&amp;gt;
-     &amp;lt;% if prediction.prediction_image.present? %&amp;gt;
-       &amp;lt;%= image_tag prediction.data_url %&amp;gt;
-     &amp;lt;% else %&amp;gt;
-       &amp;lt;sl-spinner style=&amp;quot;font-size: 8rem;&amp;quot;&amp;gt;&amp;lt;/sl-spinner&amp;gt;
-     &amp;lt;% end %&amp;gt;
-   &amp;lt;% end %&amp;gt;
+   &amp;lt;%= turbo_stream_from :predictions %&amp;gt;
+   &amp;lt;div id=&amp;quot;&amp;lt;%= dom_id(prompt, :predictions) %&amp;gt;&amp;quot;&amp;gt;
+     &amp;lt;%= render prompt.predictions %&amp;gt;
+   &amp;lt;/div&amp;gt;
  &amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&amp;#39;re ready to set up model broadcasts. In the &lt;code&gt;Prediction&lt;/code&gt; class, we add two model callbacks, invoking two Turbo Stream actions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/prediction.rb

  class Prediction &amp;lt; ApplicationRecord
+   include ActionView::RecordIdentifier

+   after_create_commit -&amp;gt; { broadcast_append_later_to :predictions,
+     target: dom_id(prompt, :predictions) }
+   after_update_commit -&amp;gt; { broadcast_replace_later_to self }

    belongs_to :prompt

    def data_url
      encoded_data = Base64.strict_encode64(prediction_image)

      &amp;quot;data:image/png;base64,#{encoded_data}&amp;quot;
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;What&amp;#39;s Happening Here?&lt;/h3&gt;
&lt;p&gt;First, when a prediction is created, we &lt;em&gt;append&lt;/em&gt; it to the predictions list. This will show our loading spinner once &lt;code&gt;GenerateImageJob&lt;/code&gt; has run.&lt;/p&gt;
&lt;p&gt;Then, every update to the record will trigger a &lt;em&gt;replace&lt;/em&gt; of the prediction partial. Once the prediction is updated in &lt;code&gt;ReplicateWebhook&lt;/code&gt;, the image returned from Replicate displays.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s what this looks like (note that I&amp;#39;m using &lt;a href=&quot;https://shoelace.style/&quot;&gt;Shoelace components&lt;/a&gt; for styling purposes):&lt;/p&gt;
&lt;Video fileName=&quot;/images/blog/2023-12/image-prediction&quot; /&gt;

&lt;h2&gt;Benchmarks: LiteCable Vs. Redis&lt;/h2&gt;
&lt;p&gt;So far, this article has shown that it&amp;#39;s &lt;em&gt;possible&lt;/em&gt; to run ActionCable with LiteCable as its adapter. This is a nice proof of concept, but we&amp;#39;re here to check how LiteStack &lt;em&gt;compares&lt;/em&gt; to other adapters as well.&lt;/p&gt;
&lt;p&gt;Luckily, the &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/master/BENCHMARKS.md&quot;&gt;official LiteStack benchmarks&lt;/a&gt; include measurements for LiteCable against Redis, which I am going to quote here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here&amp;#39;s a small but important caveat:&lt;/strong&gt; &lt;em&gt;All these measurements were performed on the same machine. In typical production setups with managed Redis, you&amp;#39;ll have to factor in additional network latency.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at the &lt;strong&gt;requests per second&lt;/strong&gt; metric first. This captures how many Pub/Sub requests the Redis and SQLite processes are able to serve.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requests&lt;/th&gt;
&lt;th&gt;Redis requests/second&lt;/th&gt;
&lt;th&gt;LiteStack requests/second&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;2611&lt;/td&gt;
&lt;td&gt;3058&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;3110&lt;/td&gt;
&lt;td&gt;5328&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;3403&lt;/td&gt;
&lt;td&gt;5385&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Note that LiteStack is able to process more requests per second, but tapers off with higher loads. Though not representative, this might be an issue for loads of 1M requests and beyond — but that&amp;#39;s when you&amp;#39;ll typically reach for faster solutions like &lt;a href=&quot;https://anycable.io/&quot;&gt;AnyCable&lt;/a&gt; over stock ActionCable anyway.&lt;/p&gt;
&lt;p&gt;Furthermore, there are some &lt;strong&gt;latency tests&lt;/strong&gt; included in the benchmarks.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requests&lt;/th&gt;
&lt;th&gt;Redis p90 Latency&lt;/th&gt;
&lt;th&gt;LiteStack p90 Latency&lt;/th&gt;
&lt;th&gt;Redis p99 Latency&lt;/th&gt;
&lt;th&gt;LiteStack p99 Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;153&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;138&lt;/td&gt;
&lt;td&gt;122&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;153&lt;/td&gt;
&lt;td&gt;235&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Allowing for some inaccuracy of measurement, both perform equivalently in this regard, maybe with the exception of the 99 percentile. Here, SQLite&amp;#39;s locking model interferes with the amount of concurrent requests.&lt;/p&gt;
&lt;p&gt;Again keep in mind, though, that you&amp;#39;ll have to add a couple of milliseconds of latency once Redis runs on a different machine (LiteCable always runs on the same machine by design).&lt;/p&gt;
&lt;h2&gt;Limitations of SQLite for Rails&lt;/h2&gt;
&lt;p&gt;It is fair to assume that once you hit a certain level of Pub/Sub activity, you&amp;#39;ll reach the ceiling of what&amp;#39;s possible with a single SQLite database. That&amp;#39;s the moment when you&amp;#39;ll have to think about sharding, and here other technologies like Redis have a head start — though it will be interesting to see what &lt;a href=&quot;https://fly.io/docs/litefs/&quot;&gt;LiteFS&lt;/a&gt; will have to offer.&lt;/p&gt;
&lt;p&gt;Continuous monitoring of your app&amp;#39;s WebSocket performance metrics using &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;tools like AppSignal&lt;/a&gt; is your friend here. Reusing the ActionCable consumer on the client side is also advisable, as it will prevent wasting Pub/Sub connections.&lt;/p&gt;
&lt;p&gt;LiteCable is tailored for vertical scaling by a tight integration of components. If you extract maximum performance from the SQLite engine, the limits of this approach are pushed a lot further. Once you observe that your latencies start to explode, though, I would suggest researching options like &lt;a href=&quot;https://anycable.io/&quot;&gt;AnyCable&lt;/a&gt;, which inherently provide better strategies for horizontal scaling.&lt;/p&gt;
&lt;h2&gt;Up Next: Speed Up Rails App Rendering with LiteCache&lt;/h2&gt;
&lt;p&gt;In this post, we explored using SQLite as a Pub/Sub adapter for ActionCable to enable real-time updates in a Rails application via WebSockets. Configuring LiteCable was straightforward, requiring just a simple adapter specification. Leveraging &lt;code&gt;Turbo::Broadcastable&lt;/code&gt; model callbacks made our implementation clean, tying broadcasts to creation and updates.&lt;/p&gt;
&lt;p&gt;Though powerful, LiteCable is not designed to scale across multiple processes or servers. But for single-machine deployments, it unlocks real-time features in Rails without requiring a separate Redis instance.&lt;/p&gt;
&lt;p&gt;Our next post will look at the next puzzle piece in LiteStack: the ActiveSupport cache store it provides. We&amp;#39;ll test out how it can help us to lower server response times, and look at some benchmarks again.&lt;/p&gt;
&lt;p&gt;See you then!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Use Shoulda Matchers with RSpec for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/12/06/how-to-use-shoulda-matchers-with-rspec-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2023/12/06/how-to-use-shoulda-matchers-with-rspec-for-ruby-on-rails.html</id>
    <published>2023-12-06T00:00:00+00:00</published>
    <updated>2023-12-06T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn how to test functionality in your Rails app by using shoulda-matchers with RSpec.</summary>
    <content type="html">&lt;p&gt;When writing tests in Rails, you should avoid repetition and have the right amount of tests to satisfy your use case.&lt;/p&gt;
&lt;p&gt;This article will introduce you to shoulda-matchers with RSpec for testing functionality in Rails. At the end of the post, you should feel confident about using shoulda-matchers in your Rails application.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Go ahead and clone the &lt;a href=&quot;https://github.com/kinsomicrote/appsignal-shoulda-matchers&quot;&gt;repository of this starter Rails app&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;starter-code&lt;/code&gt; branch has the following gems installed and set up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rspec/rspec-rails&quot;&gt;RSpec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/thoughtbot/factory_bot_rails&quot;&gt;Factory Bot Rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DatabaseCleaner/database_cleaner-active_record&quot;&gt;Database Cleaner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/faker-ruby/faker&quot;&gt;Faker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Shoulda Matchers for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://github.com/thoughtbot/shoulda-matchers&quot;&gt;shoulda-matchers documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Shoulda Matchers provides RSpec- and Minitest-compatible one-liners to test common Rails functionality that, if written by hand, would be much longer, more complex, and error-prone.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;#39;s see how &lt;code&gt;shoulda-matchers&lt;/code&gt; will look before installing and using them. Our repository has an &lt;code&gt;Author&lt;/code&gt; and &lt;code&gt;Book&lt;/code&gt; model. We&amp;#39;ll add name validation to the &lt;code&gt;Author&lt;/code&gt; model without &lt;code&gt;shoulda-matchers&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;RSpec.describe Author, type: :model do
  describe &amp;quot;validations&amp;quot; do
    it &amp;quot;is invalid with invalid attributes&amp;quot; do
      expect(build(:author, name: &amp;#39;&amp;#39;)).to_not be_valid
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above, we build an &lt;code&gt;author&lt;/code&gt; record without a &lt;code&gt;name&lt;/code&gt;, and we expect it to be invalid. If we validate the &lt;code&gt;name&lt;/code&gt;&amp;#39;s presence in our &lt;code&gt;Author&lt;/code&gt; model, this spec should pass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;While we’ll cover shoulda-matchers with RSpec in this post, you can use other frameworks like Minitest instead.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Installation of shoulda-matchers Gem for Ruby on Rails&lt;/h3&gt;
&lt;p&gt;Add the shoulda-matchers gem to the &lt;code&gt;test&lt;/code&gt; group in your Gemfile. It should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;group :test do
  gem &amp;#39;shoulda-matchers&amp;#39;, &amp;#39;~&amp;gt; 5.0&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run &lt;code&gt;bundle install&lt;/code&gt; to install the gem. Next, place the code snippet below at the bottom of the &lt;code&gt;spec/rails_helper.rb&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we specify the test framework and library we’ll be using.&lt;/p&gt;
&lt;p&gt;Now we&amp;#39;ll dive into our Active Model spec.&lt;/p&gt;
&lt;h3&gt;Active Model Spec in Rails&lt;/h3&gt;
&lt;p&gt;Your Active Model spec might consist entirely of validations similar to the spec above, which shoulda-matchers handles for you. You’ll want to test validating the presence or length of certain attributes. For example, in the sample app we have above, it’s important to validate the &lt;code&gt;name&lt;/code&gt; presence for the author model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;quot;validations&amp;quot; do
  it { should validate_presence_of(:name) }
  it { should validate_length_of(:name).is_at_least(2)}
  it { should validate_length_of(:name).is_at_most(50)}
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we validate the presence and length of &lt;code&gt;name&lt;/code&gt;. You can see that these validations are one-liners compared to the initial spec we created when we didn’t use shoulda-matchers.
The opposite of &lt;code&gt;presence&lt;/code&gt; is &lt;code&gt;absence&lt;/code&gt;, so we can validate that an attribute is absent like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should validate_absence_of(:name) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&amp;#39;s another validation spec:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should validate_numericality_of(:publication_year).is_greater_than_or_equal_to(1800) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above, we test whether &lt;code&gt;publication_year&lt;/code&gt; is a numerical value and if it’s greater than or equal to &lt;code&gt;1800&lt;/code&gt;. We can modify the comparison to look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should validate_comparison_of(:publication_year).greater_than(1800) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This assumes we intend to make use of &lt;code&gt;validate_comparison_of&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also test for &lt;code&gt;validate_exclusion_of&lt;/code&gt; (and its opposite, &lt;code&gt;validate_inclusion_of&lt;/code&gt;) like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should validate_exclusion_of(:username).in_array([&amp;#39;admin&amp;#39;, &amp;#39;superadmin&amp;#39;]) }
it { should validate_inclusion_of(:country).in_array([&amp;#39;Nigeria&amp;#39;, &amp;#39;Ghana&amp;#39;]) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s say you need to validate a &lt;code&gt;password&lt;/code&gt; confirmation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should validate_confirmation_of(:password) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll want to validate that an attribute has been accepted where necessary. This comes in handy when dealing with &lt;code&gt;terms_of_service&lt;/code&gt;, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should validate_acceptance_of(:terms_of_service) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next up, let&amp;#39;s turn our attention to the Active Record spec.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Active Record Spec in Rails&lt;/h3&gt;
&lt;p&gt;In some cases, you’ll want to validate an attribute&amp;#39;s uniqueness. This one-liner handles that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it {  should validate_uniqueness_of(:title) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can take it a bit further using scope:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it {  should validate_uniqueness_of(:title).scoped_to(:author_id) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will check that you have a uniqueness validation for the &lt;code&gt;title&lt;/code&gt; attribute, but scoped to &lt;code&gt;author_id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can also test the relationship between authors and books. Let&amp;#39;s say an author is supposed to have many books.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;quot;association&amp;quot; do
  it { should have_many(:books)}
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This spec will pass if we have the relationship specified in the author model. Then, for the book model, we can have a &lt;code&gt;belongs_to&lt;/code&gt; spec:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should belong_to(:author) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are also one-line specs for other associations you might want to test:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should have_one(:delivery_address) }
it { should have_one_attached(:avatar) }
it { should have_many_attached(:pictures) }
it { should have_and_belong_to_many(:publishers) }
it { should have_rich_text(:description) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want, you can test that there are specific columns in your database:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should have_db_column(:title) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can take it further to test for the column type:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should have_db_column(:title).of_type(:string) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is also the option of testing for an index:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should have_db_index(:name) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if you have a composite index:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should have_db_index([:author_id, :title]) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use &lt;code&gt;implicit_order_column&lt;/code&gt; in Rails v6+ to define the custom column for implicit ordering:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;self.implicit_order_column = &amp;quot;updated_at&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we specify that we want the &lt;code&gt;updated_at&lt;/code&gt; column to handle ordering. So when we run &lt;code&gt;Book.first&lt;/code&gt;, Rails will use the &lt;code&gt;updated_at&lt;/code&gt; column instead of the &lt;code&gt;id&lt;/code&gt;. By default, Rails uses the &lt;code&gt;id&lt;/code&gt; to order records.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shoulda-matchers&lt;/code&gt; has a one-liner test for this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should have_implicit_order_column(:updated_at) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we have an enum for our model (like &lt;code&gt;enum status: [:published, :unpublished]&lt;/code&gt;), we can write this test:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should define_enum_for(:status) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can specify the test values:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should define_enum_for(:status).with_values([:published, :unpublished]) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have a read-only attribute, you can also test for that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should have_readonly_attribute(:genre) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you can test for &lt;code&gt;accepts_nested_attributes_for&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should accept_nested_attributes_for(:publishers) }
it { should accept_nested_attributes_for(:publishers).allow_destroy(true) }
it { should accept_nested_attributes_for(:publishers).update_only(true) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above tests depend on the use case defined in your model. You can check the &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html&quot;&gt;Rails API Documentation&lt;/a&gt; if you’re unsure how &lt;code&gt;accept_nested_attributes_for&lt;/code&gt; works.&lt;/p&gt;
&lt;p&gt;There are also options for testing that your records are serialized when you use the &lt;code&gt;serialize&lt;/code&gt; macro:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should serialize(:books) }
it { should serialize(:books).as(BooksSerializer) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we test that &lt;code&gt;books&lt;/code&gt; is serialized. We specify the exact serializer that we expect to use with &lt;code&gt;as&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;#39;s turn to the Action Controller spec.&lt;/p&gt;
&lt;h3&gt;Action Controller Spec in Rails&lt;/h3&gt;
&lt;p&gt;Moving on to params, let&amp;#39;s use &lt;code&gt;config.filter_parameters&lt;/code&gt; to filter parameters that we don’t want to show in our logs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;RSpec.describe ApplicationController, type: :controller do
  it { should filter_param(:password) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see from the above that this spec is for the &lt;code&gt;ApplicationController&lt;/code&gt;.
For params that will be used in other controllers when creating a record (like the &lt;code&gt;BooksController&lt;/code&gt;), we can have a spec that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;RSpec.describe BooksController, type: :controller do
  it do
    params = {
      book: {
        title: &amp;#39;Tipping Point&amp;#39;,
        description: &amp;#39;Tipping Point&amp;#39;,
        author: 1,
        publication_year: 2001
      }
    }
    should permit(:title, :description, :author, :publication_year).
      for(:create, params: params).
      on(:book)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will test that the right parameters are permitted for the &lt;code&gt;BooksController&lt;/code&gt; action. The &lt;code&gt;params&lt;/code&gt; hash we create matches part of the request to the controller. The test checks that &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, and &lt;code&gt;publication_year&lt;/code&gt; are permitted parameters for &lt;code&gt;book&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What if the action needs a query parameter to work?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;RSpec.describe BooksController, type: :controller do
  before do
    create(:book, id: 1)
  end

  it do
    params = {
      id: 1,
      book: {
        title: &amp;#39;Tipping Point&amp;#39;,
        description: &amp;#39;Tipping Point&amp;#39;,
        author: 1,
        publication_year: 2001
      }
    }
    should permit(:title, :description, :author, :publication_year).
      for(:update, params: params).
      on(:book)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above, we use the &lt;code&gt;before&lt;/code&gt; block to create a new book record with the &lt;code&gt;id&lt;/code&gt; as 1. Then we include the &lt;code&gt;id&lt;/code&gt; in the params hash.&lt;/p&gt;
&lt;p&gt;If you have a controller action that simply redirects to another path, you can have a spec that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;#39;GET #show&amp;#39; do
  before { get :show }

  it { should redirect_to(books_path) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This checks that we are redirected to the &lt;code&gt;books_path&lt;/code&gt; when the request gets to the &lt;code&gt;show&lt;/code&gt; action.&lt;/p&gt;
&lt;p&gt;We can modify the above spec to also test for its response:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;#39;GET #show&amp;#39; do
  before { get :show }

  it { should redirect_to(books_path) }
  it { should respond_with(301) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ve modified it to test for the status code. If we’re not sure of the exact status code but we have a range of numbers, we can use the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;#39;GET #show&amp;#39; do
  before { get :show }

  it { should redirect_to(books_path) }
  it { should respond_with(301..308) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use a &lt;code&gt;rescue_from&lt;/code&gt; matcher to rescue from certain errors, like &lt;code&gt;ActiveRecord::RecordInvalid&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should rescue_from(ActiveRecord::RecordInvalid).with(:handle_invalid) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This assumes we have (or will have) a method called &lt;code&gt;handle_invalid&lt;/code&gt; that will handle the error.&lt;/p&gt;
&lt;p&gt;There are matchers for callbacks we tend to use in our controllers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should use_before_action(:set_user) }
it { should_not use_before_action(:set_admin) }

it { should use_around_action(:wrap_in_transaction) }
it { should_not use_around_action(:wrap_in_transaction) }

it { should use_after_action(:send_admin_email) }
it { should_not use_after_action(:send_user_email) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can test if the &lt;code&gt;session&lt;/code&gt; has been set or not.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should set_session }
it { should_not set_session }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll want to use &lt;code&gt;should_not set_session&lt;/code&gt; in your &lt;code&gt;destroy&lt;/code&gt; action.&lt;/p&gt;
&lt;p&gt;Finally, here&amp;#39;s how you can write a spec for your routes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;it { should route(:get, &amp;#39;/books&amp;#39;).to(action: :index) }
it { should route(:get, &amp;#39;/books/1&amp;#39;).to(action: :show, id: 1) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&amp;#39;s it!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, we’ve seen what a spec that does not use shoulda-matchers looks like. We then explored how to use shoulda-matchers for your Rails project. It simplifies specs — instead of a spec spanning multiple lines, shoulda-matchers span just one line.&lt;/p&gt;
&lt;p&gt;While it’s helpful to use shoulda-matchers, you should know that they cannot replace every spec you’ll need to write (mostly just specs to do with business logic).&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Keep Your Ruby Code Maintainable with Money-Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/11/29/keep-your-ruby-code-maintainable-with-money-rails.html"/>
    <id>https://blog.appsignal.com/2023/11/29/keep-your-ruby-code-maintainable-with-money-rails.html</id>
    <published>2023-11-29T00:00:00+00:00</published>
    <updated>2023-11-29T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We will cover some best practices and common ways of handling money in your Rails app with Money-Rails.</summary>
    <content type="html">&lt;p&gt;When working with money in an application, ensuring everything is accounted for is important.&lt;/p&gt;
&lt;p&gt;In this post, we will explore some common methods and best practices of handling money in your Ruby app, and see how you can use &lt;code&gt;money-rails&lt;/code&gt; to write maintainable money-handling code.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Use Cases for Storing Money&lt;/h2&gt;
&lt;p&gt;There are several use cases where your Ruby app might need to handle money — for example, an e-commerce company that sells products, a SaaS maintaining users&amp;#39; subscriptions, etc.&lt;/p&gt;
&lt;p&gt;In this post, we will walk through some examples of how to handle money in an e-commerce app.&lt;/p&gt;
&lt;h2&gt;Storing Money in a Ruby Database&lt;/h2&gt;
&lt;p&gt;First, let’s start with strategies for storing money in your database: using a float column, the &lt;code&gt;numeric&lt;/code&gt; type, and the &lt;code&gt;integer&lt;/code&gt; type.&lt;/p&gt;
&lt;h3&gt;Using a Float Column&lt;/h3&gt;
&lt;p&gt;The most obvious way to store money is to use a float column. However, because of the way floating-point numbers are stored, they do not guarantee exactness. Instead, they only approximate the actual value.
While this might be sufficient for some applications, there are much better ways to store money.&lt;/p&gt;
&lt;h3&gt;Using the &lt;code&gt;numeric&lt;/code&gt; Type&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL&quot;&gt;&lt;code&gt;numeric&lt;/code&gt; type&lt;/a&gt; can store arbitrary-precision numbers in a database.&lt;/p&gt;
&lt;p&gt;A numeric column can store a large range of numbers (up to 131,072 &lt;strong&gt;digits&lt;/strong&gt; before a decimal point and up to 16,383 digits after a decimal point) and is therefore perfectly suited to work with money.&lt;/p&gt;
&lt;p&gt;But one important point to note here is that calculations on numeric types are very slow compared to integer/floating-point numbers.&lt;/p&gt;
&lt;p&gt;Numeric types should be the go-to data type when you need arbitrary-precision numbers, but, depending on business logic, they are rarely needed unless it&amp;#39;s for very specific use cases.
Let’s see if we can do better in an e-commerce scenario.&lt;/p&gt;
&lt;h3&gt;Using the &lt;code&gt;integer&lt;/code&gt; Type&lt;/h3&gt;
&lt;p&gt;We need to store the prices of products, with the lowest unit being cents.
A product can be priced at &lt;code&gt;$19.99&lt;/code&gt; but never &lt;code&gt;$19.9954321&lt;/code&gt;.
This means that, instead of working at the dollar level, we will always have an integer value as the price at the cents level.
We can easily store an integer without any approximation in the database using one of the integer types (e.g., &lt;code&gt;smallint&lt;/code&gt;, &lt;code&gt;integer&lt;/code&gt;, &lt;code&gt;bigint&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;But please note that this does constrain the represented values (as compared to the &lt;code&gt;numeric&lt;/code&gt; type) — the maximum &lt;code&gt;integer&lt;/code&gt; value on Postgres &lt;code&gt;2147483647&lt;/code&gt; will be &lt;code&gt;$21,474,836.47&lt;/code&gt;.
This should normally be enough for any e-commerce product, but it’s good to know that there’s a limit.
If you require higher precision (for example, a 10th of a cent), this is possible by just storing the amount as a mill instead of cents.
Keep in mind that this will further decrease the range of values that can be represented in an integer column.&lt;/p&gt;
&lt;p&gt;But an &lt;code&gt;integer&lt;/code&gt;&amp;#39;s big advantage over a &lt;code&gt;numeric&lt;/code&gt; type is that calculations are very fast.
As long as you can work within the integer (or &lt;code&gt;bigint&lt;/code&gt;) range and have clear constraints set, using an &lt;code&gt;integer&lt;/code&gt;/&lt;code&gt;bigint&lt;/code&gt; is the recommended storage option.&lt;/p&gt;
&lt;p&gt;Sorting out storage is just the first step to representing money in our Ruby application, though.
You might also need to handle different currencies, formatting the values based on the location of users (e.g., USD is formatted as &lt;code&gt;$1,234.56&lt;/code&gt;, whereas the same amount in Euros is formatted as &lt;code&gt;€1.234,56&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;While it is possible to implement this manually, a gem like &lt;code&gt;money-rails&lt;/code&gt; can handle it out of the box for you.&lt;/p&gt;
&lt;h2&gt;Enter &lt;code&gt;money-rails&lt;/code&gt; for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/RubyMoney/money-rails&quot;&gt;Money-Rails&lt;/a&gt; integrates the &lt;code&gt;money&lt;/code&gt; gem in Rails.
It can handle automatic conversion between the database representation of money to &lt;code&gt;Money&lt;/code&gt; instances. These instances can then be used to access all helpers available from the &lt;code&gt;money&lt;/code&gt; gem (we will get to an overview of those helpers later in this post).&lt;/p&gt;
&lt;p&gt;Additionally, &lt;code&gt;money-rails&lt;/code&gt; also has helpers for migrations and configurable validation options.
Let’s start by creating a product with a price.
We&amp;#39;ll use the &lt;a href=&quot;https://github.com/RubyMoney/money-rails#migration-helpers&quot;&gt;monetize&lt;/a&gt; helper to do that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# db/migrate/20230803062226_create_products.rb
class CreateProducts &amp;lt; ActiveRecord::Migration[7.0]
  def change
    create_table :products do |t|
      t.string :title, null: false
      t.monetize :price, null: false

      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;t.monetize :price&lt;/code&gt; adds &lt;code&gt;price_cents&lt;/code&gt; and &lt;code&gt;price_currency&lt;/code&gt; columns to the table.
In addition to this, we need to mark the &lt;code&gt;price_cents&lt;/code&gt; column that &lt;code&gt;money-rails&lt;/code&gt; will control in the model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/product.rb
class Product &amp;lt; ApplicationRecord
  monetize :price_cents
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With just a few lines, we now have access to a lot of helpers.
We can create a new product by simply passing a price attribute.
The &lt;code&gt;price&lt;/code&gt; will automatically be converted to a cent value for the database. When accessing the &lt;code&gt;price&lt;/code&gt; later, we will have instances of &lt;code&gt;Money&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; product = Product.new(title: &amp;quot;Foo&amp;quot;, price: 19.99)
# =&amp;gt; #&amp;lt;Product:0x0000000108ff9e30 id: nil, title: &amp;quot;Foo&amp;quot;, price_cents: 1999, price_currency: &amp;quot;USD&amp;quot;, created_at: nil, updated_at: nil&amp;gt;

irb&amp;gt; product.price
# =&amp;gt; #&amp;lt;Money fractional:1999 currency:USD&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also pass the attributes directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; product = Product.new(title: &amp;quot;Foo&amp;quot;, price_cents: 1999, price_currency: &amp;quot;EUR&amp;quot;)
# =&amp;gt; #&amp;lt;Product:0x0000000108ff2130 id: nil, title: &amp;quot;Foo&amp;quot;, price_cents: 1999, price_currency: &amp;quot;EUR&amp;quot;, created_at: nil, updated_at: nil&amp;gt;

irb&amp;gt; product.price.format
# =&amp;gt; &amp;quot;€19,99&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Currency in the &lt;code&gt;money&lt;/code&gt; Gem for Ruby&lt;/h2&gt;
&lt;p&gt;Currencies are consistently represented as instances of &lt;code&gt;Money::Currency&lt;/code&gt;.
This not only includes the name of the currency, but a lot of additional information like the name, symbol, iso code, etc.:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb(main):042:0&amp;gt; product.price.currency
# =&amp;gt; #&amp;lt;Money::Currency id: usd, priority: 1, symbol_first: true, thousands_separator: ,, html_entity: $, decimal_mark: ., name: United States Dollar, symbol: $, subunit_to_unit: 100, exponent: 2, iso_code: USD, iso_numeric: 840, subunit: Cent, smallest_denomination: 1, format: &amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/RubyMoney/money/tree/main#currency&quot;&gt;&lt;code&gt;money&lt;/code&gt; gem&amp;#39;s currency documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say you only have a single currency in your application and don’t need to track currency inside your database.
First, set a default currency for &lt;code&gt;money&lt;/code&gt; in an initializer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/money.rb
MoneyRails.configure do |config|
  # set the default currency
  config.default_currency = :usd
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the migration, disable the currency column:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# db/migrate/20230803062226_create_products.rb
class CreateProducts &amp;lt; ActiveRecord::Migration[7.0]
  def change
    create_table :products do |t|
      t.string :title, null: false
      t.monetize :price, null: false, currency: { present: false }

      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are many &lt;a href=&quot;https://github.com/RubyMoney/money-rails#migration-helpers&quot;&gt;other Money-Rails migration helpers&lt;/a&gt; that allow null values for amounts or configure default amounts/currencies.&lt;/p&gt;
&lt;h2&gt;Validations with &lt;code&gt;monetize&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You can configure validations on money using the &lt;code&gt;monetize&lt;/code&gt; Active Record helper.
To do this, update your model to opt in for validations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Product &amp;lt; ApplicationRecord
  monetize :price_cents,
           numericality: {
             greater_than_or_equal_to: 0,
             less_than_or_equal_to: 1000
           }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if you try to save a product with an invalid price, validation will fail:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; product = Product.new(title: &amp;quot;Foo&amp;quot;, price: -10)
irb&amp;gt; product.save!
# =&amp;gt; raise_validation_error: Validation failed: Price must be greater than or equal to 0 (ActiveRecord::RecordInvalid)

irb&amp;gt; product.errors
# =&amp;gt; #&amp;lt;ActiveModel::Errors [#&amp;lt;ActiveModel::Error attribute=price, type=greater_than_or_equal_to, options={:allow_nil=&amp;gt;nil, :value=&amp;gt;-10, :count=&amp;gt;0}&amp;gt;]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Localization&lt;/h2&gt;
&lt;p&gt;Money can be configured to either use the location information provided by &lt;code&gt;i18n&lt;/code&gt; or to localize based on the amount’s currency.
Use the &lt;code&gt;locale_backend&lt;/code&gt; to configure this — usually in an initializer like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/money.rb
Money.locale_backend = :currency
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example, we set it to &lt;code&gt;:currency&lt;/code&gt;. This uses the formatting rules defined for each currency:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; product = Product.new(title: &amp;quot;Foo&amp;quot;, price_cents: 15_999_00, price_currency: &amp;quot;USD&amp;quot;)
irb&amp;gt; product.price.format
# =&amp;gt; &amp;quot;$15,999.00&amp;quot;

irb&amp;gt; product.price_currency = &amp;quot;EUR&amp;quot;
irb&amp;gt; product.price.format
# =&amp;gt; &amp;quot;€15.999,00&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To configure a consistently formatted amount regardless of the amount’s currency, use the &lt;code&gt;:i18n&lt;/code&gt; locale backend:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; Money.locale_backend = :i18n
irb&amp;gt; I18n.locale = :en

irb&amp;gt; Product.new(title: &amp;quot;Foo&amp;quot;, price_cents: 15_999_00, price_currency: &amp;quot;USD&amp;quot;).price.format
# =&amp;gt; &amp;quot;$15,999.00&amp;quot;
irb&amp;gt; Product.new(title: &amp;quot;Foo&amp;quot;, price_cents: 15_999_00, price_currency: &amp;quot;EUR&amp;quot;).price.format
# =&amp;gt; &amp;quot;€15,999.00&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using the locale backend &lt;code&gt;i18n&lt;/code&gt;, it is also possible to configure the formatting using the translation files:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# config/locale/en.yml
en:
  number:
    currency:
      format:
        delimiter: &amp;quot;,&amp;quot;
        format: &amp;quot;%u%n&amp;quot;
        precision: 2
        separator: &amp;quot;.&amp;quot;
        significant: false
        strip_insignificant_zeros: false
        unit: &amp;quot;$&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that if you are using &lt;a href=&quot;https://github.com/svenfuchs/rails-i18n/&quot;&gt;&lt;code&gt;rails-i18n&lt;/code&gt;&lt;/a&gt;, configuration is automatically available for many locales.&lt;/p&gt;
&lt;h2&gt;Computations&lt;/h2&gt;
&lt;p&gt;Money instances are usable as regular numerical values with all operators, so you can compare values and perform arithmetic operations (using &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;).
This makes it much more intuitive to perform operations on objects containing money.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Comparisons - both value and currency must be equal for equality
irb&amp;gt; p1 = Product.new(price_cents: 1000, price_currency: &amp;quot;USD&amp;quot;)
irb&amp;gt; p2 = Product.new(price_cents: 1000, price_currency: &amp;quot;USD&amp;quot;)
irb&amp;gt; p1.price == p2.price
# =&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since mathematical operators work as expected, you can easily compute sum/average or price discounts.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Compute invoice total by summing all line item amounts
irb&amp;gt; item = LineItem.new(amount_cents: 1000, amount_currency: &amp;quot;USD&amp;quot;)
irb&amp;gt; discount = LineItem.new(amount_cents: -250, amount_currency: &amp;quot;USD&amp;quot;)
irb&amp;gt; total = item.amount + discount.amount
# =&amp;gt; #&amp;lt;Money fractional:750 currency:USD&amp;gt;

# Find the average price of a product
irb&amp;gt; average = Product.sum(&amp;amp;:price) / Product.count
# =&amp;gt; #&amp;lt;Money fractional:1000 currency:USD&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to access the underlying value from a Money instance, you can do that using &lt;code&gt;amount&lt;/code&gt; or &lt;code&gt;cents&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; Product.new(price_cents: 1000, price_currency: &amp;quot;USD&amp;quot;).amount
# =&amp;gt; BigDecimal(10)

irb&amp;gt; Product.new(price_cents: 1000, price_currency: &amp;quot;USD&amp;quot;).cents
# =&amp;gt; 1000 (Integer)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Currency Exchange&lt;/h2&gt;
&lt;p&gt;If you are working with multiple currencies, you can automatically convert two different currencies based on exchange rates using exchange rate stores.&lt;/p&gt;
&lt;p&gt;The default implementation is just an in-memory store.
It stores the exchange rates you provide using &lt;code&gt;Money.add_rate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To use it, first add a rate. Then, you can use the &lt;code&gt;exchange_to&lt;/code&gt; method to convert a money object to another currency.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; Money.add_rate(&amp;quot;USD&amp;quot;, &amp;quot;EUR&amp;quot;, 0.92)
irb&amp;gt; Product.new(price_cents: 1000, price_currency: &amp;quot;USD&amp;quot;).price.exchange_to(&amp;quot;EUR&amp;quot;)
# =&amp;gt; #&amp;lt;Money fractional:920 currency:EUR&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a conversion rate is not present, it raises a &lt;code&gt;Money::Bank::UnknownRate&lt;/code&gt; exception.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; Product.new(price_cents: 1000, price_currency: &amp;quot;USD&amp;quot;).price.exchange_to(&amp;quot;EUR&amp;quot;)
# =&amp;gt; No conversion rate known for &amp;#39;USD&amp;#39; -&amp;gt; &amp;#39;INR&amp;#39; (Money::Bank::UnknownRate)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, if you are using this in production, you must provide the exchange rate before you perform any conversions.&lt;/p&gt;
&lt;p&gt;You can provide a custom &lt;a href=&quot;https://github.com/RubyMoney/money/tree/main#exchange-rate-stores&quot;&gt;exchange rate store&lt;/a&gt; to handle this automatically. There are also some exchange rate implementations already available as gems.
For example, you can drop in the &lt;a href=&quot;https://github.com/RubyMoney/eu_central_bank&quot;&gt;&lt;code&gt;eu_central_bank&lt;/code&gt;&lt;/a&gt; exchange rate store to fetch the latest exchange rates from the European Central Bank.&lt;/p&gt;
&lt;p&gt;Once the gem is installed, you can set it as the default bank in the initializer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/money.rb
require &amp;#39;eu_central_bank&amp;#39;
eu_bank = EuCentralBank.new
Money.default_bank = eu_bank

# Update rates from the EU Central Bank
# Ideally this should be performed periodically in a cron job.
# If you need conversions rarely, you can call it before using exchange_to.
eu_bank.update_rates
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once set up, you can simply exchange any currency, and it should work:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; Product.new(price_cents: 1000, price_currency: &amp;quot;USD&amp;quot;).price.exchange_to(&amp;quot;EUR&amp;quot;)
# =&amp;gt; Money.from_cents(92, &amp;quot;EUR&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we saw how to store and work with money in a Rails application.&lt;/p&gt;
&lt;p&gt;Money-Rails greatly simplifies the whole process and provides helper methods that make working with money safer and more maintainable in the long run.&lt;/p&gt;
&lt;p&gt;Many other options are available to customize this for your Ruby on Rails app.
Check out the full details in the &lt;a href=&quot;https://github.com/RubyMoney/money&quot;&gt;&lt;code&gt;money&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/RubyMoney/money-rails&quot;&gt;&lt;code&gt;money-rails&lt;/code&gt;&lt;/a&gt; guides.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Shaping the Future of Ruby and Kafka Together with rdkafka-ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/11/22/shaping-the-future-of-ruby-and-kafka-together-with-rdkafka-ruby.html"/>
    <id>https://blog.appsignal.com/2023/11/22/shaping-the-future-of-ruby-and-kafka-together-with-rdkafka-ruby.html</id>
    <published>2023-11-22T00:00:00+00:00</published>
    <updated>2023-11-22T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">The team behind Karafka and AppSignal have worked together on the rdkafka-ruby gem.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;Hello there! My name is Maciej Mensfeld, and some of you might recognize me from my involvement in RubyGems Security, OSS commitments, or perhaps from Karafka: a multi-threaded, efficient Kafka processing framework tailored for Ruby and Rails.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;While I generally pen my thoughts on my personal blog, today&amp;#39;s post is unique. This article results from a collaborative effort with the brilliant people over at AppSignal. To set the record straight, I don&amp;#39;t work for AppSignal. However, I felt that AppSignal&amp;#39;s blog would be the most appropriate platform for this piece due to its nature.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Karafka Embraces &lt;code&gt;rdkafka&lt;/code&gt; Gem&lt;/h2&gt;
&lt;p&gt;We&amp;#39;re happy to stand behind the &lt;a href=&quot;https://github.com/appsignal/rdkafka-ruby&quot;&gt;&lt;code&gt;rdkafka-ruby&lt;/code&gt;&lt;/a&gt; gem, a brainchild of &lt;a href=&quot;https://www.appsignal.com/&quot;&gt;AppSignal&lt;/a&gt;. Their incredible contributions have not only transformed the landscape of Ruby Kafka but have also played a pivotal role in the evolution of the &lt;a href=&quot;https://github.com/karafka&quot;&gt;Karafka&lt;/a&gt; ecosystem.&lt;/p&gt;
&lt;p&gt;This library is a beacon of modernity in the &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Kafka&lt;/a&gt; space for Ruby. It integrates with &lt;a href=&quot;https://github.com/confluentinc/librdkafka&quot;&gt;&lt;code&gt;librdkafka&lt;/code&gt;&lt;/a&gt;, the production-ready C client using the &lt;code&gt;ffi&lt;/code&gt; gem that provides outstanding performance and stability.&lt;/p&gt;
&lt;p&gt;Following the sunset of &lt;a href=&quot;https://github.com/zendesk/ruby-kafka&quot;&gt;&lt;code&gt;ruby-kafka&lt;/code&gt;&lt;/a&gt;, it rapidly became the go-to low-level driver for Kafka interactions in Ruby. The migration of &lt;a href=&quot;https://github.com/karafka/&quot;&gt;Karafka&lt;/a&gt; and &lt;a href=&quot;https://github.com/zendesk/racecar&quot;&gt;Racecar&lt;/a&gt; (leading Ruby Kafka frameworks) to &lt;code&gt;rdkafka-ruby&lt;/code&gt; further solidified its dominance.&lt;/p&gt;
&lt;h2&gt;AppSignal&amp;#39;s Use Case and the OSS Community&lt;/h2&gt;
&lt;p&gt;While AppSignal deploys &lt;code&gt;rdkafka-ruby&lt;/code&gt; on a vast scale, it&amp;#39;s essential to recognize that its use cases might only encompass some of the needs of the Ruby OSS community. This is especially true in realms like data consumption and crafting higher-level functionalities.&lt;/p&gt;
&lt;p&gt;Recognizing these gaps and driven by my passion for the Karafka ecosystem and Kafka, I introduced a fork named &lt;a href=&quot;https://github.com/karafka/karafka-rdkafka&quot;&gt;&lt;code&gt;karafka-rdkafka&lt;/code&gt;&lt;/a&gt; to expand this library further. However, the vision of having two autonomous gems, each with its developmental journey, raised concerns about the overarching benefit to the Ruby Kafka OSS community. It was this realization that led me to extend an &amp;quot;adoption offer&amp;quot; to AppSignal&amp;#39;s talented development team, particularly to &lt;a href=&quot;https://blog.appsignal.com/authors/thijs-cadier&quot;&gt;Thijs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Merging Visions for a Unified Future&lt;/h2&gt;
&lt;p&gt;Today, I&amp;#39;m pleased to share that our discussions bore fruit, carving a path that supports the collective interests of the Ruby and Kafka open-source community. The &lt;code&gt;rdkafka-ruby&lt;/code&gt; gem will now thrive under the care of the Karafka community, with some guiding principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Openness&lt;/strong&gt;: &lt;code&gt;rdkafka-ruby&lt;/code&gt; will eternally stand as a fully open, MIT-licensed primary binding for &lt;code&gt;librdkafka&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low-level Commitment&lt;/strong&gt;: While it offers direct APIs based on &lt;code&gt;librdkafka&lt;/code&gt;, &lt;code&gt;rdkafka-ruby&lt;/code&gt; will maintain its &amp;quot;layer 1&amp;quot; stature, focusing on low-level functionalities and leaving high-level details to libraries built on top of it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alignment&lt;/strong&gt;: &lt;code&gt;rdkafka-ruby&lt;/code&gt; and &lt;code&gt;karafka-rdkafka&lt;/code&gt; will harmonize their developmental paths. Unless &lt;code&gt;karafka-rdkafka&lt;/code&gt; requires any framework-specific elements that would not be configurable, both will be merged into one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt; - &lt;code&gt;rdkafka-ruby&lt;/code&gt; will consistently support every official version of both Kafka and Ruby, ensuring smooth operations for its users.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Such a strategic shift promises stability and continued evolution to the entire ecosystem. Whether you&amp;#39;re a &lt;code&gt;rdkafka-ruby&lt;/code&gt; enthusiast valuing its flexibility or someone invested in high-level frameworks like Karafka and WaterDrop, you can rest assured that &lt;code&gt;rdkafka-ruby&lt;/code&gt; remains the heartbeat of these platforms.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;rdkafka-ruby&lt;/code&gt; and Karafka Instrumentation with AppSignal&lt;/h2&gt;
&lt;p&gt;While transitioning the &lt;code&gt;rdkafka-ruby&lt;/code&gt; ownership, feedback from AppSignal users highlighted another need. It became clear that an official instrumentation and monitoring solution for the Karafka ecosystem was essential. So, in response to this demand, I took steps to provide just that. This wasn&amp;#39;t just about meeting a need; it was about fortifying the Karafka community with robust tools to ensure efficiency, stability, and clarity in their operations.&lt;/p&gt;
&lt;p&gt;In the realm of Kafka with Ruby and Rails, the choice of monitoring tools is crucial. AppSignal emerges as the premier choice for Karafka applications instrumentation, and here&amp;#39;s why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AppSignal offers insights into the performance, stability, health, and resource utilization of Karafka consumers, ensuring they operate optimally.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It provides comprehensive error notifications for both consumers and producers, adeptly capturing asynchronous errors related to their operations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With official support from Karafka&amp;#39;s creator, AppSignal&amp;#39;s integration promises continuous updates and refinements, ensuring it remains the go-to tool for Karafka monitoring.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For developers seeking simplicity, AppSignal integration is a breeze. It comes packaged with Karafka, eliminating the need for extra gems or tedious setups.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choosing AppSignal isn&amp;#39;t just about monitoring; it&amp;#39;s about optimizing and ensuring the reliability of your Karafka and Kafka-based applications.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;The Importance of Monitoring Your Consumers and Producers&lt;/h2&gt;
&lt;p&gt;Monitoring producers and consumers is not just a &amp;#39;good-to-have&amp;#39; but a foundational pillar of any robust Kafka-based system. Here&amp;#39;s an in-depth look into why this is so essential:&lt;/p&gt;
&lt;h3&gt;Ensuring Data Consistency and Integrity&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Accurate Data Flow&lt;/strong&gt;: Kafka&amp;#39;s architecture is fundamentally about ensuring that messages are transported from producers to consumers. Effective monitoring verifies that this data flow is accurate and consistent.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Detecting Anomalies&lt;/strong&gt;: In a distributed system, there&amp;#39;s a possibility of message loss, duplication, or even out-of-order processing. Monitoring tools can promptly detect such issues, allowing for timely interventions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Audit and Compliance&lt;/strong&gt;: For sectors where data integrity is not just an operational requirement but a legal one (like finance or healthcare), monitoring ensures that systems remain compliant with regulatory standards.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Identifying and Resolving Performance Bottlenecks&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Understanding System Dynamics&lt;/strong&gt;: Monitoring offers insights into the dynamics between producers and consumers. By visualizing message flow rates, system administrators can pinpoint areas of congestion or under-utilization.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Proactive Issue Resolution&lt;/strong&gt;: Imagine a scenario where a producer continuously pushes messages, but the consumer lags in processing them. This disparity could go unnoticed without monitoring until it snowballs into a significant issue. AppSignal highlights these discrepancies and provides actionable intelligence to resolve them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimizing Resources&lt;/strong&gt;: Resources, be they computational or human, are finite. Through monitoring, you can prevent wastage and ensure the highest levels of efficiency.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Enhancing System Reliability and Availability&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immediate Failure Detection&lt;/strong&gt;: Kafka systems are often the backbone of critical business operations. Any downtime or failure can lead to substantial financial and reputational losses. Monitoring tools act as vigilant guards, immediately detecting failures or disruptions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Insights into System Health&lt;/strong&gt;: Beyond immediate failures, monitoring provides a continuous pulse check on the Karafka ecosystem&amp;#39;s health. Whether tracking memory usage, gauging disk I/O, or measuring network latency, these insights ensure the system remains in its prime operational state.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Future-Proofing&lt;/strong&gt;: By observing trends and patterns over time, monitoring can aid in capacity planning, ensuring that the Kafka setup is reliable today and geared up for future demands.
In essence, neglecting to monitor Kafka&amp;#39;s consumers and producers is more than just an operational oversight; it&amp;#39;s a missed opportunity to harness the true power and efficiency of the Karafka and Kafka ecosystem.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started with Karafka and AppSignal&lt;/h2&gt;
&lt;p&gt;The beauty of &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;integrating Karafka with AppSignal&lt;/a&gt; lies in its simplicity. Thanks to the AppSignal integration being inherently part of the Karafka gem, there&amp;#39;s no need for extra installations or gems. All the necessary components are pre-packaged in Karafka. Remember one key detail: when configuring, ensure the errors listener is subscribed before the metrics listener for optimal monitoring accuracy.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Require both listeners
require &amp;#39;karafka/instrumentation/vendors/appsignal/errors_listener&amp;#39;
require &amp;#39;karafka/instrumentation/vendors/appsignal/metrics_listener&amp;#39;

# Create them
errors_listener = ::Karafka::Instrumentation::Vendors::Appsignal::ErrorsListener.new
metric_listener = ::Karafka::Instrumentation::Vendors::Appsignal::MetricsListener.new

# Subscribe with the error listener first to track producers and consumers errors
Karafka.monitor.subscribe(errors_listener)
Karafka.producer.monitor.subscribe(errors_listener)
# And subscribe to Karafka monitor to get all the exciting metrics
Karafka.monitor.subscribe(metric_listener)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Monitoring Karafka and Kafka with AppSignal&lt;/h2&gt;
&lt;p&gt;Here are some of the key metrics and insights offered by the Karafka-AppSignal integration (the following graphs are automatically created for you when you set up the integration):&lt;/p&gt;
&lt;h3&gt;Consumption Metrics&lt;/h3&gt;
&lt;p&gt;Understanding consumers&amp;#39; and producers&amp;#39; health and behavior is foundational to any Karafka setup. With AppSignal, you can monitor aspects such as message processing times, batch sizes, error rates, and more. This data allows developers to ensure that consumers and producers operate at their peak efficiency.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-11/consumed-messages.png&quot; alt=&quot;obraz&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Message Throughput and Lag&lt;/h3&gt;
&lt;p&gt;The core function of Kafka is the flow of messages. AppSignal offers insights into message throughput, clearly showing the volume of messages being processed. Moreover, tracking message lag is crucial; it highlights the time difference between when a message is produced and when it&amp;#39;s consumed. High lags can be indicative of potential issues in the pipeline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-11/lags.png&quot; alt=&quot;obraz&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Performance Monitoring of Consumers&lt;/h3&gt;
&lt;p&gt;AppSignal allows you to monitor the performance of each of the consumers&amp;#39; jobs. Tracking performance provides invaluable insights, ensuring that each consumer operates efficiently and processes data at optimal speeds.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-11/error-breakdown.png&quot; alt=&quot;obraz&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Error and Exception Tracking&lt;/h3&gt;
&lt;p&gt;AppSignal&amp;#39;s robust error tracking system ensures that any anomalies, whether application-level or system-level, are immediately flagged. This not only helps in prompt rectification but also aids in understanding patterns that lead to such issues.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-11/errors.png&quot; alt=&quot;obraz&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In summary, the Karafka AppSignal integration isn&amp;#39;t just a monitoring tool; it&amp;#39;s a magnifying glass into the intricate world of Karafka.&lt;/p&gt;
&lt;p&gt;While the aspects we&amp;#39;ve covered above are some of the core metrics and insights available, the platform offers much more. Dive deeper to understand how to best optimize and enhance your Karafka and Kafka operations.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Handle Incoming Webhooks with LiteJob for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/11/15/handle-incoming-webhooks-with-litejob-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2023/11/15/handle-incoming-webhooks-with-litejob-for-ruby-on-rails.html</id>
    <published>2023-11-15T00:00:00+00:00</published>
    <updated>2023-11-15T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the third part of this LiteStack series, we&#039;ll dive into using LiteJob to handle incoming webhooks.</summary>
    <content type="html">&lt;p&gt;In parts one and two of this series, we only dealt with the pure CRUD aspects of using SQLite as a production database.&lt;/p&gt;
&lt;p&gt;In this post, we will explore the world of queue mechanisms, using SQLite as the pub/sub adapter for ActiveJob.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s make use of LiteJob to handle incoming webhooks in our Rails application.&lt;/p&gt;
&lt;h2&gt;Our Ruby on Rails Use Case&lt;/h2&gt;
&lt;p&gt;Our use case is the image generation that we are currently doing synchronously in the &lt;code&gt;PromptsController&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;model = Replicate.client.retrieve_model(&amp;quot;stability-ai/stable-diffusion-img2img&amp;quot;)
version = model.latest_version
version.predict({prompt: prompt_params[:title], image: @prompt.data_url}, replicate_rails_url)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clearly, there are some long-running processes here that should be deferred to a background job: the &lt;em&gt;loading of the model&lt;/em&gt;, and the &lt;em&gt;prediction&lt;/em&gt; itself.&lt;/p&gt;
&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;p&gt;In your environment configuration files, ensure you reference LiteJob as the queue adapter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/environments/development.rb
# config/environments/production.rb
Rails.application.configure do
  # ...

  config.active_job.queue_adapter = :litejob

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&amp;#39;re all set to continue our investigation. Don&amp;#39;t forget to restart your development server, though!&lt;/p&gt;
&lt;p&gt;If you like, you can add more ActiveJob configuration in &lt;code&gt;config/litejob.yml&lt;/code&gt; — for example, queue priorities:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;queues:
  - [default, 1]
  - [urgent, 5]
  - [critical, 10, &amp;quot;spawn&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please refer to the &lt;a href=&quot;https://github.com/oldmoe/litestack#configuration-file&quot;&gt;README&lt;/a&gt; for further details.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s set up, so let&amp;#39;s move on to generating our images asynchronously.&lt;/p&gt;
&lt;h2&gt;Generating Images Asynchronously in Our Ruby on Rails Application&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ll start by scaffolding our job with the usual Rails generator command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g job GenerateImage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s move the image generation code from &lt;code&gt;PromptsController&lt;/code&gt; into the &lt;code&gt;perform&lt;/code&gt; method of this job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class GenerateImageJob &amp;lt; ApplicationJob
  include Rails.application.routes.url_helpers

  queue_as :default

  def perform(prompt:)
    model = Replicate.client.retrieve_model(&amp;quot;stability-ai/stable-diffusion-img2img&amp;quot;)
    version = model.latest_version
    version.predict({prompt: prompt.title, image: prompt.data_url}, replicate_rails_url(host: Rails.application.config.action_mailer.default_url_options[:host]))
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we have to include the URL helpers module here to access &lt;code&gt;replicate_rails_url&lt;/code&gt; (as opposed to controllers, where this happens automatically). Furthermore, we also need to explicitly specify the &lt;code&gt;host&lt;/code&gt; — we grab it off ActionMailer&amp;#39;s &lt;code&gt;default_url_options&lt;/code&gt;, in this case. Look at &lt;a href=&quot;https://andycroll.com/ruby/url-helpers-outside-views-controllers/&quot;&gt;Andy Croll&amp;#39;s excellent &amp;#39;Use Rails URL helpers outside views and controllers&amp;#39; post&lt;/a&gt; for more sophisticated uses.&lt;/p&gt;
&lt;p&gt;To make this work with the incoming webhook, make sure you also reference your ngrok URL in your app&amp;#39;s configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/environments/development.rb
config.action_mailer.default_url_options = { host: &amp;quot;YOUR_NGROK_URL&amp;quot;, port: 3000 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the final step on the requesting side of our call to Replicate, we have to actually call the job from &lt;code&gt;PromptsController&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/controllers/prompts_controller.rb

  def create
    # ...
-   model = Replicate.client.retrieve_model(&amp;quot;stability-ai/stable-diffusion-img2img&amp;quot;)
-   version = model.latest_version
-   version.predict({prompt: prompt_params[:title],
-     image: @prompt.data_url}, replicate_rails_url)

    respond_to do |format|
      if @prompt.save
+       GenerateImageJob.perform_later(prompt: @prompt)
+
        format.html { redirect_to prompt_url(@prompt), notice: &amp;quot;Prompt was successfully created.&amp;quot; }
        format.json { render :show, status: :created, location: @prompt }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @prompt.errors, status: :unprocessable_entity }
      end
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Persisting Replicate.com Predictions&lt;/h2&gt;
&lt;p&gt;Now it&amp;#39;s time to take a look at the receiving end of our prediction request, i.e., the incoming webhook.&lt;/p&gt;
&lt;p&gt;Right now, generated predictions are handed back to us, but we don&amp;#39;t do anything with them. Since images are purged from Replicate&amp;#39;s CDN periodically, we want to store them locally.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start by generating a new child model, &lt;code&gt;Prediction&lt;/code&gt;. We&amp;#39;ll prepare it to store the generated image as a binary blob again:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g model Prediction prompt:references replicate_id:string replicate_version:string prediction_image:binary logs:text
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On top of that, we create columns to store some metadata returned from Replicate.com: &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, and &lt;code&gt;logs&lt;/code&gt; — &lt;a href=&quot;https://replicate.com/docs/reference/http#predictions.create&quot;&gt;see the API docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we also have to register this new association in the &lt;code&gt;Prompt&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/prompt.rb

  class Prompt &amp;lt; ApplicationRecord
    include AccountScoped

    belongs_to :account
+   has_many :predictions, dependent: :destroy

    # ...
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we just have to process the incoming predictions in our webhook, but we face an issue. We have to find a way to identify the specific &lt;code&gt;Prompt&lt;/code&gt; instance the prediction was created for. We can circumvent this problem by adding a query param to the incoming webhook URL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/jobs/generate_image_job.rb

  class GenerateImageJob &amp;lt; ApplicationJob
    include Rails.application.routes.url_helpers

    queue_as :default

    def perform(prompt:)
      model = Replicate.client.retrieve_model(&amp;quot;stability-ai/stable-diffusion-img2img&amp;quot;)
      version = model.latest_version
-     version.predict({prompt: prompt.title, image: prompt.data_url}, replicate_rails_url(host: Rails.application.config.action_mailer.default_url_options[:host]))
+     version.predict({prompt: prompt.title, image: prompt.data_url}, replicate_rails_url(host: Rails.application.config.action_mailer.default_url_options[:host], params: {sgid: prompt.to_sgid.to_s}))
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will effectively send our incoming webhook to &lt;code&gt;YOUR_NGROK_URL/replicate/webhook?sgid=YOUR_RECORD_SGID&lt;/code&gt;, making it easy to identify the prompt.&lt;/p&gt;
&lt;h2&gt;Obtain the &lt;code&gt;sgid&lt;/code&gt; in Ruby&lt;/h2&gt;
&lt;p&gt;Sadly, the &lt;code&gt;replicate-rails&lt;/code&gt; gem&amp;#39;s default controller doesn&amp;#39;t pass the query parameters to the webhook handler, but it &lt;em&gt;does&lt;/em&gt; pass the exact webhook URL.&lt;/p&gt;
&lt;p&gt;So we can flex our Ruby standard library muscles to obtain the &lt;code&gt;sgid&lt;/code&gt; from it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/replicate.rb

# ...

require &amp;quot;open-uri&amp;quot;

class ReplicateWebhook
  def call(prediction)
    query = URI(prediction.webhook).query

    sgid = CGI.parse(query)[&amp;quot;sgid&amp;quot;].first

    prompt = GlobalID::Locator.locate_signed(sgid)

    prompt.predictions.create(
      prediction_image: URI.parse(prediction.output.first).open.read,
      replicate_id: prediction.id,
      replicate_version: prediction.version,
      logs: prediction.logs
    )
  end
end

# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s dissect this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, we pluck the exact webhook URL off the incoming &lt;code&gt;prediction&lt;/code&gt; object and parse it, returning the &lt;code&gt;query&lt;/code&gt; string.&lt;/li&gt;
&lt;li&gt;Next, we invoke &lt;code&gt;CGI.parse&lt;/code&gt; to convert the query string into a hash. Accessing the &lt;code&gt;&amp;quot;sgid&amp;quot;&lt;/code&gt; key returns a one-element array, which is why we have to send &lt;code&gt;first&lt;/code&gt; to it.&lt;/li&gt;
&lt;li&gt;Then, we can use the obtained &lt;code&gt;sgid&lt;/code&gt; to locate the &lt;code&gt;prompt&lt;/code&gt; instance.&lt;/li&gt;
&lt;li&gt;Finally, we append a new prediction to our prompt using the properties returned from Replicate.com. We also download the prediction image and save it in SQLite. Note that we can skip resizing because the generated image will already have the correct dimensions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There&amp;#39;s one final bit we have to tend to: we have to make sure our webhook handler works in an idempotent way. Webhooks can arrive duplicated or out of order, so we have to prepare for this case.&lt;/p&gt;
&lt;p&gt;Luckily, we can use the ID returned by replicate to ensure idempotency. All we have to do is add a unique index to the &lt;code&gt;replicate_id&lt;/code&gt; column. This will make it impossible to add a duplicate prediction to our database:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g migration AddUniqueIndexToPredictions
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AddUniqueIndexToPredictions &amp;lt; ActiveRecord::Migration[7.0]
  def change
    add_index :predictions, :replicate_id, unique: true
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keep in mind that you typically would still want to store your assets and attachments in object storage buckets like Amazon S3 or DigitalOcean spaces. To get a glimpse of what SQLite can do, we have opted to store and render them directly from the database here.&lt;/p&gt;
&lt;p&gt;To complete our picture — pun intended — we again have to find a way to display our stored predictions as children of a prompt. We can do this using a data URL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  class Prediction &amp;lt; ApplicationRecord
    belongs_to :prompt

+   def data_url
+     encoded_data = Base64.strict_encode64(prediction_image)
+
+     &amp;quot;data:image/png;base64,#{encoded_data}&amp;quot;
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/prompts/_prompt.html.erb --&amp;gt;
  &amp;lt;div id=&amp;quot;&amp;lt;%= dom_id prompt %&amp;gt;&amp;quot;&amp;gt;
    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Title:&amp;lt;/strong&amp;gt;
      &amp;lt;%= prompt.title %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Description:&amp;lt;/strong&amp;gt;
      &amp;lt;%= prompt.description %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Prompt image:&amp;lt;/strong&amp;gt;
      &amp;lt;%= image_tag prompt.data_url %&amp;gt;
    &amp;lt;/p&amp;gt;

+   &amp;lt;p&amp;gt;
+     &amp;lt;strong&amp;gt;Generated images:&amp;lt;/strong&amp;gt;
+     &amp;lt;% prompt.predictions.each do |prediction| %&amp;gt;
+       &amp;lt;%= image_tag prediction.data_url %&amp;gt;
+     &amp;lt;% end %&amp;gt;
+   &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A Look Behind the Scenes of LiteJob for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s quickly look into how LiteJob uses SQLite to implement a job queueing system. In essence, the class &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/master/lib/litestack/litequeue.rb&quot;&gt;&lt;code&gt;Litequeue&lt;/code&gt;&lt;/a&gt; interfaces with the SQLite &lt;code&gt;queue&lt;/code&gt; table. This table&amp;#39;s columns, like &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;fire_at&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, and &lt;code&gt;created_at&lt;/code&gt;, store and manage job details.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;repush&lt;/code&gt; methods of the &lt;code&gt;Litequeue&lt;/code&gt; class add jobs to the queue, interfacing with their respective SQL statements. When it&amp;#39;s time for a job&amp;#39;s execution, the &lt;code&gt;pop&lt;/code&gt; method in the same class retrieves and removes a job based on its scheduled time. The &lt;code&gt;delete&lt;/code&gt; method allows for specific job removal.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Litejobqueue&lt;/code&gt; class is a subclass of &lt;code&gt;Litequeue&lt;/code&gt;, which, upon initialization, is tasked with &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/master/lib/litestack/litejobqueue.rb#L186&quot;&gt;creating the worker/s&lt;/a&gt;. These workers contain the main run loop that oversees the actual job execution, continuously fetching and running jobs from the queue.&lt;/p&gt;
&lt;h2&gt;Limitations of LiteJob&lt;/h2&gt;
&lt;p&gt;Now that we&amp;#39;ve broken down how to use LiteJob for asynchronous job processing, let&amp;#39;s look at some of the potential drawbacks ensuing from this approach.&lt;/p&gt;
&lt;p&gt;While an SQLite-based architecture provides simplicity and reduces the need for external dependencies, it also introduces several limitations.&lt;/p&gt;
&lt;p&gt;Firstly, LiteJob&amp;#39;s reliance on SQLite inherently restricts its horizontal scaling capabilities. Unlike other databases, SQLite is designed for single-machine use, making it challenging to distribute workload across multiple servers. This can certainly be done using novel technologies like &lt;a href=&quot;https://github.com/superfly/litefs&quot;&gt;LiteFS&lt;/a&gt;, but it is far from intuitive.&lt;/p&gt;
&lt;p&gt;Additionally, even on the same machine, concurrency contends with your web server (e.g., Puma) because LiteJob spawns threads or fibers that compete with those handling the web requests.&lt;/p&gt;
&lt;p&gt;On the other hand (and crucially, for the majority of smaller apps), you might never need such elaborate scaling concepts.&lt;/p&gt;
&lt;h2&gt;LiteJob&amp;#39;s Performance in Benchmarks&lt;/h2&gt;
&lt;p&gt;On a more positive note, LiteJob seems to allow for a lot of overhead before you have to scale horizontally: at least the &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/master/BENCHMARKS.md&quot;&gt;benchmarks&lt;/a&gt; put me in a cautiously euphoric mood. Granted, the benchmarks don&amp;#39;t carry a realistic payload, but they demonstrate that LiteJob makes very efficient use of threads and fibers.&lt;/p&gt;
&lt;h2&gt;Up Next: Streaming Updates with LiteCable&lt;/h2&gt;
&lt;p&gt;In this installment of our series, we delved deeper into the intricacies of using SQLite with LiteJob to handle asynchronous image generation and incoming webhooks in Rails applications.&lt;/p&gt;
&lt;p&gt;To recap, while the SQLite-based architecture of LiteJob offers simplicity and reduced external dependencies, it also presents challenges in horizontal scaling. However, for smaller applications, LiteJob&amp;#39;s efficiency with threads and fibers suggests it can handle a significant workload before necessitating more complex scaling solutions.&lt;/p&gt;
&lt;p&gt;In the next post of this series, we&amp;#39;ll look at providing reactive updates to our users using Hotwire powered by LiteCable.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Deep Dive Into LiteDB for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/11/01/a-deep-dive-into-litedb-for-ruby on-rails.html"/>
    <id>https://blog.appsignal.com/2023/11/01/a-deep-dive-into-litedb-for-ruby on-rails.html</id>
    <published>2023-11-01T00:00:00+00:00</published>
    <updated>2023-11-01T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the second part of this series on LiteStack, we&#039;ll explore LiteDB in detail, including type affinity and flexible typing.</summary>
    <content type="html">&lt;p&gt;In the second post of our series covering LiteStack (an alternative way to build Rails applications entirely based on SQLite), we&amp;#39;ll explore the database&amp;#39;s concepts of &lt;em&gt;flexible typing&lt;/em&gt; and &lt;em&gt;type affinity&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll not only discover how SQLite&amp;#39;s data handling differs from other SQL databases, but also how we efficiently process and store binary data, like images, directly in a database column.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;LiteDB is essentially SQLite, but fine-tuned for usage in Rails. We&amp;#39;ll use LiteDB and SQLite interchangeably in this post.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Flexible Typing In LiteDB for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;The first thing to always keep in mind is that SQLite is &lt;em&gt;flexibly typed&lt;/em&gt;. This means that, in contrast to most other SQL databases (where the data type of a value is defined by its container — the column data type), SQLite is very forgiving. Or, as the &lt;a href=&quot;https://www.sqlite.org/datatype3.html&quot;&gt;SQLite docs&lt;/a&gt; state more formally:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Datatype of a value is associated with the value itself, not with its container.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means, for example, that you can store strings in &lt;code&gt;INTEGER&lt;/code&gt; columns, or exceed the length defined by &lt;code&gt;VARCHAR(n)&lt;/code&gt;. In the development process, this is usually not an issue. It boils down to a bit of a vendor lock-in, though: Using an SQLite database in development and a PostgreSQL one in production can become a nightmare.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; SQLite version 3.37.0 introduced the option of &lt;a href=&quot;https://www.sqlite.org/stricttables.html&quot;&gt;STRICT tables&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;SQLite for Rails: Type Affinity&lt;/h2&gt;
&lt;p&gt;Type affinity in SQLite is a concept that determines how data is stored and converted. Before explaining what this means, let&amp;#39;s take a look at SQLite&amp;#39;s &lt;em&gt;storage classes&lt;/em&gt;. These are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INTEGER&lt;/code&gt; (7 different data types depending on size)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REAL&lt;/code&gt; (any floating point value)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TEXT&lt;/code&gt; (any string)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BLOB&lt;/code&gt; (raw binary data)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are equivalent to &lt;em&gt;data types&lt;/em&gt; for the purpose of a general discussion, but have a broader scope. When you create a table, you do not declare storage classes for each column, but &lt;em&gt;type affinities&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A certain type affinity determines what storage class(es) will be used to store a certain column. Because this is a rather elusive topic to discuss theoretically, let&amp;#39;s look at an example. Say we create the following table:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE employees (
    id INTEGER PRIMARY KEY,
    name TEXT,
    department TEXT,
    salary NUMERIC
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;INTEGER&lt;/code&gt;, &lt;code&gt;TEXT&lt;/code&gt;, and &lt;code&gt;NUMERIC&lt;/code&gt; are the columns&amp;#39; type affinities. For example, suppose I enter an integer number into the &lt;code&gt;department&lt;/code&gt; column. It will be converted into &lt;code&gt;TEXT&lt;/code&gt;, because the &lt;code&gt;TEXT&lt;/code&gt; affinity only uses the &lt;code&gt;TEXT&lt;/code&gt;, &lt;code&gt;NULL&lt;/code&gt;, and &lt;code&gt;BLOG&lt;/code&gt; storage classes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- stored as 1, &amp;#39;Anna Lee&amp;#39;, &amp;#39;123&amp;#39;, 85000
INSERT INTO employees (id, name, department, salary) VALUES (1, &amp;#39;Anna Lee&amp;#39;, 123, 85000);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the other hand, if I enter the string &lt;code&gt;&amp;quot;67000&amp;quot;&lt;/code&gt; as &lt;code&gt;salary&lt;/code&gt;, the &lt;code&gt;NUMERIC&lt;/code&gt; affinity will convert it to &lt;code&gt;INTEGER&lt;/code&gt;, because it is a &lt;em&gt;well formed number literal&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- stored as 2, &amp;#39;Mike Johnson&amp;#39;, &amp;#39;Customer Support&amp;#39;, 67000
INSERT INTO employees (id, name, department, salary) VALUES (2, &amp;#39;Mike Johnson&amp;#39;, &amp;#39;Customer Support&amp;#39;, &amp;#39;67000&amp;#39;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I were to enter &lt;code&gt;&amp;quot;eighty-five thousand&amp;quot;&lt;/code&gt;, it would just default to the &lt;code&gt;TEXT&lt;/code&gt; storage class.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- stored as 3, &amp;#39;Jane Smith&amp;#39;, &amp;#39;Engineering&amp;#39;, &amp;#39;eighty-five thousand&amp;#39;
INSERT INTO employees (id, name, department, salary) VALUES (3, &amp;#39;Jane Smith&amp;#39;, &amp;#39;Engineering&amp;#39;, &amp;#39;eighty-five thousand&amp;#39;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rules are rather complex, so check out the &lt;a href=&quot;https://www.sqlite.org/datatype3.html&quot;&gt;SQLite official docs on datatypes&lt;/a&gt;. The main takeaway here is that, somewhat counterintuitively, &lt;strong&gt;the column type affinities you declare need not reflect the actual data types&lt;/strong&gt; of the stored values.&lt;/p&gt;
&lt;p&gt;SQLite also lacks boolean, datetime, and JSONB datatypes, and UUIDs. Let&amp;#39;s take a look at these now.&lt;/p&gt;
&lt;h3&gt;No Boolean Datatype&lt;/h3&gt;
&lt;p&gt;Boolean values are commonly represented as &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; instead of &lt;code&gt;TRUE&lt;/code&gt; and &lt;code&gt;FALSE&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;No Datetime Datatype&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.sqlite.org/quirks.html&quot;&gt;SQLite docs&lt;/a&gt; recommend using the built-in conversion functions and that you store dates and times:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;As a TEXT string in the ISO-8601 format. Example: &amp;#39;2018-04-02 12:13:46&amp;#39;.&lt;/li&gt;
&lt;li&gt;As an INTEGER number of seconds since 1970 (also known as &amp;quot;unix time&amp;quot;).&lt;/li&gt;
&lt;li&gt;As a REAL value that is the fractional Julian day number.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;No JSONB Datatype&lt;/h3&gt;
&lt;p&gt;Especially for developers relying on the convenience of the PostgreSQL JSONB datatype, it&amp;#39;s important to point out that SQLite &lt;a href=&quot;https://www.sqlite.org/json1.html&quot;&gt;doesn&amp;#39;t use a binary format to store JSON&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Experiments have been unable to find a binary encoding that is smaller or faster than a plain text encoding.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most notably, SQLite&amp;#39;s JSON implementation doesn&amp;#39;t support BLOB storage.&lt;/p&gt;
&lt;h3&gt;No Universally Unique Identifiers (UUIDs)&lt;/h3&gt;
&lt;p&gt;Another characteristic that comes as a surprise to most developers is that SQLite lacks the concept of UUIDs. Not completely, as they can be &lt;a href=&quot;https://www.sqlite.org/loadext.html&quot;&gt;compiled and loaded&lt;/a&gt; as an &lt;a href=&quot;https://sqlite.org/src/file/ext/misc/uuid.c&quot;&gt;extension&lt;/a&gt;, but they are not built into the database per default.&lt;/p&gt;
&lt;p&gt;If you are interested in exploring the advantages and tradeoffs of flexible database types further, the &lt;a href=&quot;https://www.sqlite.org/flextypegood.html&quot;&gt;SQLite docs have a decent write-up&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Enough with the theoretical deliberations and dwelling on what SQLite doesn&amp;#39;t have, though. Let&amp;#39;s get back to building! Let&amp;#39;s see how we can use SQLite to store image prompts.&lt;/p&gt;
&lt;h2&gt;Storing Image Prompts Directly in SQLite&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.sqlite.org/fasterthanfs.html&quot;&gt;SQLite documentation&lt;/a&gt; claims that the database is excellently suited to store small blobs of binary data (e.g., thumbnails). We probably still would use a CDN in production, but it&amp;#39;s interesting enough to give it a try.&lt;/p&gt;
&lt;p&gt;For starters, let&amp;#39;s add a &lt;code&gt;content_type&lt;/code&gt; column to our &lt;code&gt;prompts&lt;/code&gt; table. It will come in handy later:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g migration AddContentTypeToPrompts content_type:string
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, the transcoding into a data URL can be done in the model itself:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/prompt.rb
  class Prompt &amp;lt; ApplicationRecord
    include AccountScoped

    belongs_to :account
    has_rich_text :description

-   validates :title, :prompt_image, presence: true
+   validates :title, :prompt_image, :content_type, presence: true
+
+   def data_url
+     encoded_data = Base64.strict_encode64(prompt_image)
+
+     &amp;quot;data:image/#{content_type};base64,#{encoded_data}&amp;quot;
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now start to simplify our controller code a bit. If we assign both &lt;code&gt;prompt_image&lt;/code&gt; and &lt;code&gt;content_type&lt;/code&gt; to &lt;code&gt;@prompt&lt;/code&gt; from the uploaded file, we can scrap the helper methods &lt;code&gt;prompt_image&lt;/code&gt; and &lt;code&gt;prompt_image_data_url&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/controllers/prompts_controller.rb

  class PromptsController &amp;lt; ApplicationController
    # ....

    def create
+     prompt_image = prompt_params.delete(:prompt_image)
+
      @prompt = Prompt.new(prompt_params)
      @prompt.account = Current.account
+     @prompt.prompt_image = prompt_image.read
+     @prompt.content_type = prompt_image.content_type

      model = Replicate.client.retrieve_model(&amp;quot;stability-ai/stable-diffusion-img2img&amp;quot;)
      version = model.latest_version
-     version.predict({prompt: prompt_params[:title],
-       image: prompt_image_data_url}, replicate_rails_url)
-
+     version.predict({prompt: prompt_params[:title],
+       image: @prompt.data_url}, replicate_rails_url)

      # ...
    end

      # ...

    private

      # ...

-
-     def prompt_image_data_url
-       encoded_data = Base64.strict_encode64(prompt_image.read)
-
-       &amp;quot;data:image/#{prompt_image.content_type};base64,#{encoded_data}&amp;quot;
-     end
-
-     def prompt_image
-       @prompt_image ||= prompt_params[:prompt_image]
-     end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All that&amp;#39;s left to do now is to actually display the image in our view. For this, we can use the same &lt;code&gt;data_url&lt;/code&gt; helper method from the model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/prompts/_prompt.html.erb --&amp;gt;
  &amp;lt;div id=&amp;quot;&amp;lt;%= dom_id prompt %&amp;gt;&amp;quot;&amp;gt;
    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Title:&amp;lt;/strong&amp;gt;
      &amp;lt;%= prompt.title %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Description:&amp;lt;/strong&amp;gt;
      &amp;lt;%= prompt.description %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Prompt image:&amp;lt;/strong&amp;gt;
-     &amp;lt;%= image_tag prompt.prompt_image %&amp;gt;
+     &amp;lt;%= image_tag prompt.data_url %&amp;gt;
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is a tiny blemish here, though. We are potentially storing and transferring way too much image data. StableDiffusion needs only a 768 pixel wide (or long) image to work, so we will scale it down beforehand. To accomplish this, we&amp;#39;ll install the &lt;a href=&quot;https://github.com/janko/image_processing&quot;&gt;ImageProcessing library&lt;/a&gt; and use &lt;a href=&quot;https://www.libvips.org/&quot;&gt;libvips&lt;/a&gt; to transform our image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: you have to install libvips on your operating system for this to work. Instructions are available &lt;a href=&quot;https://github.com/libvips/libvips#readme&quot;&gt;in the README&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;image_processing&lt;/code&gt; gem is already installed in an off-the-shelf Rails 7 app, so we can start writing our transformation logic. We&amp;#39;ll do this in a &lt;code&gt;before_save&lt;/code&gt; callback in our &lt;code&gt;Prompt&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/prompt.rb
+ require &amp;quot;vips&amp;quot;

  class Prompt &amp;lt; ApplicationRecord
    include AccountScoped

    # ...

+   before_save :scale_prompt_image

    # ...

+   private
+
+   def scale_prompt_image
+     image = Vips::Image.new_from_buffer(prompt_image, &amp;quot;&amp;quot;)
+     pipeline = ImageProcessing::Vips.source(image)
+
+     self.prompt_image = pipeline.resize_to_fit(768, 768).call.read
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;resize_to_fit&lt;/code&gt; method will scale an image to fit the specified dimensions while preserving the aspect ratio. For example, if we upload an image with 1024x768 pixels, it will be scaled down to 768x576.&lt;/p&gt;
&lt;p&gt;This completes the preprocessing logic of our image generation pipeline.&lt;/p&gt;
&lt;h2&gt;Overall Notes on SQLite for Rails and Its Performance&lt;/h2&gt;
&lt;p&gt;Although some traditional data types like boolean and datetime are absent, SQLite excels in handling binary data, making it an excellent choice for certain applications. If you are interested in how SQLite compares to other Relational Database Management Systems (RDBMS&amp;#39;s) in terms of performance, have a look at the &lt;a href=&quot;https://github.com/oldmoe/litestack/blob/master/BENCHMARKS.md&quot;&gt;benchmarks&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A Quick Note On Litestream&lt;/h2&gt;
&lt;p&gt;As a final side-note, I would like to touch on &lt;a href=&quot;https://litestream.io/how-it-works/&quot;&gt;Litestream&lt;/a&gt;, a performant and safe way to replicate your SQLite databases to S3-compatible object storage in a streaming manner. Not only does it provide a very cost-effective backup solution, but it is also a very simple way to &lt;a href=&quot;https://litestream.io/getting-started/#restoring-your-database&quot;&gt;restore your database&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Up Next: Asynchronously Handling Image Predictions with LiteJob&lt;/h2&gt;
&lt;p&gt;In this article, we&amp;#39;ve delved into SQLite&amp;#39;s flexible typing and type affinity, harnessing its power to process and store binary data. We have looked into optimizing image storage and leveraged SQLite&amp;#39;s unique data-handling features.&lt;/p&gt;
&lt;p&gt;Next, we&amp;#39;ll consider how to handle the actual generation and persistence of prediction images. We have only implemented a webhook stub so far, and created the predictions in a peculiar way in our &lt;code&gt;PromptsController&lt;/code&gt;. We will look into performing this step asynchronously with LiteJob.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Advanced Usages of Action Policy for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/10/18/advanced-usages-of-action-policy-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2023/10/18/advanced-usages-of-action-policy-for-ruby-on-rails.html</id>
    <published>2023-10-18T00:00:00+00:00</published>
    <updated>2023-10-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore some advanced usages of Action Policy, including pre-checks, scopes, caching, and aliases.</summary>
    <content type="html">&lt;p&gt;In part one of this series, we looked at some basic usages of Action Policy. Now we&amp;#39;ll leverage Action Policy for more advanced authorization use cases.&lt;/p&gt;
&lt;p&gt;First up, let&amp;#39;s explore applying pre-checks.&lt;/p&gt;
&lt;h2&gt;Pre-checks&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s say we want users with &amp;quot;editor&amp;quot; status to have access to a post&amp;#39;s &lt;code&gt;show&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, and &lt;code&gt;destroy&lt;/code&gt; actions, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/posts_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
    def show?
      user.editor || user.reader || user.id == record.user_id
    end

    def update?
      user.editor || (user.id == record.user_id)
    end

    def destroy?
      user.editor || user.id == record.user_id
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can refactor this policy code using an Action Policy pre-check that extracts common rules into &lt;code&gt;pre-checks&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/posts_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
  pre-check :allow_editors

  def show?
    user.reader || user.id == record.user_id
  end

  def update?
    user.id == record.user_id
  end

  def destroy?
    user.id == record.user_id
  end

  private

  def allow_editors
    allow! if user.editor
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Scopes&lt;/h2&gt;
&lt;p&gt;Another Action Policy feature that deserves our attention is &lt;code&gt;scoping&lt;/code&gt;. Scopes filter data depending on any authorization rules you&amp;#39;ve set.&lt;/p&gt;
&lt;p&gt;Using our blog app as an example, let&amp;#39;s say we want to apply the following rules to the posts index action:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List all posts for all users with the &amp;quot;editor&amp;quot; role&lt;/li&gt;
&lt;li&gt;List only posts that a user has created if they have the &amp;quot;author&amp;quot; role&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without using any Action Policy authorization, the posts controller index action looks like any generic index action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController
  ...

  def index
    @posts = Post.all
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ll need to utilize Action Policy scoping to refactor this action and apply the outlined access rules. Scoping rules are defined within a policy class and applied in the respective controller using the &lt;code&gt;authorized_scope&lt;/code&gt; or &lt;code&gt;authorized&lt;/code&gt; methods.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;First modify the Post policy, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/posts_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
  ...

  relation_scope do |scope|
      if user.editor?
        scope
      end
    end
  end

  relation_scope(:own) do |scope|
      scope.where(user: user)
    end
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then the posts controller&amp;#39;s index action, as shown below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController

  ...

  def index
    @posts = authorized_scope(Post, type: :relation, as: :own)
  end
  ...

end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caching in Action Policy for Ruby and Rails&lt;/h2&gt;
&lt;p&gt;Action Policy is a performant authorization library, partly thanks to its efficient use of caching.&lt;/p&gt;
&lt;p&gt;It has several cache layers for you to leverage, including memoization and external caching.&lt;/p&gt;
&lt;h3&gt;Memoization&lt;/h3&gt;
&lt;p&gt;Consider a situation where the same rule is called several times on an object instance. For example, let&amp;#39;s say we need to load all comments associated with a post within the &lt;code&gt;index&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController

  ...

  def index
    @posts = authorized_scope(Post.includes(comments), ...)
  end
  ...

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, imagine there&amp;#39;s an &amp;quot;edit&amp;quot; link for every comment a post has within the index view. As you can see, this is a resource-intensive undertaking since we need to check if the current user is allowed to first access the post index, and then edit a post&amp;#39;s comments. If a post has multiple comments, this would mean loading the authorization policy multiple times.&lt;/p&gt;
&lt;p&gt;In a situation like this, Action Policy will re-use the required policy instance — specifically, the &lt;code&gt;record.policy_cache_key&lt;/code&gt; — as one of the identifiers in the local store.&lt;/p&gt;
&lt;p&gt;This kind of caching works well for short-lived requests, but if you need to cache resource-intensive rules that will be persisted across requests, using an external cache store is a better option.&lt;/p&gt;
&lt;h3&gt;Using an External Cache Store&lt;/h3&gt;
&lt;p&gt;If you need to run access control rules that utilize complex database queries, for example, you can use an external cache store such as Redis. Your rules cache will be made available across requests. You just need to remember to explicitly define which rules to cache within the policy class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/post_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
  cache :index?

  def show?
    # some complex database heavy rules...
  end

  ....
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And configure the cache store for your app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/application.rb

Rails.application.configure do |config|
  config.action_policy.cache_store = :redis_cache_store
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Quick tip:&lt;/strong&gt; There&amp;#39;s a lot more to this. Dig into the &lt;a href=&quot;https://actionpolicy.evilmartians.io/#/caching?id=rule-cache&quot;&gt;Action Policy caching documentation&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Aliases in Action Policy for Rails&lt;/h2&gt;
&lt;p&gt;An alias is an alternative way to name policies so that they make more sense.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s check out an example using our blog app. Consider this Post policy:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/post_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
  alias_rule :destroy?, :update?, to: :manage_post?

  ...

  def manage_article?
    user.editor || user.id == record.user_id
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we combine the &lt;code&gt;update?&lt;/code&gt; and &lt;code&gt;destroy?&lt;/code&gt; rules into one alias called &lt;code&gt;manage_post?&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then we use it in the controller, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController

  ...

  def update
    authorize! :manage_post?, @post
    ...
  end

  # DELETE /posts/1 or /posts/1.json
  def destroy
    authorize! :manage_post?, @post
    ...
  end
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let&amp;#39;s quickly look at how to handle unauthorized access.&lt;/p&gt;
&lt;h2&gt;Handling Unauthorized Access in a Ruby and Rails App&lt;/h2&gt;
&lt;p&gt;If a user tries to access a resource they shouldn&amp;#39;t, an &lt;code&gt;ActionPolicy::Unauthorized&lt;/code&gt; error is raised.&lt;/p&gt;
&lt;p&gt;You need to explicitly handle this error in your app&amp;#39;s &lt;code&gt;ApplicationController&lt;/code&gt;, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/application_controller.rb

class ApplicationController &amp;lt; ActionController::Base
  rescue_from ActionPolicy::Unauthorized do
    redirect_to root_path, alert: &amp;#39;Access denied.&amp;#39;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&amp;#39;s it!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we explored advanced Action Policy features, including pre-checks, scopes, caching, aliases, and finally, handling unauthorized access.&lt;/p&gt;
&lt;p&gt;From basic rules to complex conditional scenarios, Action Policy has everything you need to handle almost any authorization scenario. Use this library in your next project and see how powerful it is.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Expressive Ruby and Rails: Communicate Effectively With Your Code</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/10/11/expressive-ruby-and-rails-communicate-effectively-with-your-code.html"/>
    <id>https://blog.appsignal.com/2023/10/11/expressive-ruby-and-rails-communicate-effectively-with-your-code.html</id>
    <published>2023-10-11T00:00:00+00:00</published>
    <updated>2023-10-11T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s see why expressive code is important and how to best use some Ruby methods.</summary>
    <content type="html">&lt;p&gt;Ruby is an expressive language. This is no accident; Matz very consciously designed Ruby as an intuitive language to more or less read like English. It&amp;#39;s safe to say that he succeeded. Methods are named very carefully, and do what they say they do; they also tend to have inverse methods which do the opposite.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll look at why expressive code is important and its impact on your productivity as a developer. Then, we&amp;#39;ll explore how to best use some of Ruby&amp;#39;s methods.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;The Importance of Expressiveness in Ruby&lt;/h2&gt;
&lt;p&gt;Expressive code goes beyond readability (&amp;quot;What does this code &lt;em&gt;do?&lt;/em&gt;&amp;quot;), and clearly communicates intent: it clues us into the developer&amp;#39;s considerations and concerns, and the edge cases they were accounting for. It makes clear not just &lt;em&gt;what&lt;/em&gt; the code does, but &lt;em&gt;why&lt;/em&gt; it was written how it was.&lt;/p&gt;
&lt;p&gt;Why is this important? It saves us from spending a lot of time familiarizing ourselves with the context of the code we&amp;#39;re working on, clues us in to what we should be looking out for when we modify it, and keeps us from potentially bulldozing carefully-coded functionality and measures meant to safeguard our application.&lt;/p&gt;
&lt;p&gt;Ruby takes to heart the principle that good code should be self-documenting; it largely obviates the need for comments. This is likely one of the reasons we often associate commented code in Ruby with bad code: because, in many cases, it is. Working with external APIs or unfamiliar gems is one thing, but generally, for most of our core functionality, writing idiomatic Ruby means we don&amp;#39;t need to write (or read) comments. Usually, any time I find myself with comments all over the place, it&amp;#39;s a sign I need to refactor.&lt;/p&gt;
&lt;p&gt;Ultimately, expressive code saves us time, and our company money.&lt;/p&gt;
&lt;h2&gt;The Impact of Expressive Coding on Productivity&lt;/h2&gt;
&lt;p&gt;What problems do we face if we aren&amp;#39;t conscientious about how we write and structure our code? The answer is at once straightforward and easy to forget in the moment. The most obvious issue — a lack of clarity — in addition to being its own problem, can lead to a host of other headaches:&lt;/p&gt;
&lt;h3&gt;Opacity&lt;/h3&gt;
&lt;p&gt;In addition to being confusing, inexpressive code also turns the thought process of the developer who coded it into something of a black box, rather than a window into their decision-making.&lt;/p&gt;
&lt;p&gt;As touched on above, this can force the developer maintaining our code to delve into the implementation in greater detail than they should to get context. This only serves to derail us from our task at hand, and makes our lives harder.&lt;/p&gt;
&lt;h3&gt;Loss of Trust&lt;/h3&gt;
&lt;p&gt;When a particular feature or area lacks expressiveness, it can be frustrating, but it is also an opportunity to improve our app. But when a lack of expressiveness is a pattern across our entire codebase, we lose faith in our code as a reliable source of truth to communicate the potential values it may be handling.&lt;/p&gt;
&lt;p&gt;This inevitably leads to constant defensive programming, which can serve to further muddy the waters. We may wind up accounting for potential values that aren&amp;#39;t even real possibilities, magnifying the existing problem and further eroding trust. In addition, we might add verbosity to our code, and send potentially misleading signals to other developers (and even our future selves) about what we&amp;#39;re accounting for and trying to accomplish.&lt;/p&gt;
&lt;p&gt;On the other hand, a lack of trust can lead to bad assumptions when the &lt;em&gt;right&lt;/em&gt; method is actually being used. Consider a scenario where someone has just spent time cleaning up a section of code and spots something like &lt;code&gt;if @user.address.present?&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Rather than stopping and thinking: &amp;quot;hey, there must be a reason &lt;code&gt;present?&lt;/code&gt; was used,&amp;quot; they might think: &amp;quot;hey, this particular field is optionally set by the user... so either they set it and it&amp;#39;s a string, or it wasn&amp;#39;t set, so it&amp;#39;s &lt;code&gt;nil&lt;/code&gt;.&amp;quot; So they change it to &lt;code&gt;if @user.address&lt;/code&gt;. Except, what happens when the user has a value set, and then later removes it and submits the form? We&amp;#39;ll now have an empty string (which now, without &lt;code&gt;present?&lt;/code&gt;, evaluates as truthy).&lt;/p&gt;
&lt;h3&gt;Type Issues&lt;/h3&gt;
&lt;p&gt;Constantly having to check for potential data types and the possibility of &lt;code&gt;nil&lt;/code&gt; values can be genuinely exhausting and &lt;em&gt;really&lt;/em&gt; slow us down. It not only wastes time by forcing us to work out the possible data types of inputs, but it can also break our focus and flow, and add unnecessary cognitive overhead.&lt;/p&gt;
&lt;p&gt;In some ways, we can think of Ruby&amp;#39;s expressiveness as an answer to a lack of static typing. By employing clear variable names and self-explanatory methods that do what they say in a fixed, predictable manner, we can automatically infer the type (or potential types) of data we&amp;#39;re working on in any given line with little effort.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Maintenance and Extensibility Issues&lt;/h3&gt;
&lt;p&gt;Inexpressive code can be a nightmare to maintain, and it often leads to tangled code, quickly becoming tough to modify without breaking something. It&amp;#39;s even harder to extend with new features and functionality, and can become a bug-prone black box that&amp;#39;s slow and challenging to develop for.&lt;/p&gt;
&lt;h3&gt;Hurting the Bottom Line&lt;/h3&gt;
&lt;p&gt;We pile up technical debt, which costs us increasingly painful amounts of time, loses our business money, and potentially hurts our reputation if this leads to missed deadlines or buggy releases.&lt;/p&gt;
&lt;p&gt;On top of all this, the less expressive our code is, the more painful turnover becomes; onboarding new developers takes longer, as it will be a while before they&amp;#39;re productive and know the codebase, and losing developers who do know it has a greater impact. And, of course, even when they&amp;#39;re up and running, they and everyone else working on the application will simply be less productive than they could be.&lt;/p&gt;
&lt;p&gt;Now, let&amp;#39;s focus on how we should best use methods in Ruby and Rails to avoid some of these issues.&lt;/p&gt;
&lt;h2&gt;Comparing Similar Ruby and Rails Methods&lt;/h2&gt;
&lt;p&gt;As we touched on early in this post, Ruby and Rails come together to offer us a wide variety of methods that accomplish an array of everyday tasks and answer basic questions we might need to ask as programmers. At first glance, there might appear to be a lot of overlap, and we may think that many of these methods can simply be used interchangeably without issue.&lt;/p&gt;
&lt;p&gt;Of course, as we know, these methods were named very carefully. Both Ruby and Rails were designed with close attention to what these methods do and don&amp;#39;t do.&lt;/p&gt;
&lt;p&gt;As a result, methods (excluding aliases) each behave slightly differently, with different intended purposes. Their correct use can help us provide specific context to clue in anyone reading our code to what&amp;#39;s going on. Conversely, failing to do so can have the opposite effect. Let&amp;#39;s take a look at some methods that sometimes get used interchangeably, to our detriment — starting with &lt;code&gt;nil?&lt;/code&gt;, &lt;code&gt;empty?&lt;/code&gt;, &lt;code&gt;blank?&lt;/code&gt;, and &lt;code&gt;none?&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;nil?&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This method does exactly what you&amp;#39;d expect: it tells us whether a value is &lt;code&gt;nil&lt;/code&gt;. I generally like to use it to avoid doing things like &lt;code&gt;!@user&lt;/code&gt;, but it can also indicate that a value is &lt;em&gt;not&lt;/em&gt; a boolean, if that&amp;#39;s not already clear. While it&amp;#39;s difficult to misuse, it&amp;#39;s easy to use another, technically valid but misleading method (such as &lt;code&gt;blank?&lt;/code&gt;) in its place. Don&amp;#39;t do this!&lt;/p&gt;
&lt;p&gt;If a value can only be &lt;code&gt;nil&lt;/code&gt; or truthy, &lt;code&gt;nil?&lt;/code&gt; should always be used. Doing otherwise can mislead anyone modifying the code and potentially lead them to code for non-existent edge cases, which is both a waste of time, and adds unnecessary complexity and verbosity.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;empty?&lt;/code&gt; (Nearly Opposite Method: &lt;code&gt;any?&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;empty?&lt;/code&gt; is useful when you want to check whether a collection array, string, hash, or other type of collection contains no elements. It&amp;#39;s a straightforward and intuitive method that returns &lt;code&gt;true&lt;/code&gt; if the collection contains no elements, and &lt;code&gt;false&lt;/code&gt; otherwise.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;&amp;quot;&amp;quot;.empty?&lt;/code&gt; and &lt;code&gt;[].empty?&lt;/code&gt; both return &lt;code&gt;true&lt;/code&gt;, because the string and the array do not contain any elements. Similarly, &lt;code&gt;{}.empty?&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; because the hash does not contain any key-value pairs.&lt;/p&gt;
&lt;p&gt;When you use &lt;code&gt;empty?&lt;/code&gt;, you&amp;#39;re communicating to other developers that the object you&amp;#39;re checking should be a string or collection of some sort, and you&amp;#39;re interested in whether it contains anything.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;blank?&lt;/code&gt; (Opposite Method: &lt;code&gt;present?&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://apidock.com/rails/Object/blank%3F&quot;&gt;&lt;code&gt;blank?&lt;/code&gt;&lt;/a&gt; is often used to check if a string is empty. However, as you&amp;#39;re likely aware, it checks for more than just empty strings; it will return &lt;code&gt;true&lt;/code&gt; if a value is &lt;code&gt;nil&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, or an empty array or hash. Essentially, it asks and answers &lt;code&gt;!object || object.empty?&lt;/code&gt;, so we expect that the potential values are probably either &lt;code&gt;nil&lt;/code&gt;, or an empty string, array, or hash — this is what you&amp;#39;re communicating to other developers when you use this.&lt;/p&gt;
&lt;p&gt;It is often used in place of &lt;code&gt;empty?&lt;/code&gt; or &lt;code&gt;nil?&lt;/code&gt; when it really shouldn&amp;#39;t be.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;none?&lt;/code&gt; (Opposite Method: &lt;code&gt;all?&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;This asks whether an array or hash is empty or only contains falsy values, and it&amp;#39;s not too often misused. Sometimes, though, we might see someone use &lt;code&gt;blank?&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;This is a mistake. In addition to the fact that &lt;code&gt;blank?&lt;/code&gt; asks: &amp;quot;is this object falsy or empty?&amp;quot;, &lt;code&gt;none?&lt;/code&gt; asks not only: &amp;quot;is this array or hash empty?&amp;quot;, but also: &amp;quot;...or does this array have no truthy values?&amp;quot;. &lt;code&gt;[false, nil].blank?&lt;/code&gt;, for example, will return &lt;code&gt;false&lt;/code&gt;, but &lt;code&gt;[false, nil].none?&lt;/code&gt; will return &lt;code&gt;true&lt;/code&gt; (likewise, &lt;code&gt;any?&lt;/code&gt; would return &lt;code&gt;false&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;So not only have we introduced ambiguity, we&amp;#39;ve introduced a bug waiting to happen — and sadly, this one is not a feature.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;size&lt;/code&gt; Vs. &lt;code&gt;length&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;size&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Put simply: &lt;a href=&quot;https://apidock.com/rails/v6.1.3.1/ActiveRecord/Associations/CollectionProxy/size&quot;&gt;&lt;code&gt;size&lt;/code&gt;&lt;/a&gt; is for collections (ActiveRecord Collections, arrays), and should almost always be used for them. It never makes sense to call on a string.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;length&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;length&lt;/code&gt; is generally for strings, and except in specific cases, shouldn&amp;#39;t be used for collections (and probably never for arrays).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;boolean?&lt;/code&gt; Methods in Ruby&lt;/h3&gt;
&lt;p&gt;Every Ruby &lt;code&gt;boolean?&lt;/code&gt; method answers a yes or no question: &amp;quot;Is this value truthy or not?&amp;quot;. Most Rails developers are aware of this and generally use the question mark to good effect, adding it to the end of the boolean methods that they write. But many may not be aware that Rails itself does, too.&lt;/p&gt;
&lt;p&gt;Any Rails object automatically responds to a column with a &lt;code&gt;?&lt;/code&gt; on the end. So, a &lt;code&gt;verified&lt;/code&gt; column on your user table can be called with &lt;code&gt;@user.verified?&lt;/code&gt;. Setting up a standard for your team to only use these methods for &lt;code&gt;boolean&lt;/code&gt; columns can help provide even more context for other (especially new) team members. They won&amp;#39;t have to check the schema to see if &lt;code&gt;verified&lt;/code&gt; is maybe a &lt;code&gt;timestamp&lt;/code&gt; or even a &lt;code&gt;string&lt;/code&gt; rather than a &lt;code&gt;boolean&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And let&amp;#39;s not forget the methods we write ourselves; generally speaking, any method we write that answers a yes-or-no question should return a boolean method, and end with a question mark.&lt;/p&gt;
&lt;p&gt;For example, say we have a method that tells us whether or not a value is outside a particular limit. We can define something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Vendor
  def use_exceeds_limit?
    get_time_slot_use_count(1.month) &amp;gt; time_slot_tier.limit
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then conditionally email to let users who have exceeded the limit know they&amp;#39;ll be charged at a higher rate for the month:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class DailyLimitCheckJob &amp;lt; ApplicationJob
  def perform(vendor)
    VendorMailer.alert_vendor_limit_exceeded(vendor) if vendor.use_exceeds_limit?
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, great, we know that methods that end in a &lt;code&gt;?&lt;/code&gt; return a boolean. But what about other methods?&lt;/p&gt;
&lt;p&gt;Note the method &lt;code&gt;get_time_slot_use_count&lt;/code&gt;. It may be slightly more explicit than it needs to be, but it also tells us exactly what we&amp;#39;re getting: an &lt;code&gt;Integer&lt;/code&gt;. If it were &lt;code&gt;get_time_slot_usage&lt;/code&gt;, we might wonder if it wasn&amp;#39;t a &lt;code&gt;Float&lt;/code&gt;, or even an &lt;code&gt;ActiveSupport::Duration&lt;/code&gt; (with a return value like &lt;code&gt;173.hours&lt;/code&gt;). Being explicit helps us know what we&amp;#39;ll get without looking at the actual implementation elsewhere in the &lt;code&gt;Vendor&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;So, we can also apply this principle elsewhere: we probably don&amp;#39;t need it for strings like &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;title&lt;/code&gt;, but if we&amp;#39;re assigning variables to dynamically hold parts of a query, we probably do want to name it something like &lt;code&gt;query_string&lt;/code&gt;, rather than &lt;code&gt;query&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same is true of arrays and hashes: generally, a plural name (say, &lt;code&gt;products&lt;/code&gt;) indicates that we&amp;#39;re dealing with a collection. We often don&amp;#39;t need to explicitly specify &amp;quot;hash&amp;quot;, either; consider &lt;code&gt;pagination_options&lt;/code&gt;. We know options in contexts like these are typically hashes. But sometimes we have &lt;code&gt;products&lt;/code&gt; as a key-value pair collection; we should probably name this &lt;code&gt;products_hash&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Further Resources&lt;/h2&gt;
&lt;p&gt;For those seeking a deeper dive into the world of Ruby, a number of resources are available.&lt;/p&gt;
&lt;p&gt;Check out the books &lt;a href=&quot;https://www.amazon.co.uk/Ruby-Programming-Language-David-Flanagan/dp/0596516177&quot;&gt;The Ruby Programming Language&lt;/a&gt; and &lt;a href=&quot;https://www.amazon.co.uk/Ruby-Best-Practices-Gregory-Brown/dp/0596523009&quot;&gt;Ruby Best Practices&lt;/a&gt;, the &lt;a href=&quot;https://ruby-doc.org/&quot;&gt;official Ruby docs&lt;/a&gt;, and &lt;a href=&quot;https://guides.rubyonrails.org/&quot;&gt;Rails Guides&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ve walked through the pitfalls of inexpressive code, including a lack of clarity, a decrease in trust, and other problems. In contrast, well-crafted, expressive code can help avoid issues like technical debt, make our lives easier, and save money.&lt;/p&gt;
&lt;p&gt;The design philosophy of Ruby prioritizes expressiveness and intuitiveness. Properly leveraging the tools Ruby provides to communicate your intent can eliminate the need for extensive comments and lead to substantial savings in both time and resources.&lt;/p&gt;
&lt;p&gt;To fully unlock the power Ruby and Rails affords us, it&amp;#39;s critical to be conscientious in your variable and method naming, and to use the right method for the job. Expressive code makes you — and everyone around you — more productive.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Using Action Policy for a Ruby on Rails App: The Basics</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/10/04/using-action-policy-for-a-ruby-on-rails-app-the-basics.html"/>
    <id>https://blog.appsignal.com/2023/10/04/using-action-policy-for-a-ruby-on-rails-app-the-basics.html</id>
    <published>2023-10-04T00:00:00+00:00</published>
    <updated>2023-10-04T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the first part of a two-part series, we&#039;ll introduce how to use the Action Policy gem for a Ruby on Rails blog application.</summary>
    <content type="html">&lt;p&gt;To keep your app secure, you need to control who and what can access it. Access control can be categorized into authentication — &amp;quot;who&amp;quot; to allow — and authorization — &amp;quot;what&amp;quot; they can access.&lt;/p&gt;
&lt;p&gt;Authentication is a subject for another day, but when it comes to user authorization, you generally have two ways to go about it: using a role-based or resource-based strategy.&lt;/p&gt;
&lt;p&gt;In this two-part series, we&amp;#39;ll take a deep dive into using the Action Policy gem for a Ruby on Rails blog application.&lt;/p&gt;
&lt;p&gt;In this part, we&amp;#39;ll cover the basics of Action Policy.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ruby (we&amp;#39;re using version 3.2.2)&lt;/li&gt;
&lt;li&gt;Rails (using version 7.0.7)&lt;/li&gt;
&lt;li&gt;Some experience using Ruby&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get into it by first defining resource-based authorization.&lt;/p&gt;
&lt;h2&gt;What Is Resource-Based Authorization?&lt;/h2&gt;
&lt;p&gt;Where role-based authorization focuses on setting user permissions according to predefined user roles, resource-based authorization enforces access by setting rules on the actual resources within an application. Each resource is associated with a policy that explicitly defines what a user can do on that resource.&lt;/p&gt;
&lt;p&gt;Even though this article is focused on resource-based authorization, knowing the differences between the two authorization strategies means you are better equipped to know what each can do.&lt;/p&gt;
&lt;h3&gt;When to Use Role-Based Authorization&lt;/h3&gt;
&lt;p&gt;You should use role-based authorization for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simple applications&lt;/strong&gt; - If you&amp;#39;re working on an app with a straightforward permissions system and fewer user roles, then a role-based authorization strategy could work for you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Well-defined user groups&lt;/strong&gt; - If your app has well-defined user groups such as &amp;quot;admins&amp;quot;, &amp;quot;editors&amp;quot;, &amp;quot;writers&amp;quot;, and so forth, use role-based authorization as it handles access control at the user role level.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;When to Use Resource-Based Authorization&lt;/h3&gt;
&lt;p&gt;Resource-based authorization is great for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dynamic or complex access control&lt;/strong&gt; - When your application&amp;#39;s authorization needs evolve frequently or are determined by dynamic conditions, then a resource-based authorization system makes for a better choice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fine-grained control&lt;/strong&gt; - Useful for when you need to allow or deny access to resources based on multiple conditions (for example, a Rails helpdesk app where users submit support tickets based on a list of dynamic categories). Assuming support staff are assigned tickets by category, this is a scenario where resource-based authorization would really shine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Object-oriented control&lt;/strong&gt; - Because resource-based authorization happens at the object level, defining complex object-oriented rules is easier when you use resource-based authorization techniques.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, whether you choose between role-based and resource-based authorization will depend on your application&amp;#39;s unique characteristics.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s now turn our attention to the Action Policy gem.&lt;/p&gt;
&lt;h2&gt;The Action Policy Gem for Ruby and Rails&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/palkan/action_policy&quot;&gt;Action Policy&lt;/a&gt; is a flexible, extensible, and performant authorization framework for Ruby and Rails apps. It uses multiple caching strategies out of the box, making it very fast, especially if your authorization rules require database queries.&lt;/p&gt;
&lt;p&gt;Another feature that makes this gem ideal for building resource-based rules is its ability to be customized. It provides several Ruby classes and modules that can be combined in many ways. You can pretty much set fine-grained access control rules beyond Rails controller, anywhere in your app.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://actionpolicy.evilmartians.io/#/&quot;&gt;Action Policy project homepage&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;p&gt;Now let&amp;#39;s go over the Rails app we&amp;#39;ll be building today.&lt;/p&gt;
&lt;h2&gt;Create the Rails App&lt;/h2&gt;
&lt;p&gt;Moving forward, we&amp;#39;ll reference a Rails 7 blog application where users can Create, Read, Update, and Delete (CRUD) posts.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll progressively define an action policy for the &lt;code&gt;Post&lt;/code&gt; model that offers resource-based access control. You&amp;#39;ll learn how to use Action Policy to make this access control strategy work.&lt;/p&gt;
&lt;p&gt;Go ahead and generate a new Rails application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails new blog_app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; We&amp;#39;ll use &lt;a href=&quot;https://picocss.com/&quot;&gt;Pico CSS&lt;/a&gt; styles for our example app, but you can use whatever you like.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Then quickly scaffold a &lt;code&gt;Post&lt;/code&gt; resource that we&amp;#39;ll use throughout the rest of the tutorial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g scaffold Post title body
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now run the migration with &lt;code&gt;rails db:migrate&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Setting up User Authentication&lt;/h2&gt;
&lt;p&gt;As much as this article is about user authorization, there&amp;#39;s something important we need to cover: user authentication. Without it, any authorization policies we try to define later on will be useless. But there is no need to write authentication from scratch. Let&amp;#39;s use &lt;a href=&quot;https://github.com/heartcombo/devise&quot;&gt;Devise&lt;/a&gt;.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Installing Devise&lt;/h3&gt;
&lt;p&gt;Begin by adding the Devise gem to your Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

gem &amp;quot;devise&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install it and generate a &lt;code&gt;User&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle install
rails generate devise:install
rails generate devise User
rails db:create
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also, remember to add &lt;code&gt;config.action_mailer.default_url_options = { host: &amp;#39;localhost&amp;#39;, port: 3000 }&lt;/code&gt; to the app&amp;#39;s development configuration.&lt;/p&gt;
&lt;p&gt;Next up, let&amp;#39;s implement a few basic user roles to test different user access scenarios for the &lt;code&gt;Post&lt;/code&gt; resource.&lt;/p&gt;
&lt;h3&gt;Defining Basic User Roles&lt;/h3&gt;
&lt;p&gt;First, add the &lt;code&gt;role&lt;/code&gt; column to the user table. Run this command to generate a migration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g migration AddColumnRoleToUser role:integer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; We use an integer data type for the role, so we can use an &lt;code&gt;enum&lt;/code&gt; — a quick and easy way to implement roles.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now run the migration with &lt;code&gt;rails db:migrate&lt;/code&gt;, then open up the &lt;code&gt;User&lt;/code&gt; model and edit it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/users.rb

class User &amp;lt; ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

         enum :role, { reader: 0, author: 1, editor: 2 }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that done, let&amp;#39;s shift our focus to installing Action Policy.&lt;/p&gt;
&lt;h2&gt;Setting up Action Policy for Ruby and Rails&lt;/h2&gt;
&lt;p&gt;Open the app&amp;#39;s &lt;code&gt;Gemfile&lt;/code&gt; and add the line below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;#  Gemfile

gem &amp;quot;action_policy&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the command &lt;code&gt;bundle install&lt;/code&gt; to install Action Policy.&lt;/p&gt;
&lt;p&gt;Finalize the installation by running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g action_policy:install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should give us a base — the &lt;code&gt;ApplicationPolicy&lt;/code&gt; class under &lt;code&gt;app/policies&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/application_policy.rb

class ApplicationPolicy &amp;lt; ActionPolicy::Base
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Basic Usage of Action Policy&lt;/h2&gt;
&lt;p&gt;Action Policy&amp;#39;s core foundation is a &lt;code&gt;policy&lt;/code&gt; class, &lt;code&gt;ApplicationPolicy&lt;/code&gt;, where you can define global configurations from which all policies can inherit. A good recommendation is to place all policies under &lt;code&gt;app/policies&lt;/code&gt;, and to separate policies according to the resources in your app. For example, if you have a &lt;code&gt;Post&lt;/code&gt; resource, the corresponding policy should be &lt;code&gt;PostPolicy&lt;/code&gt;, &lt;code&gt;CommentPolicy&lt;/code&gt; would accompany a &lt;code&gt;Comment&lt;/code&gt; resource, and so forth.&lt;/p&gt;
&lt;p&gt;One more thing to consider when working with Action Policy: rule definitions must happen within public methods in policy classes. Using a private method will raise an error.&lt;/p&gt;
&lt;p&gt;Moving on, let&amp;#39;s use this information to create a &lt;code&gt;PostPolicy&lt;/code&gt; where we&amp;#39;ll progressively build various levels of user access.&lt;/p&gt;
&lt;h2&gt;Creating Our First Policy&lt;/h2&gt;
&lt;p&gt;Create a new file named &lt;code&gt;post_policy.rb&lt;/code&gt; under &lt;code&gt;app/policies&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/post_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
    def show?
      true
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we define a simple rule on the &lt;code&gt;Post&lt;/code&gt; resource declaring that anyone can access a &lt;code&gt;Post&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Associating Posts to Users&lt;/h2&gt;
&lt;p&gt;To work with this new post policy, we need to modify the &lt;code&gt;Post&lt;/code&gt; resource we generated earlier so that it&amp;#39;s associated with a logged-in user on creation (by adding a &lt;code&gt;user_id&lt;/code&gt; column to the posts table):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g migration AddColumnUserIDToPost user_id:integer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the migration with &lt;code&gt;rails db:migrate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we need to modify the posts controller to account for this change. First, let&amp;#39;s add &lt;code&gt;user_id&lt;/code&gt; to the allowed &lt;code&gt;post_params&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController

    ...

    private

    def post_params
      params.require(:post).permit(:title, :body, :user_id)
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then modify the &lt;code&gt;create&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController

    ...

    def create
        @post = Post.new(post_params)
        @post.user_id = current_user.id # Devise gives us the logged in user as a helper method `current_user`

        respond_to do |format|
            if @post.save
                ...
            end
        end
    end

    ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&amp;#39;s secure the &lt;code&gt;PostsController&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Implementing Authorization in Controllers&lt;/h2&gt;
&lt;p&gt;We need to modify the &lt;code&gt;PostsController&lt;/code&gt; with a callback to only allow access to logged-in users:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController

  before_action :authenticate_user!, except: :show

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&amp;#39;s use the new Post policy to add some basic user access controls:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/post_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
    ...

    def update?
    # post can only be updated by an author role or the post&amp;#39;s author
      user.author? || (user.id == record.user_id)
    end

    def destroy?
    # a post can only be deleted by its author
      user.id == record.user_id
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we define an access rule on the posts controller&amp;#39;s &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;destroy&lt;/code&gt; actions. We specify that a post&amp;#39;s author (or a user with the &amp;quot;author&amp;quot; role) can update a post, but only a post&amp;#39;s author can delete a post.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Quick tip:&lt;/strong&gt; Action Policy can reference the &lt;code&gt;current_user&lt;/code&gt; provided by Devise and assign it to a &lt;code&gt;user&lt;/code&gt; within the policy.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Next, we need to use the access rule in the posts controller like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController
  before_action :set_post, only: %i[ show edit update destroy ]
  ...

  def update
    authorize! @post # Add this line
    respond_to do |format|
      if @post.update(post_params)
        ...
      end
    end
  end

  def destroy
    authorize! @post # Add this line
    @post.destroy
    ...
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the access rule applied to the posts controller, our next course of action is to ensure the views are protected.&lt;/p&gt;
&lt;h2&gt;Protecting Views with Authorization&lt;/h2&gt;
&lt;p&gt;In the screenshot below, a user with the email &lt;code&gt;&amp;lt;user2@example.com&amp;gt;&lt;/code&gt; and &amp;quot;reader&amp;quot; role is logged in. As you can see, this user can view the post created by &lt;code&gt;&amp;lt;user@example.com&amp;gt;&lt;/code&gt;, and they even have access to the edit and delete links for this post. This is not ideal — the edit and delete links should only be available to the post&amp;#39;s author.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-10/user-2-access.png&quot; alt=&quot;User 2 access&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Our first task will be to remove access to these links for users that aren&amp;#39;t authors of a post. Since we&amp;#39;ve already defined this access rule, we only need to apply it to the &lt;code&gt;show&lt;/code&gt; view using Action Policy&amp;#39;s nifty &lt;code&gt;allowed_to?&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/posts/show.html.erb --&amp;gt;

&amp;lt;%= render @post %&amp;gt;


&amp;lt;div&amp;gt;
  &amp;lt;% if allowed_to?(:update?, @post) %&amp;gt;
    &amp;lt;%= link_to &amp;quot;Edit this post&amp;quot;, edit_post_path(@post) %&amp;gt;
  &amp;lt;% end %&amp;gt;

  &amp;lt;% if allowed_to?(:destroy?, @post) %&amp;gt;
    &amp;lt;%= button_to &amp;quot;Destroy this post&amp;quot;, @post, method: :delete %&amp;gt;
  &amp;lt;% end %&amp;gt;

  &amp;lt;%= link_to &amp;quot;Back to posts&amp;quot;, posts_path %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this rule applied, we can now refresh the post&amp;#39;s &lt;code&gt;show&lt;/code&gt; page and see what we get:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-10/user-2-access-checked.png&quot; alt=&quot;User 2 access checked&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The same user who had access is now limited in what they can do while on a post&amp;#39;s view.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In the first part of this two-part series, we&amp;#39;ve learned some of the basics around the authorization gem Action Policy.&lt;/p&gt;
&lt;p&gt;In the second and final part, we&amp;#39;ll explore some more advanced use cases.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to LiteStack for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/09/27/an-introduction-to-litestack-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2023/09/27/an-introduction-to-litestack-for-ruby-on-rails.html</id>
    <published>2023-09-27T00:00:00+00:00</published>
    <updated>2023-09-27T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the first part of this series, we&#039;ll set up an example Rails application and introduce the basics of LiteStack.</summary>
    <content type="html">&lt;p&gt;In this series of posts, we will look at &lt;strong&gt;LiteStack&lt;/strong&gt;, a one-stop-shop solution that hosts and processes all your production data on a single machine. LiteStack (as the name suggests) makes use of SQLite to provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a database using the &lt;em&gt;LiteDB&lt;/em&gt; adapter&lt;/li&gt;
&lt;li&gt;an ActiveJob backend (&lt;em&gt;LiteJob&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;an ActionCable backend (&lt;em&gt;LiteCable&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;an ActiveSupport::Cache store (&lt;em&gt;LiteCache&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this first post, we&amp;#39;ll introduce the basics of LiteStack and set up an example Rails application.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s begin!&lt;/p&gt;
&lt;h2&gt;An Introduction to SQLite&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sqlite.org/index.html&quot;&gt;SQLite&lt;/a&gt; itself has been the go-to embedded database of many industries for decades. For example, it&amp;#39;s widely used in &lt;a href=&quot;https://www.sqlite.org/whentouse.html&quot;&gt;native app development, testing environments, caching, and others&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recently, though, it has attracted a lot of experimentation and extensions. One of the most popular extensions is &lt;a href=&quot;https://litestream.io/&quot;&gt;Litestream&lt;/a&gt;, which can recover stream changes to an S3-compatible bucket. This means you get a replica of your production database at a very cheap price point and can recover from failure anytime.&lt;/p&gt;
&lt;p&gt;Incidentally, this has made using SQLite as a production database for your Rails app a feasible option. Combined with a full-fledged development stack like LiteStack, it promises to make apps hosted on a single machine a reality. We are here to test this hypothesis and point out any obstacles in the way.&lt;/p&gt;
&lt;p&gt;To do this, we need an example app that&amp;#39;s complex enough to surface potential difficulties, but simple enough to fit this series.&lt;/p&gt;
&lt;h2&gt;Our Example Rails Application&lt;/h2&gt;
&lt;p&gt;We will write an app that transforms children&amp;#39;s drawings using &lt;a href=&quot;https://replicate.com/cjwbw/stable-diffusion-img2img-v2.1&quot;&gt;StableDiffusion on replicate.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As an example, here&amp;#39;s a cute teddy bear drawn by my daughter, with a couple of StableDiffusion interpretations:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-09/teddybears.png&quot; alt=&quot;Teddybears&quot;/&gt;&lt;/p&gt;
&lt;p&gt;As a rough sketch, our app will cover the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The user uploads an image with a textual prompt (we&amp;#39;ll show some advanced SQLite techniques here).&lt;/li&gt;
&lt;li&gt;The user chooses an image style (e.g., &amp;quot;cartoon&amp;quot;, &amp;quot;oil painting&amp;quot;, &amp;quot;photorealistic&amp;quot;, &amp;quot;3D rendering&amp;quot;).&lt;/li&gt;
&lt;li&gt;The processing happens in the background (this kicks off a &lt;em&gt;LiteJob&lt;/em&gt;-powered job).&lt;/li&gt;
&lt;li&gt;While the processing is underway, we show a placeholder image and update the logs sent over by the server. Once completed, we update it to show the actual image. This allows us to explore &lt;em&gt;LiteCable&lt;/em&gt; as we replace the image via Turbo Streams.&lt;/li&gt;
&lt;li&gt;We store the image prediction.&lt;/li&gt;
&lt;li&gt;We use &lt;em&gt;LiteCache&lt;/em&gt; to wrap computationally costly views.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These steps provide a scaffold for this series. The remainder of this post, though, will be concerned with setting up the app.&lt;/p&gt;
&lt;p&gt;We start by creating a new Rails app called &lt;em&gt;skAItch&lt;/em&gt;, using esbuild as our JavaScript bundler and SASS as the CSS preprocessor:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails new -d sqlite3 --skip-action-mailbox -j esbuild -c sass skaitch
$ cd skaitch
$ bin/rails g action_text:install
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Install LiteStack&lt;/h2&gt;
&lt;p&gt;Next, we install &lt;a href=&quot;https://github.com/oldmoe/litestack&quot;&gt;LiteStack&lt;/a&gt; using the shipped generator:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add litestack
$ bin/rails g litestack:install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that is done, we complete the setup and start the development server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/setup
$ bin/dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Authentication and Tenants&lt;/h2&gt;
&lt;p&gt;Subsequently, we need a way to authenticate our users to associate prompts with them. Rather than using an incumbent like Devise, I chose to use a different approach. The &lt;a href=&quot;https://github.com/lazaronixon/authentication-zero&quot;&gt;authentication-zero gem&lt;/a&gt; can flexibly generate an authentication system, as opposed to including it as an engine. Conveniently, it comes with options such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;authentication by token (for APIs)&lt;/li&gt;
&lt;li&gt;two-factor auth&lt;/li&gt;
&lt;li&gt;multitenancy&lt;/li&gt;
&lt;li&gt;rate limiting&lt;/li&gt;
&lt;li&gt;an OmniAuth interface&lt;/li&gt;
&lt;li&gt;passwordless auth&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;#39;ve chosen to add the &lt;code&gt;--tenantable&lt;/code&gt; option because it&amp;#39;s always a good idea to automatically scope your database records to accounts. Authentication-zero provides this with the &lt;code&gt;AccountScoped&lt;/code&gt; model concern.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add authentication-zero --group development
$ bin/rails g authentication --tenantable
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s add a first user via database seeds:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# db/seeds.rb
User.create(
  email: &amp;quot;julian@example.com&amp;quot;,
  password: &amp;quot;mypassword123&amp;quot;,
  password_confirmation: &amp;quot;mypassword123&amp;quot;,
  verified: true
)
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Prompt Scaffold&lt;/h2&gt;
&lt;p&gt;Now it&amp;#39;s time to start writing our actual application logic. We start by defining the central model of our app: the &lt;code&gt;Prompt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We want our prompt to have a title, a description, and a reference to the account that created it. Furthermore, to test SQLite&amp;#39;s &amp;quot;file system&amp;quot; capabilities, we would like it to store the prompt image in binary form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g scaffold Prompt title:string description:rich_text prompt_image:binary account:references
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;prompts&lt;/code&gt; resources entry is also added to &lt;code&gt;config/routes.rb&lt;/code&gt;. Note that authentication-zero adds a &lt;code&gt;before_action&lt;/code&gt; authenticating the user to &lt;code&gt;ApplicationController&lt;/code&gt; by default.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # config/routes.rb
    Rails.application.routes.draw do
+     resources :prompts

      get  &amp;quot;sign_in&amp;quot;, to: &amp;quot;sessions#new&amp;quot;
      post &amp;quot;sign_in&amp;quot;, to: &amp;quot;sessions#create&amp;quot;
      get  &amp;quot;sign_up&amp;quot;, to: &amp;quot;registrations#new&amp;quot;
      post &amp;quot;sign_up&amp;quot;, to: &amp;quot;registrations#create&amp;quot;
      resources :sessions, only: [:index, :show, :destroy]
      resource  :password, only: [:edit, :update]
      namespace :identity do
        resource :email,              only: [:edit, :update]
        resource :email_verification, only: [:show, :create]
        resource :password_reset,     only: [:new, :edit, :create, :update]
      end
      root &amp;quot;home#index&amp;quot;
    end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Furthermore, we include the &lt;code&gt;AccountScoped&lt;/code&gt; concern in our Prompt model, which allows us to scope the stored records by logged-in account. We also validate that a prompt title and image are present.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/prompt.rb
  class Prompt &amp;lt; ApplicationRecord
+   include AccountScoped

    belongs_to :account
    has_rich_text :description

+   validates :title, :prompt_image, presence: true
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the final step to enable multitenancy, we have to connect the prompt to an account when it is created. We do this in the &lt;code&gt;PromptsController&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/controllers/prompts_controller.rb
  class PromptsController &amp;lt; ApplicationController

    # other actions omitted

    def create
      @prompt = Prompt.new(prompt_params)
+     @prompt.account = Current.account

      respond_to do |format|
        if @prompt.save
          format.html { redirect_to prompt_url(@prompt), notice: &amp;quot;Prompt was successfully created.&amp;quot; }
          format.json { render :show, status: :created, location: @prompt }
        else
          format.html { render :new, status: :unprocessable_entity }
          format.json { render json: @prompt.errors, status: :unprocessable_entity }
        end
      end
    end

    # ...
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Connecting our Rails Application to Replicate.com&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://replicate.com/&quot;&gt;Replicate.com&lt;/a&gt; is a leading platform for running AI predictions on high-performance graphics cards. It features an &lt;a href=&quot;https://replicate.com/docs/reference/http&quot;&gt;API&lt;/a&gt; to create predictions, train and store models, etc. To use it, you have to obtain an API token from &lt;a href=&quot;https://replicate.com/account/api-tokens&quot;&gt;https://replicate.com/account/api-tokens&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Running predictions on Replicate is subject to a charge.&lt;/p&gt;
&lt;p&gt;Fortunately for us, there are official and unofficial clients that interface with the API. One such wrapper is the &lt;a href=&quot;https://github.com/dreamingtulpa/replicate-rails&quot;&gt;replicate-rails&lt;/a&gt; gem, which we will install now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # Gemfile

  # ... other gems

+ gem &amp;#39;replicate-rails&amp;#39;, require: &amp;#39;replicate_rails&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To store our API key securely, we are going to use Rails credentials:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ EDITOR=vim bin/rails credentials:edit
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # config/credentials.yml.enc
  secret_key_base: YOUR_SECRET_KEY_BASE

+ replicate:
+   api_token: YOUR_REPLICATE_API_TOKEN
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Editing and saving this file will encrypt your credentials, and it can only be opened by providing the correct &lt;code&gt;RAILS_MASTER_KEY&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, we have to put it to use. As suggested in replicate-rails&amp;#39; README, we authenticate against Replicate in an initializer. We also define a webhook handler which (in our case) is just a class put into the same file. Note that I added a &lt;code&gt;binding.irb&lt;/code&gt; breakpoint here for a first test of our functionality.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/replicate.rb
Replicate.client.api_token = Rails.application.credentials[:replicate][:api_token]

class ReplicateWebhook
  def call(prediction)
    binding.irb
  end
end

ReplicateRails.configure do |config|
  config.webhook_adapter = ReplicateWebhook.new
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replicate-rails also ships a default webhook controller, which calls the above handler. We only have to mount it in our &lt;code&gt;config/routes.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # config/routes.rb
  Rails.application.routes.draw do
+   mount ReplicateRails::Engine =&amp;gt; &amp;quot;/replicate/webhook&amp;quot;

    resources :prompts

    # ... more routes omitted
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test the webhook locally, you have to set up a tunnel, for example, with &lt;a href=&quot;https://ngrok.com/&quot;&gt;Ngrok&lt;/a&gt;. The steps to set it up are beyond the scope of this article, but it&amp;#39;s pretty simple. Please refer to the &lt;a href=&quot;https://ngrok.com/docs&quot;&gt;Ngrok docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You do, however, have to tell Rails that it may listen to your tunnel&amp;#39;s URL. To enable this, add it to the &lt;code&gt;allowed_hosts&lt;/code&gt; in &lt;code&gt;config/application.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # ...requires omitted

  # config/application.rb
  module Skaitch
    class Application &amp;lt; Rails::Application
      # Initialize configuration defaults for originally generated Rails version.
      config.load_defaults 7.0

      # Configuration for the application, engines, and railties goes here.
      #
      # These settings can be overridden in specific environments using the files
      # in config/environments, which are processed later.
      #
      # config.time_zone = &amp;quot;Central Time (US &amp;amp; Canada)&amp;quot;
      # config.eager_load_paths &amp;lt;&amp;lt; Rails.root.join(&amp;quot;extras&amp;quot;)
+     config.hosts &amp;lt;&amp;lt; &amp;quot;YOUR_NGROK_URL&amp;quot;
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Running a Prediction&lt;/h2&gt;
&lt;p&gt;Now we&amp;#39;ll test creating a prediction. The only missing bit is to hook it up in our &lt;code&gt;PromptsController&lt;/code&gt;. The general workflow is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We retrieve a model (in our case, the &lt;code&gt;stable-diffusion-img2img&lt;/code&gt; model) from Replicate.&lt;/li&gt;
&lt;li&gt;We grab a specific version (in our case, the latest one) to run the prediction against.&lt;/li&gt;
&lt;li&gt;We run the prediction, specifying:&lt;ul&gt;
&lt;li&gt;A text prompt describing the image (in our case, our prompt&amp;#39;s title).&lt;/li&gt;
&lt;li&gt;An image prompt that we have to provide as a Base64-encoded Data URL.&lt;/li&gt;
&lt;li&gt;A webhook to ping when the prediction completes (we point it at the webhook route provided by replicate-rails).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;# app/controllers/prompts_controller.rb
  class PromptsController &amp;lt; ApplicationController

    # other actions omitted

    def create
      @prompt = Prompt.new(prompt_params)
      @prompt.account = Current.account

+     model = Replicate.client.retrieve_model(&amp;quot;stability-ai/stable-diffusion-img2img&amp;quot;)
+     version = model.latest_version
+     version.predict({prompt: prompt_params[:title], image: prompt_image_data_url}, replicate_rails_url)

      # respond_to omitted
    end

    # ...

    private

    # other private methods omitted

+   def prompt_image_data_url
+     encoded_data = Base64.strict_encode64(prompt_image.read)
+
+     &amp;quot;data:image/#{prompt_image.content_type};base64,#{encoded_data}&amp;quot;
+   end
+
+   def prompt_image
+     @prompt_image ||= prompt_params[:prompt_image]
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you go to &lt;a href=&quot;https://YOUR_NGROK_URL/prompts/new&quot;&gt;https://YOUR_NGROK_URL/prompts/new&lt;/a&gt; and upload an image together with a title, you will now trigger a prediction. When done, it will call back via the provided webhook. Because we set a &lt;code&gt;binding.irb&lt;/code&gt; breakpoint there, the controller action stops in a REPL, and we can take a look around:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;gt; prediction.succeeded?
=&amp;gt; true
&amp;gt; prediction.output
=&amp;gt; [&amp;quot;https://replicate.delivery/SOME_SIGNED_PATH/out-0.png&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, our first run of an image-to-image generation succeeded. The prediction model returns a URL to the created image, which we will want to store locally (they are deleted at Replicate periodically). We will take a closer look at this next time.&lt;/p&gt;
&lt;h2&gt;Up Next: LiteDB Deep Dive&lt;/h2&gt;
&lt;p&gt;In this opening post, we introduced the LiteStack environment as an interesting alternative to host all of your Rails app&amp;#39;s components on a single machine. We have furthermore set up an example app that talks to Replicate.com for AI image generation.&lt;/p&gt;
&lt;p&gt;In the next part of this series, we will explore the first and central element of LiteStack — &lt;em&gt;LiteDB&lt;/em&gt; — in more depth. We will look at some traits that make it uniquely powerful, common pitfalls, limitations, and tradeoffs.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Sidekiq for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/09/20/an-introduction-to-sidekiq-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2023/09/20/an-introduction-to-sidekiq-for-ruby-on-rails.html</id>
    <published>2023-09-20T00:00:00+00:00</published>
    <updated>2023-09-20T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore how we can use Sidekiq to manage and process background jobs in a Rails application.</summary>
    <content type="html">&lt;p&gt;Sidekiq allows Ruby developers to maintain fast and responsive web
applications by moving time-consuming tasks into the
background.&lt;/p&gt;
&lt;p&gt;With multithreading at its core, Sidekiq can process many jobs at once.
This makes Sidekiq an important part of Ruby or Rails applications that
handle heavy loads or perform tasks like sending emails or processing
files. Without background processing, long-running tasks would block your application&amp;#39;s main thread, resulting in slow response times and
a poor user experience.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll focus on
effectively utilizing Sidekiq to manage and process background jobs.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;The Basics of Sidekiq&lt;/h2&gt;
&lt;p&gt;In essence, Sidekiq is a full-featured background processing framework for Ruby.
It lets you run background tasks concurrently,
crucial for responsive and reliable web applications. Whether
sending emails, resizing images, or processing CSV files, time-consuming
tasks can be done behind the scenes using Sidekiq.&lt;/p&gt;
&lt;p&gt;Sidekiq&amp;#39;s architecture relies heavily on multithreading and the concept of
&amp;quot;workers&amp;quot;. However, as of Sidekiq 6.3.0, &lt;a href=&quot;https://www.mikeperham.com/2021/11/07/whats-new-in-sidekiq-6.3/&quot;&gt;the &lt;code&gt;Sidekiq::Worker&lt;/code&gt; module has been deprecated in favor of &lt;code&gt;Sidekiq::Job&lt;/code&gt;&lt;/a&gt; as the terminology &amp;quot;worker&amp;quot; can be confusing:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Are you talking about a process? A thread? A type of job? I encourage developers to stop using the term worker.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Redis, a lightning-fast in-memory database, plays an essential role here,
acting as the queue system that stores these background jobs.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; While Sidekiq does rely on Redis for storage, we won&amp;#39;t be getting into the details of Redis configuration and usage in this article.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Installation and Setup of Sidekiq for Rails&lt;/h2&gt;
&lt;p&gt;To begin, you&amp;#39;ll need to run Ruby version 2.5 or later and
Redis server version 4 or later.&lt;/p&gt;
&lt;p&gt;Now you&amp;#39;re ready to
install the Sidekiq Ruby gem.&lt;/p&gt;
&lt;p&gt;Start by adding Sidekiq to your application&amp;#39;s Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;sidekiq&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, install it in the directory of your project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that Sidekiq is installed, you&amp;#39;ll need to configure it. In a Rails
application, you configure Sidekiq as your &lt;code&gt;ActiveJob&lt;/code&gt; adapter.
Open &lt;code&gt;config/application.rb&lt;/code&gt; and add the following line inside the application
class definition:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;config.active_job.queue_adapter = :sidekiq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can further configure Sidekiq by
creating a Sidekiq initializer to point to a Redis instance, for example. This is a special script in Rails that
runs when your application starts up.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;config/initializers&lt;/code&gt;, create
a file named &lt;code&gt;sidekiq.rb&lt;/code&gt;. Inside this file, you can specify various Sidekiq
configurations. For instance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Sidekiq.configure_server do |config|
  config.redis = { url: &amp;#39;redis://localhost:6379/1&amp;#39; }
end

Sidekiq.configure_client do |config|
  config.redis = { url: &amp;#39;redis://localhost:6379/1&amp;#39; }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration tells Sidekiq to connect to a local Redis server. Of
course, in a production environment, you&amp;#39;d replace the local URL
with that of your actual Redis server (this is a great place for an
environment variable).&lt;/p&gt;
&lt;p&gt;And that&amp;#39;s it! With Sidekiq installed and configured, you&amp;#39;re now ready to start
creating and processing jobs in the background of your Rails application.&lt;/p&gt;
&lt;h2&gt;Setting Up a Sidekiq Job&lt;/h2&gt;
&lt;p&gt;Here&amp;#39;s how you set up and initialize a basic Sidekiq job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;sidekiq&amp;#39;

class SomeNameForAJob
  include Sidekiq::Job

  def perform(name, count)
    # Actual implementation of the task to run in the background goes here
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;To actually queue a job to run, you call the &lt;code&gt;perform_async&lt;/code&gt; method on
the job class, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;SomeNameForAJob.perform_async(&amp;#39;some_name&amp;#39;, 42)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This line adds a job to the default queue that executes as soon as a job
becomes available. The arguments &amp;#39;some_name&amp;#39; and 42 are passed to the perform method &lt;code&gt;SomeNameForAJob&lt;/code&gt; upon execution.&lt;/p&gt;
&lt;p&gt;From here, the Sidekiq client pushes the job into a queue in Redis, and the
Sidekiq server pulls that job out of the queue when it&amp;#39;s ready to process it.&lt;/p&gt;
&lt;h2&gt;Creating Your First Sidekiq Job&lt;/h2&gt;
&lt;p&gt;In Sidekiq, jobs are represented by job classes, and the
tasks they need to perform are defined in a &lt;code&gt;perform&lt;/code&gt; method. Let&amp;#39;s create a basic Sidekiq job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class HelloNameJob
  include Sidekiq::Job

  def perform(name, times)
    times.times do
      puts &amp;quot;Hello, #{name}!&amp;quot;
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this code snippet, we&amp;#39;ve created the class &lt;code&gt;HelloNameJob&lt;/code&gt;. This takes two arguments: &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;times&lt;/code&gt;. When performed, it prints
a greeting to the console a specified number of times. This is, of course,
a simple example. In a real-world application, the &lt;code&gt;perform&lt;/code&gt; method can
contain any code you want to run in the background.&lt;/p&gt;
&lt;p&gt;Now execute your job. Simply call the &lt;code&gt;perform_async&lt;/code&gt; method on the class and
pass in any arguments your &lt;code&gt;perform&lt;/code&gt; method expects. Here&amp;#39;s how we
can schedule the &lt;code&gt;HelloNameJob&lt;/code&gt; we&amp;#39;ve just created:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;HelloNameJob.perform_async(&amp;#39;Jeff&amp;#39;, 5)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will enqueue a job to print &amp;quot;Hello, Jeff!&amp;quot; five times.&lt;/p&gt;
&lt;p&gt;If you need a job to be performed at a particular time, you can use the
&lt;code&gt;perform_in&lt;/code&gt; or &lt;code&gt;perform_at&lt;/code&gt; methods. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;HelloNameJob.perform_in(5.minutes, &amp;#39;Meredith&amp;#39;, 3)

HelloNameJob.perform_at(2.days.from_now, &amp;#39;Jeff&amp;#39;, 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In these examples, &amp;#39;Meredith&amp;#39; will receive her greeting three times in
five minutes, while &amp;#39;Jeff&amp;#39; will have to wait two days to receive his greeting
two times.&lt;/p&gt;
&lt;h2&gt;Advanced Sidekiq Usage&lt;/h2&gt;
&lt;p&gt;Sidekiq is not restricted to offloading work to the background. It
also offers advanced features that allow you to configure your jobs, including job retries and job prioritization.&lt;/p&gt;
&lt;h3&gt;Automatic Job Retries&lt;/h3&gt;
&lt;p&gt;One of Sidekiq&amp;#39;s most powerful features is automatic job retries. By
default, if a job fails due to an unhandled exception, Sidekiq will
retry the job with an exponential backoff. You can customize the number
of retries by specifying &lt;code&gt;sidekiq_options&lt;/code&gt; in your class
like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ThisJob
  include Sidekiq::Job

  sidekiq_options retry: 10

  def perform(args)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, if a job fails, Sidekiq will retry it ten times before giving up.&lt;/p&gt;
&lt;h3&gt;Job Prioritization&lt;/h3&gt;
&lt;p&gt;Job prioritization is another advanced feature that Sidekiq provides.
You can control the priority of your jobs by assigning them to different
queues and setting the priority of each queue. Here&amp;#39;s how you can specify
a queue when defining a job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ThisJob
  include Sidekiq::Jobs

  sidekiq_options queue: &amp;#39;critical&amp;#39;

  def perform(args)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, jobs for &lt;code&gt;ThisJob&lt;/code&gt; will be placed in the
critical queue. When starting your Sidekiq server, you can then specify
the order in which queues should be processed.&lt;/p&gt;
&lt;p&gt;These are just a couple of examples of how Sidekiq&amp;#39;s advanced features can
be leveraged to fine-tune your background job processing. With Sidekiq,
you have the tools to ensure that your jobs are processed efficiently,
reliably, and in a way that best suits your application&amp;#39;s needs.&lt;/p&gt;
&lt;h2&gt;Monitoring and Scaling with Sidekiq&lt;/h2&gt;
&lt;p&gt;Sidekiq comes with standard features for monitoring and
scaling your job processing capabilities. A web-based dashboard gives you
a real-time view of your job queues. You can see the number of
processed and failed jobs, as well as detailed information about current
and scheduled jobs.&lt;/p&gt;
&lt;p&gt;To use the Sidekiq dashboard, mount
it in your Rails routes file. In &lt;code&gt;config/routes.rb&lt;/code&gt;, add:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;sidekiq/web&amp;#39;
mount Sidekiq::Web =&amp;gt; &amp;#39;/sidekiq&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After adding these lines, you can access the dashboard by navigating to
&amp;#39;/sidekiq&amp;#39; on your server.&lt;/p&gt;
&lt;h2&gt;Monitoring Sidekiq with AppSignal&lt;/h2&gt;
&lt;p&gt;For more comprehensive monitoring, Sidekiq integrates well with application
performance monitoring tools like AppSignal. AppSignal&amp;#39;s &lt;a href=&quot;https://www.appsignal.com/tour/automated-dashboards/sidekiq&quot;&gt;Sidekiq dashboard&lt;/a&gt; provides detailed insights into your Sidekiq jobs,
including failed and retried jobs, job
duration, and Redis memory usage.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-09/sidekiq-dashboard.png&quot; alt=&quot;Sample AppSignal Sidekiq Dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This kind of information
is invaluable for identifying bottlenecks as you scale your application.
Beyond that, AppSignal&amp;#39;s alerts help you stay on top of anomalies like
queue length, so you know when your queues have grown.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-09/queue-length.png&quot; alt=&quot;AppSignal Sidekiq Queue Length Graph&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/sidekiq.html&quot;&gt;Integrating AppSignal&lt;/a&gt; into your Sidekiq
setup with Rails is simple. AppSignal&amp;#39;s Ruby gem inserts into the
Sidekiq server middleware without any extra configuration. This also works with ActiveJob!&lt;/p&gt;
&lt;p&gt;You can
even use AppSignal for a Sidekiq application that doesn&amp;#39;t run on Rails, using the
&lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/sidekiq.html&quot;&gt;setup mentioned in the docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A Sidekiq Use Case&lt;/h2&gt;
&lt;p&gt;Two of the most common use cases for Sidekiq are asynchronous emailing
and scheduled report generation. Let&amp;#39;s take a closer look at one of these
examples.&lt;/p&gt;
&lt;p&gt;Emails, particularly those with large attachments or numerous recipients,
can take a significant amount of time to send. By handling email sending
in a background job, your application can continue to respond to user
requests while an email is being processed.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s a simple example of
a Sidekiq job for sending emails in a Rails application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ResetPasswordJob
  include Sidekiq::Job

  def perform(user_id)
    UserMailer.reset_password_email_email(user_id)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;ResetPasswordJob&lt;/code&gt; uses the &lt;code&gt;UserMailer&lt;/code&gt; class
to send a password reset email to a user. To enqueue an email to send, you call &lt;code&gt;ResetPasswordJob.perform_async(user_id)&lt;/code&gt; from
somewhere else in your application.&lt;/p&gt;
&lt;p&gt;However, in a typical Rails application, you often don&amp;#39;t need to create a separate
Sidekiq job for sending emails. Rails provides the Action Mailer framework, which
is used to send emails and integrates nicely with Active Job for background
processing.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s an example of how you might use Active Job and Action Mailer to send a
password reset email:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UserMailer &amp;lt; ApplicationMailer
  def reset_password_email(user_id)
    mail(to: User.find(user_id).email, subject: &amp;#39;Password Reset&amp;#39;)
  end
end

# elsewhere in your application
UserMailer.reset_password_email(user_id).deliver_later
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, calling &lt;code&gt;deliver_later&lt;/code&gt; on the mailer automatically enqueues the
email to be sent as a background job. Behind the scenes, Active Job uses Sidekiq
to manage these jobs.&lt;/p&gt;
&lt;p&gt;But if you want more control over your jobs — e.g., you want to customize retry behavior or
queue prioritization — or if you&amp;#39;re dealing with tasks that don&amp;#39;t integrate with Active
Job as easily as Action Mailer, then it&amp;#39;s appropriate to create separate Sidekiq
jobs.&lt;/p&gt;
&lt;p&gt;This flexibility, along with Sidekiq&amp;#39;s performance and ease of use, makes it a valuable
tool for handling various background tasks in Rails applications.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we walked through an introduction to Sidekiq, exploring its
basics, diving into installation and setup, and creating
and executing useful jobs.&lt;/p&gt;
&lt;p&gt;We also touched on Sidekiq&amp;#39;s more advanced features,
looked at real-world use cases, and discussed its crucial role in
improving the performance and scalability of Ruby applications.&lt;/p&gt;
&lt;p&gt;Armed with the knowledge from this introduction, you&amp;#39;re now equipped to
start using Sidekiq more effectively in your own Rails apps.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to RuboCop for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/09/06/an-introduction-to-rubocop-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2023/09/06/an-introduction-to-rubocop-for-ruby-on-rails.html</id>
    <published>2023-09-06T00:00:00+00:00</published>
    <updated>2023-09-06T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s dive into what RuboCop is, how it helps developers, how to use it, and some key practical use cases.</summary>
    <content type="html">&lt;p&gt;Good code has a lot to do with how readable it is. As developers, we more often read code than write it. As my Perl teacher told us many times: the flexibility of Perl&amp;#39;s syntax was its best and worst trait at the same time. Ruby&amp;#39;s syntax was influenced partly by Perl and is also quite flexible.&lt;/p&gt;
&lt;p&gt;Whatever language you pick, set some guidelines to avoid overusing a language&amp;#39;s flexibility. Style guides for Ruby abound on the web, and it&amp;#39;s not difficult to pick a style nowadays. But there is not much point in having copious debates on style and whether a proposed change follows a guide. Style enforcement is best left to a tool.&lt;/p&gt;
&lt;p&gt;Such tools are called linters and static code analyzers. The de facto standard for Ruby in that category is RuboCop. Here follows an introduction to RuboCop: what it is, how it helps developers, how to use it, and some key practical use cases.&lt;/p&gt;
&lt;h2&gt;What Is RuboCop for Ruby?&lt;/h2&gt;
&lt;p&gt;First, RuboCop is a great tool to lint a code base and ensure a specific coding style is followed (such as indentation, spacing, naming conventions, etc.). It&amp;#39;s heavily configurable on that level through many rules, called &amp;quot;cops&amp;quot;. Each can be activated (or not) and tailored (for example, the number of characters per line).&lt;/p&gt;
&lt;p&gt;Ensuring a specific coding style is followed helps reduce developers&amp;#39; cognitive load in writing and reading code. It&amp;#39;s not a matter of code looking &amp;quot;nice&amp;quot;; it&amp;#39;s rather a matter of code looking similar to the rest of the code base.&lt;/p&gt;
&lt;p&gt;The second side of RuboCop is its ability to analyze code statically for quick insights into code complexity and potential code issues, such as the misuse of variables.&lt;/p&gt;
&lt;p&gt;As RuboCop is a little utility, it&amp;#39;s easily run within a CI pipeline and in any developer&amp;#39;s IDE. Thus, RuboCop&amp;#39;s feedback can be integrated into the key places where it matters: when code is being written and before it&amp;#39;s merged into the code base.&lt;/p&gt;
&lt;h2&gt;How to Use RuboCop for Ruby&lt;/h2&gt;
&lt;p&gt;Style is a matter of taste, so it is entirely subjective. Remember the main point behind using a linter: it&amp;#39;s just there to help follow a chosen style because it gives us more capacity to do what matters in any project.&lt;/p&gt;
&lt;p&gt;By default, RuboCop will enforce the style defined in the &lt;a href=&quot;https://rubystyle.guide&quot;&gt;Ruby Community Style Guide&lt;/a&gt;. We can tailor it to our specific tastes and context, but let&amp;#39;s rely on this basic set of rules to learn how to use RuboCop.&lt;/p&gt;
&lt;h3&gt;Simple Use of RuboCop&lt;/h3&gt;
&lt;p&gt;Adding RuboCop to a project is as simple as adding the &lt;code&gt;rubocop&lt;/code&gt; gem to the development and test group in the Gemfile.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;group :development, :test do
  gem &amp;#39;rubocop&amp;#39;, require: false
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are also several complimentary gems you can use, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/rubocop-rails&quot;&gt;rubocop-rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/rubocop-rspec&quot;&gt;rubocop-rspec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/rubocop-performance&quot;&gt;rubocop-performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those last two will be useful for many of you.&lt;/p&gt;
&lt;p&gt;Once the gem, or gems, is installed, we can run &lt;code&gt;rubocop&lt;/code&gt; from within the root folder of the Ruby application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$&amp;gt; rubocop *
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will print out a report of all &amp;quot;offenses&amp;quot; and how many can be automatically fixed.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$&amp;gt; rubocop *
Inspecting 807 files
...CCC....................
Offenses:

app/controllers/api/messages_controller.rb:46:25: C: Naming/MethodParameterName: Method parameter must be at least 3 characters long.

80 files inspected, 20 offenses detected, 7 offenses autocorrectable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s dissect the only offense we have kept in this example.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.rubocop.org/rubocop/cops_naming.html#namingmethodparametername&quot;&gt;RuboCop&amp;#39;s documentation&lt;/a&gt; explains that this issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Checks method parameter names for how descriptive they are. It is highly configurable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is followed by an explanation of the configuration that can be made for this specific rule (we will cover that soon).&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;What will be of interest to most are the &lt;a href=&quot;https://docs.rubocop.org/rubocop/cops_naming.html#namingmethodparametername&quot;&gt;examples of good and bad styles&lt;/a&gt;, as checked by this rule:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# bad
def bar(varOne, varTwo)
  varOne + varTwo
end

# With `AllowNamesEndingInNumbers` set to false
def foo(num1, num2)
  num1 * num2
end

# With `MinNameLength` set to number greater than 1
def baz(a, b, c)
  do_stuff(a, b, c)
end

# good
def bar(thud, fred)
  thud + fred
end

def foo(speed, distance)
  speed * distance
end

def baz(age_a, height_b, gender_c)
  do_stuff(age_a, height_b, gender_c)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can apply this advice and rewrite the code to match the required style. RuboCop can often fix the issue it has found (we will look at this a bit later).&lt;/p&gt;
&lt;h3&gt;Configuring Rules in RuboCop&lt;/h3&gt;
&lt;p&gt;By default, out of the box, RuboCop comes with a default set of pre-configured rules. The documentation will tell you &lt;a href=&quot;https://github.com/rubocop/rubocop/blob/master/config/default.yml&quot;&gt;Rubocop&amp;#39;s default rules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Configuring RuboCop for a project is done through the &lt;code&gt;.rubocop.yml&lt;/code&gt; file at the project&amp;#39;s root. This can be tailored further in sub-folders, but I wouldn&amp;#39;t recommend it.
And, of course, it&amp;#39;s also possible to have system-wide configuration files within the user&amp;#39;s home folder: &lt;code&gt;~/.rubocop.yml&lt;/code&gt; or &lt;code&gt;~/.config/rubocop/config.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is a simple example of such a configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;Layout/LineLength:
  Max: 99
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s reuse the previous rule: &lt;code&gt;Naming/MethodParameterName&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;Naming/MethodParameterName:
  MinNameLength: 3
  AllowedNames:
    - as
    - at
    - in
    - ip
    - id
    - to
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a lot of things that can be tailored in the RuboCop configuration file. The documentation can help you pick the rules you want to use, but a few other options are available to give you a starting point beyond the default rules.&lt;/p&gt;
&lt;p&gt;The Ruby style guide matches RuboCop&amp;#39;s default configuration, for example. But you can also find other guides like the &lt;a href=&quot;https://relaxed.ruby.style&quot;&gt;Relaxed Ruby Style&lt;/a&gt;, and some companies - such as &lt;a href=&quot;https://ruby-style-guide.shopify.dev&quot;&gt;Shopify&lt;/a&gt; - share theirs.&lt;/p&gt;
&lt;h3&gt;Arguments in RuboCop&lt;/h3&gt;
&lt;p&gt;RuboCop can be run without arguments for a basic check, but we can add on a few arguments for different effects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-a&lt;/code&gt; : to auto-correct offenses, but only when it&amp;#39;s safe to do so.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-A&lt;/code&gt;: to auto-correct offenses, even when it&amp;#39;s not safe to do so.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-E&lt;/code&gt;: to display extra details for each offense listed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-S&lt;/code&gt;: to display style guide URLs in the offense messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those are the main options we might be interested in. &lt;code&gt;-E&lt;/code&gt; and &lt;code&gt;-S&lt;/code&gt; are great to add whenever you run RuboCop to check the code base. They both give more details for developers to figure out what to do.
&lt;code&gt;-a&lt;/code&gt; and &lt;code&gt;-A&lt;/code&gt; are handy to quickly take care of the &amp;quot;small&amp;quot; issues, but beware of the automatic side. It might do well enough for minor style offenses, such as spaces, but other things might not be that simple.&lt;/p&gt;
&lt;h3&gt;Bending the Law&lt;/h3&gt;
&lt;p&gt;In some rare cases, you might want a RuboCop rule or rules to be disabled around one or several lines in a file. This is generally frowned upon, as it creates a precedent many developers won&amp;#39;t hesitate to replicate. Yet it might still be useful to know how to do this hack for certain cases.&lt;/p&gt;
&lt;p&gt;The way to disable a rule and then re-enable it is to use a pair of comments:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# rubocop:disable Layout/ClassStructure, Style/AndOr
[...]
# rubocop:enable Layout/ClassStructure, Style/AndOr
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will disable and enable two rules: &lt;code&gt;Layout/ClassStructure&lt;/code&gt;, and &lt;code&gt;Style/AndOr&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the previous example, you can also disable whole sections (or departments) of rules by using the department name: &lt;code&gt;Layout&lt;/code&gt; or &lt;code&gt;Style&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, you can also disable all the rules in one go.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# rubocop:disable all
[...]
# rubocop:enable all
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;RuboCop in Practice&lt;/h2&gt;
&lt;p&gt;Now that we have seen what it is and how to use it, let&amp;#39;s review how we might use RuboCop in our day to day.&lt;/p&gt;
&lt;h3&gt;Local Uses&lt;/h3&gt;
&lt;p&gt;As discussed before, a linter&amp;#39;s purpose is to ensure a standard style is used throughout a code base. Standards are how we help each other work together without putting up complex conversion and compensation schemes.
When building a house, standards are the way every carpenter knows how to work together. The actual details will be a bit different from one house to another, but, using the standard, the work different carpenters do will match.&lt;/p&gt;
&lt;p&gt;RuboCop helps software engineers work together by keeping their code up to a certain standard style.&lt;/p&gt;
&lt;p&gt;The simplest way to use RuboCop is to run it before making a commit or once in a while before pushing commits to a remote repository. As described in the first part of this post, using &lt;code&gt;rubocop *&lt;/code&gt; will run a check of the code base and display any offenses for you to fix.&lt;/p&gt;
&lt;p&gt;You can then use the &lt;code&gt;-a&lt;/code&gt; or &lt;code&gt;-A&lt;/code&gt; option on another run to get some or all offenses fixed by RuboCop.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$&amp;gt; rubocopo -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also fix them yourself, of course.&lt;/p&gt;
&lt;h3&gt;Within an IDE&lt;/h3&gt;
&lt;p&gt;Some IDEs and text editors have RuboCop plugins, so you can get notices about style offenses while working directly on code.
The only issue with these is the configuration, which can be fiddly.&lt;/p&gt;
&lt;h3&gt;Integration with CI&lt;/h3&gt;
&lt;p&gt;It&amp;#39;s important to run RuboCop within CI pipelines.&lt;/p&gt;
&lt;p&gt;Usually, we run linters such as RuboCop as part of a pipeline&amp;#39;s first steps to notify the author of code changes about any offense quickly. We also consider such infractions important, so they will block the Pull or Merge request in its tracks. A merge should not be possible if the style is not correct.&lt;/p&gt;
&lt;p&gt;If your project relies on GitHub&amp;#39;s Actions, you can find a RuboCop action. You can build similar steps for any CI/CD pipeline out there. The principle is simple: run RuboCop against the code base as an early block within the pipeline; if there is an infraction, it will return a non-zero code, and the CI will treat that as a failure. The output can be used more easily as an artifact.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-f&lt;/code&gt; option will allow you to tailor the output format. This can be practical when running RuboCop in the CI pipeline: in markdown, HTML, or GitHub. I&amp;#39;d advise starting with markdown or HTML, but you should also consider the fairly readable default format.&lt;/p&gt;
&lt;h2&gt;Adopting RuboCop in a New Project&lt;/h2&gt;
&lt;p&gt;Many projects start without a coding style or diverge from one for some reason. Getting such code bases back in the fold is often challenging and hard work.&lt;/p&gt;
&lt;p&gt;The first run of RuboCop in such a context will usually output a fairly long list. Trying to solve all offenses at once is foolish. Instead, RuboCop allows us to generate a to-do list.&lt;/p&gt;
&lt;p&gt;Running RuboCop with the &lt;code&gt;--auto-gen-config&lt;/code&gt; option will generate the &lt;code&gt;todo&lt;/code&gt; file. This file will disable any rule that is not respected. It&amp;#39;s located within the project folder &lt;code&gt;.rubocop_todo.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Disabling all the rules that are not respected rather than fixing them might seem counterproductive. But we don&amp;#39;t stop here. This step ensures we don&amp;#39;t face a mountain of work.
We can remove a rule from the ignore list and fix all offenses against it, working our way, one rule at a time, through the to-do list and the code base.&lt;/p&gt;
&lt;p&gt;This approach — of ignoring infractions to one or more rules within multiple files and then fixing the infractions one by one in many files — is doable, but will require a lot of back and forth between files. You might end up changing several files and several lines multiple times over the course of the process.&lt;/p&gt;
&lt;p&gt;Another method is to use a &amp;quot;per file&amp;quot; approach. Using the following syntax within the to-do list file, you will ignore infractions to all cops in the files listed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;AllCops:
  Exclude:
    - &amp;quot;app/models/user.rb&amp;quot;
    - &amp;quot;app/controllers/users_controller.rb&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can work your way through those files one by one: removing the file from the list, running RuboCop against it, and then fixing all offenses.&lt;/p&gt;
&lt;p&gt;Whichever approach you pick, you must remember this is a step towards improvement through continuous learning and effort. You need a strategy and vision that works well with that: include enough Slack time within the team schedule, or set specific times aside to work on this issue.&lt;/p&gt;
&lt;h2&gt;Making RuboCop Easier to Use with Standard Ruby&lt;/h2&gt;
&lt;p&gt;RuboCop and coding style guides have helped us determine standards for each project and team. We should only debate what code is doing, not how it looks.&lt;/p&gt;
&lt;p&gt;Some Ruby developers have made the point that we can even forego adjusting RuboCop&amp;#39;s configuration. The idea is simple: after so many years working with Ruby, we more or less know how Ruby code should look, so a style that works for most will be quite okay.&lt;/p&gt;
&lt;p&gt;This approach is known as &lt;a href=&quot;https://github.com/standardrb/standard&quot;&gt;Standard Ruby&lt;/a&gt;. It can also be completed with plugins, including one for Ruby on Rails projects.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;RuboCop is, alongside Bundler, a good friend to any Ruby developer. As we have seen, it&amp;#39;s easy to run it locally to get a list of offenses against a project&amp;#39;s style standard.&lt;/p&gt;
&lt;p&gt;We have also seen how RuboCop can fix a lot of those offenses on its own, and how to work our way through a long list of offenses if we start from a large backlog.&lt;/p&gt;
&lt;p&gt;RuboCop is a tool in your toolbox; code style is only meant to help a team work together and shouldn&amp;#39;t be a perpetual topic of debate. Yet, we have also seen that if you find it tedious to figure out a style for your project, you should look into projects such as Standard Ruby.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Secure Your Ruby App with JSON Web Tokens</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/08/23/secure-your-ruby-app-with-json-web-tokens.html"/>
    <id>https://blog.appsignal.com/2023/08/23/secure-your-ruby-app-with-json-web-tokens.html</id>
    <published>2023-08-23T00:00:00+00:00</published>
    <updated>2023-08-23T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out how you can use JWT authentication to secure your Ruby application.</summary>
    <content type="html">&lt;p&gt;If a web application involves users, as a matter of course, their data should be protected and secured.&lt;/p&gt;
&lt;p&gt;Securing a web application can mean several things. In this post, we&amp;#39;ll discuss a subset of web security that involves authentication using JSON Web Tokens (JWTs) and the Ruby on Rails web application framework.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;What is a JSON Web Token?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc7519&quot;&gt;A JSON Web Token&lt;/a&gt; is an internet standard defined by the Internet Engineering Task Force (IETF) as a: &amp;quot;compact, URL-safe means of representing claims to be transferred between two parties&amp;quot;.&lt;/p&gt;
&lt;p&gt;Here, &amp;quot;claims&amp;quot; refers to assorted pieces of information about a subject. A claim is represented as a name/value pair where the name is always a string, and the value can be any JSON value.&lt;/p&gt;
&lt;h2&gt;The Basic Structure of JSON Web Tokens&lt;/h2&gt;
&lt;p&gt;Delving into the intricacies of JWTs is out of the scope of this post. That said, it&amp;#39;s worth knowing the structure of JWTs.&lt;/p&gt;
&lt;p&gt;A JWT consists of three parts, separated by a period: the header, payload, and signature.&lt;/p&gt;
&lt;p&gt;An example JWT could look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the sake of readability, each part of the token starts on a new line. In practice, the parts are joined.&lt;/p&gt;
&lt;p&gt;This token is sent from a server to a client. The client sends the token to the server to identify itself and have a request processed.&lt;/p&gt;
&lt;p&gt;The first part, the header, contains information about the algorithm used to generate the token and the type of token. If decoded, we get something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;alg&amp;quot;: &amp;quot;HS256&amp;quot;,
  &amp;quot;typ&amp;quot;: &amp;quot;JWT&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second part, the payload, contains a set of claims about the user. In most cases, this would be the client in a client-server setup, and it could look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;sub&amp;quot;: &amp;quot;1234567890&amp;quot;,
  &amp;quot;name&amp;quot;: &amp;quot;John Doe&amp;quot;,
  &amp;quot;iat&amp;quot;: 1516239022
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The signature (the last part of a signed JWT) validates the token. It is generated by encoding the header and the payload using &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4648&quot;&gt;Base64url Encoding — RFC 4648&lt;/a&gt; and then concatenating them with a period separator.&lt;/p&gt;
&lt;p&gt;Essentially, what happens with the signature bit is this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;HMAC_SHA256(
  secret,
  base64urlEncoding(header) + &amp;#39;.&amp;#39; +
  base64urlEncoding(payload)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;HMAC_SHA256&lt;/code&gt; is a type of keyed hash algorithm constructed from the SHA-256 hash function that hashes the signature. The choice of the cryptographic algorithm comes from the &lt;code&gt;&amp;quot;alg&amp;quot;: &amp;quot;HS256&amp;quot;&lt;/code&gt; in the header. If the token is unsigned, it&amp;#39;ll only have the header and payload without the signature.&lt;/p&gt;
&lt;h2&gt;JWTs Vs. Other Authentication Methods for Your Ruby App&lt;/h2&gt;
&lt;p&gt;JSON Web Tokens, as the name implies, are token-based. On the other end of the spectrum, we have session-based authentication: a more traditional way of authenticating users. The flow of session-based authentication is quite different from that of token-based authentication.&lt;/p&gt;
&lt;p&gt;The flow of session-based authentication might look like the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A user or client sends a request which contains user credentials.&lt;/li&gt;
&lt;li&gt;The server authenticates the user, stores a session, and returns a session ID stored as a cookie in the browser.&lt;/li&gt;
&lt;li&gt;The client sends the cookies along with its subsequent requests to the server.&lt;/li&gt;
&lt;li&gt;The server inspects the session information presented and, if valid, authenticates the user and returns the requested information to the client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While session-based authentication is mostly used for client-server connections, token-based authentication is often used with server-server connections (e.g., between two APIs).&lt;/p&gt;
&lt;p&gt;One important difference to note, however, is that with session-based authentication, the authentication state is handled on the server — while tokens are managed on the client.&lt;/p&gt;
&lt;h2&gt;Why Use JWTs for Authentication?&lt;/h2&gt;
&lt;p&gt;Aside from being relatively simple to implement, there are a few other advantages of using JSON Web Tokens for authentication, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They are &lt;strong&gt;stateless&lt;/strong&gt;, meaning that a session store is not necessary. The token itself contains all of the user information, so there is no need to query a database or authentication server for information on each request.&lt;/li&gt;
&lt;li&gt;JWTs are &lt;strong&gt;generally more performant&lt;/strong&gt; than most traditional methods of authentication (as long as the server doesn&amp;#39;t do any lookups against a database or store to authenticate a user), making them quite efficient.&lt;/li&gt;
&lt;li&gt;They also offer &lt;strong&gt;solid security guarantees&lt;/strong&gt;, in that signed JWTs provide safeguards so an attacker or client can&amp;#39;t modify the tokens to gain access to protected data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;JWT Best Practices for Ruby Apps&lt;/h2&gt;
&lt;p&gt;It goes without saying that the secret keys used to sign JWTs should be long, random, and have complex character combinations. This ensures that the keys are adequately safe and it&amp;#39;s hard for attackers to brute-force them.&lt;/p&gt;
&lt;p&gt;The secret keys Rails generates are safe for the most part, but safety guarantees are nullified if you accidentally commit keys and reveal them.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s also important to use Transport Layer Security (TLS) when transporting tokens between parties on a network. TLS can mitigate a man-in-the-middle attack (both token and session-based authentication methods are prone to such attacks).&lt;/p&gt;
&lt;h2&gt;Implementing JWT authentication in a Rails App&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s take a look at a token-based authentication flow:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-08/jwt-auth-flow.png&quot; alt=&quot;Token-based Authentication Flow&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Unlike session-based authentication, a stateful authentication technique where we use sessions to keep track of an authenticated user, token-based authentication with JWTs is stateless; there&amp;#39;s no need to store any information about a user&amp;#39;s authentication state on the server. This simplifies application design.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll assume that our application is split into a frontend and a backend. Authentication occurs on the backend, so we will be building a Rails API backend with authentication.&lt;/p&gt;
&lt;p&gt;The sample code in this post is based on Rails 7.0.5 and Ruby 3.2.2.&lt;/p&gt;
&lt;h3&gt;Using the &lt;code&gt;jwt&lt;/code&gt; and &lt;code&gt;bcrypt&lt;/code&gt; Ruby Gems&lt;/h3&gt;
&lt;p&gt;We&amp;#39;ll need two gems for our application: &lt;code&gt;jwt&lt;/code&gt; and &lt;code&gt;bcrypt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jwt&lt;/code&gt; is a &lt;a href=&quot;https://github.com/jwt/ruby-jwt&quot;&gt;Ruby implementation&lt;/a&gt; of the RFC 7519 OAuth JSON Web Token standard. &lt;code&gt;bcrypt&lt;/code&gt; is a Ruby binding for the OpenBSD &lt;code&gt;bcrypt()&lt;/code&gt; password hashing algorithm.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/siaw23/jwt_rails_api&quot;&gt;You can follow along with the sample code in this code repo.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;&lt;code&gt;jwt&lt;/code&gt; is not the only solution for working with JWTs; another well-known gem is &lt;code&gt;devise-jwt&lt;/code&gt;, which provides JWT authentication for Devise and Rails. But we&amp;#39;ll focus on &lt;code&gt;jwt&lt;/code&gt; in this post.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started.&lt;/p&gt;
&lt;h3&gt;Building our Rails API&lt;/h3&gt;
&lt;p&gt;The first thing we need is an API application. We&amp;#39;ll create one with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails new jwt_rails_api --api
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;--api&lt;/code&gt; option here preconfigures a smaller stack of Rails for API applications only.&lt;/p&gt;
&lt;p&gt;Inside the Gemfile, we can add our first dependency, &lt;code&gt;jwt&lt;/code&gt;. Our second gem, &lt;code&gt;bcrypt&lt;/code&gt;, is already in the Gemfile of a newly-generated Rails application — we only need to uncomment it.&lt;/p&gt;
&lt;p&gt;We need &lt;code&gt;bcrypt&lt;/code&gt; to securely hash user passwords in the database. It&amp;#39;s important to note that we won&amp;#39;t use &lt;code&gt;bcrypt&lt;/code&gt; directly. We&amp;#39;ll leverage Active Model&amp;#39;s &lt;code&gt;has_secure_password&lt;/code&gt; class method, which depends on &lt;code&gt;bcrypt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ignoring the default gems that come with a new Rails application, our Gemfile should look something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;jwt&amp;#39;, &amp;#39;~&amp;gt; 2.7&amp;#39;
gem &amp;quot;bcrypt&amp;quot;, &amp;quot;~&amp;gt; 3.1.7&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Now is a good time to install our gems with &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Generate &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Product&lt;/code&gt; Models&lt;/h3&gt;
&lt;p&gt;Next, we&amp;#39;ll generate two models: &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Product&lt;/code&gt;. &lt;code&gt;User&lt;/code&gt; will be the model to represent users and we&amp;#39;ll authenticate it to allow access to products, represented by the &lt;code&gt;Product&lt;/code&gt; model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails g model User username:string password_digest:string
rails g model Product name:string description:text
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running our migration with &lt;code&gt;rails db:migrate&lt;/code&gt;, our setup with models is complete, and our schema, found in &lt;code&gt;db/schema.rb&lt;/code&gt;, should now look similar to this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Schema[7.0].define(version: 2023_05_28_224534) do
  create_table &amp;quot;products&amp;quot;, force: :cascade do |t|
    t.string &amp;quot;name&amp;quot;
    t.text &amp;quot;description&amp;quot;
    t.datetime &amp;quot;created_at&amp;quot;, null: false
    t.datetime &amp;quot;updated_at&amp;quot;, null: false
  end

  create_table &amp;quot;users&amp;quot;, force: :cascade do |t|
    t.string &amp;quot;username&amp;quot;
    t.string &amp;quot;password_digest&amp;quot;
    t.datetime &amp;quot;created_at&amp;quot;, null: false
    t.datetime &amp;quot;updated_at&amp;quot;, null: false
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;It&amp;#39;s important to point out at this stage that we&amp;#39;re deliberately ignoring potential issues such as database constraints, validations, ensuring uniqueness, etc. For the sake of this post, we&amp;#39;ll disregard handling errors, among others. Our purpose here is to demonstrate JWTs in action as a form of stateless authentication to secure our Ruby application. In a production application, you&amp;#39;d want to make sure all of these are covered.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Build a &lt;code&gt;jwt&lt;/code&gt; Gem Wrapper&lt;/h3&gt;
&lt;p&gt;The next step is to build a wrapper around the &lt;code&gt;jwt&lt;/code&gt; gem we installed earlier. We&amp;#39;ll use this wrapper to encode and decode claims from the server to the client. For this, we&amp;#39;ll create an &lt;code&gt;app/lib&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;The reason we&amp;#39;re not using the &lt;code&gt;lib&lt;/code&gt; folder that comes with Rails is that it&amp;#39;s not autoloaded. Everything under &lt;code&gt;app&lt;/code&gt; is autoloaded and eager-loaded by default, making for a simpler setup in our case.&lt;/p&gt;
&lt;p&gt;Our wrapper class is found in &lt;code&gt;app/lib/json_web_token.rb&lt;/code&gt; and looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class JsonWebToken
  JWT_SECRET = Rails.application.secrets.secret_key_base

  def self.encode(payload, exp = 12.hours.from_now)
    payload[:exp] = exp.to_i

    JWT.encode(payload, JWT_SECRET)
  end

  def self.decode(token)
    body = JWT.decode(token, JWT_SECRET)[0]

    HashWithIndifferentAccess.new(body)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main methods here are &lt;code&gt;encode&lt;/code&gt; — to encode user information — and &lt;code&gt;decode&lt;/code&gt; — to later decode user information in the server. Note how we&amp;#39;re delegating the encoding and decoding tasks to the &lt;code&gt;jwt&lt;/code&gt; gem through &lt;code&gt;JWT.encode&lt;/code&gt; and &lt;code&gt;JWT.decode&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point, you can already test this class in your Rails console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;data = {&amp;quot;name&amp;quot;=&amp;gt;&amp;quot;AppSignal&amp;quot;}

JsonWebToken.encode(data)
# =&amp;gt; &amp;quot;eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiQXBwU2lnbmFsIiwiZXhwIjoxNjg1NDI0MjI5fQ.zWJyFHH8Pa6phBOU99XgtRntyfZQSOTX4TdwOxFY9gY&amp;quot;

JsonWebToken.decode(JsonWebToken.encode(data))
# =&amp;gt; {&amp;quot;name&amp;quot;=&amp;gt;&amp;quot;AppSignal&amp;quot;, &amp;quot;exp&amp;quot;=&amp;gt;1685424262}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how the result of &lt;code&gt;JsonWebToken.encode(data)&lt;/code&gt; is split into three parts by a period, producing the header, payload, and signature. We have the signature bit because we signed our payload with a secret key that Rails provides through &lt;code&gt;Rails.application.secrets.secret_key_base&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Back to the &lt;code&gt;User&lt;/code&gt; Model&lt;/h3&gt;
&lt;p&gt;Now will be a good time to visit our &lt;code&gt;User&lt;/code&gt; model at &lt;code&gt;app/models/user.rb&lt;/code&gt;. All we need to do here is add the &lt;code&gt;has_secure_password&lt;/code&gt; class method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  has_secure_password
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;has_secure_password&lt;/code&gt; &lt;a href=&quot;https://guides.rubyonrails.org/active_model_basics.html#securepassword&quot;&gt;securely hashes&lt;/a&gt; our users&amp;#39; passwords in the database.&lt;/p&gt;
&lt;h3&gt;Create a Sample User and Product in Rails&lt;/h3&gt;
&lt;p&gt;Now we can hop into the Rails console to generate a sample user and product in our database and test the security of our application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.create(username: &amp;quot;emi&amp;quot;, password: &amp;quot;password&amp;quot;)
Product.create(name: &amp;quot;Rad Ruby&amp;quot;, description: &amp;quot;A book collection of Ruby tips&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can place the same piece of code in your &lt;code&gt;seeds.rb&lt;/code&gt; file to save some typing in case you reset your database.&lt;/p&gt;
&lt;h3&gt;Using JWTs in Rails Controllers&lt;/h3&gt;
&lt;p&gt;In the next steps, we&amp;#39;ll implement security using JWTs inside our controllers. A good place to start is the &lt;code&gt;ApplicationController&lt;/code&gt; at &lt;code&gt;app/controllers/application_controller.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ApplicationController &amp;lt; ActionController::API
  before_action :authenticate

  rescue_from JWT::VerificationError, with: :invalid_token
  rescue_from JWT::DecodeError, with: :decode_error

  private

  def authenticate
    authorization_header = request.headers[&amp;#39;Authorization&amp;#39;]
    token = authorization_header.split(&amp;quot; &amp;quot;).last if authorization_header
    decoded_token = JsonWebToken.decode(token)

    User.find(decoded_token[:user_id])
  end

  def invalid_token
    render json: { invalid_token: &amp;#39;invalid token&amp;#39; }
  end

  def decode_error
    render json: { decode_error: &amp;#39;decode error&amp;#39; }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we create an &lt;code&gt;authenticate&lt;/code&gt; method to decode JSON Web tokens that users send us. If we can successfully verify the token, we return the &lt;code&gt;User&lt;/code&gt; object that represents the user making the request. We&amp;#39;re mostly interested in the happy path here and will bypass a lot of checks.&lt;/p&gt;
&lt;p&gt;Defining the &lt;code&gt;authenticate&lt;/code&gt; method in the &lt;code&gt;ApplicationController&lt;/code&gt; and setting it up as a &lt;code&gt;before_action&lt;/code&gt; secures every controller inheriting from it. A request to any other controller will need a valid JWT to access those controllers (because every other controller will inherit this main controller).&lt;/p&gt;
&lt;p&gt;Next, we need an &lt;code&gt;AuthenticationController&lt;/code&gt; to which users can send requests and get a signed JSON Web Token from our server. This controller should be placed at &lt;code&gt;app/controllers/authentication_controller.rb&lt;/code&gt; and may look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AuthenticationController &amp;lt; ApplicationController
  skip_before_action :authenticate

  def login
    user = User.find_by(username: params[:username])
    authenticated_user = user&amp;amp;.authenticate(params[:password])

    if authenticated_user
      token = JsonWebToken.encode(user_id: user.id)
      expires_at = JsonWebToken.decode(token)[:exp]

      render json: { token:, expires_at: }, status: :ok
    else
      render json: { error: &amp;#39;unauthorized&amp;#39; }, status: :unauthorized
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a request hits this controller, because it&amp;#39;s a user asking for a token, we don&amp;#39;t want to initially authenticate them. The purpose of this controller is to respond with a token the user can use to access the rest of the resources on our server. Hence the need for &lt;code&gt;skip_before_action :authenticate&lt;/code&gt; on the second line.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;login&lt;/code&gt; action (the one that users hit for a token), we grab the &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; from the parameters that come with the request to this controller. If we can authenticate the user — that is, verify if their username and password match what we have stored in our database — then we present them with a signed token and information about when that token expires.&lt;/p&gt;
&lt;p&gt;In our case, we won&amp;#39;t use the expiry period of the token. But in a production application, that could be used to revoke access to a resource.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll go through all these steps using &lt;code&gt;curl&lt;/code&gt; later on.&lt;/p&gt;
&lt;h2&gt;Testing Our Ruby Application with a Protected Resource&lt;/h2&gt;
&lt;p&gt;We partly covered the flow in the earlier &amp;#39;Implementing JWT authentication in a Rails App&amp;#39; section&amp;#39;s flow diagram. To completely cover everything and test out all the steps in the flow diagram, we need a resource to protect.&lt;/p&gt;
&lt;p&gt;We have a &lt;code&gt;Product&lt;/code&gt; model already. We now need a controller for the product model and routes to access the product and tokens.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s create a controller with &lt;code&gt;rails g controller Product index&lt;/code&gt; so we have something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ProductsController &amp;lt; ApplicationController
  before_action :authenticate

  def index
    @products = Product.all

    render json: @products
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, we need a route to access these controllers. Our &lt;code&gt;config/routes.rb&lt;/code&gt; should look something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.routes.draw do
  post &amp;#39;login&amp;#39;, to: &amp;quot;authentication#login&amp;quot;
  get &amp;#39;products&amp;#39;, to: &amp;quot;products#index&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&amp;#39;ll test with &lt;code&gt;curl&lt;/code&gt; to see if everything works as expected. Note that we already have a user to authenticate and a product resource to access.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s try getting a JWT with a user that doesn&amp;#39;t exist:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;curl -H &amp;quot;Content-Type: application/json&amp;quot; -X POST -d &amp;#39;{&amp;quot;username&amp;quot;:&amp;quot;manny&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;password&amp;quot;}&amp;#39; http://localhost:3000/login
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We should get the following response:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;{&amp;quot;error&amp;quot;:&amp;quot;unauthorized&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now try the same with a user that we created earlier:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;curl -H &amp;quot;Content-Type: application/json&amp;quot; -X POST -d &amp;#39;{&amp;quot;username&amp;quot;:&amp;quot;emi&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;password&amp;quot;}&amp;#39; http://localhost:3000/login
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should give us a signed JSON web token that could look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;{&amp;quot;token&amp;quot;:&amp;quot;eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE2ODU0NTEyMTR9.1UEYAbmFOSF93yp9pJqNEzkdHr3rVqutPNZWRIPDYkY&amp;quot;,&amp;quot;expires_at&amp;quot;:1685432077}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s keep this token for a second and try accessing a product resource with a bad token (I changed a random character in the token):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;curl -H &amp;quot;Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE2ODU0NTEyMTR9.1UEYAbmFOSF93yp9pJqNEzkdHr3rVqutPNZWRIPZYkY&amp;quot; http://localhost:3000/products
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we should get:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;{&amp;quot;decode_error&amp;quot;:&amp;quot;decode error&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, if we make the same request to access the product resource with the valid token we got from the server previously:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;curl -H &amp;quot;Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE2ODU0NTEyMTR9.1UEYAbmFOSF93yp9pJqNEzkdHr3rVqutPNZWRIPDYkY&amp;quot; http://localhost:3000/products
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;re granted access to the product resource:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;[{&amp;quot;id&amp;quot;:1,&amp;quot;name&amp;quot;:&amp;quot;Rad Ruby&amp;quot;,&amp;quot;description&amp;quot;:&amp;quot;A book collection of Ruby tips&amp;quot;,&amp;quot;created_at&amp;quot;:&amp;quot;2023-05-29T19:33:30.826Z&amp;quot;,&amp;quot;updated_at&amp;quot;:&amp;quot;2023-05-29T19:33:30.826Z&amp;quot;}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s it! We&amp;#39;ve successfully secured our Ruby application with a JSON web token!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we discussed JSON Web Tokens and how they work. We first covered the basics of JWTs, including their structure and some best practices. Then we implemented a simple JWT authentication using the &lt;code&gt;jwt&lt;/code&gt; gem.&lt;/p&gt;
&lt;p&gt;I hope you&amp;#39;ve found this post helpful. Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Advanced Usages of Devise for Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/08/02/advanced-usages-of-devise-for-rails.html"/>
    <id>https://blog.appsignal.com/2023/08/02/advanced-usages-of-devise-for-rails.html</id>
    <published>2023-08-02T00:00:00+00:00</published>
    <updated>2023-08-02T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s dive into some advanced usages of Devise, including the use of OmniAuth, API authentication, and Authtrail.</summary>
    <content type="html">&lt;p&gt;In part one of this series, we introduced Devise using an example app to explore modules, helpers, views, controllers, and routes.&lt;/p&gt;
&lt;p&gt;In this part, we&amp;#39;ll explore more advanced usages of Devise, specifically the use of OmniAuth, API authentication, and Authtrail.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive straight in!&lt;/p&gt;
&lt;h2&gt;Authentication with OmniAuth for Ruby&lt;/h2&gt;
&lt;p&gt;Nowadays, almost every web application you come across will offer you the option to log in via a wide selection of authentication providers, ranging from social networks like Twitter and Facebook to Google, GitHub, and many more.&lt;/p&gt;
&lt;p&gt;In many cases, this convenient multi-provider authentication is powered by a library called &lt;a href=&quot;https://github.com/omniauth/omniauth&quot;&gt;OmniAuth&lt;/a&gt;. OmniAuth is a flexible and powerful authentication library for Ruby that allows you to integrate with multiple external providers.&lt;/p&gt;
&lt;p&gt;It provides a simple and unified API for connecting to various OAuth providers. OmniAuth is particularly useful in situations where you want to give your users the option to sign up or log in using their social media accounts. With OmniAuth, you can easily add social login functionality to your Rails application.&lt;/p&gt;
&lt;p&gt;When OmniAuth is used alongside the Devise gem, it becomes even easier to manage user authentication and authorization. You can take advantage of Devise&amp;#39;s built-in authentication features, while using OmniAuth for external provider sign-in options.&lt;/p&gt;
&lt;h2&gt;Getting Started with OmniAuth and Devise&lt;/h2&gt;
&lt;p&gt;As mentioned, OmniAuth allows you to integrate with a number of third-party authentication providers. For the purposes of this article, we&amp;#39;ll use GitHub.&lt;/p&gt;
&lt;h3&gt;Install OmniAuth Gems&lt;/h3&gt;
&lt;p&gt;In your app&amp;#39;s &lt;code&gt;Gemfile&lt;/code&gt;, add the following lines:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

gem &amp;#39;omniauth&amp;#39;
gem &amp;#39;omniauth-github&amp;#39; # each provider will have their own gem to work with Omniauth
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if you&amp;#39;re using the OmniAuth 2.0+ gem, you&amp;#39;ll also need to add:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;#  Gemfile

gem &amp;#39;omniauth-rails_csrf_protection&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;omniauth-rails_csrf_protection&lt;/code&gt; gem ensures that any &lt;code&gt;GET&lt;/code&gt; requests to the OAuth flow are disabled. It also inserts a Rails CSRF token verifier before the OAuth request phase. These two actions are meant to mitigate cross-site forgery attacks aimed at the OAuth authentication flow.&lt;/p&gt;
&lt;p&gt;Next, run &lt;code&gt;bundle install&lt;/code&gt; to install the gems.&lt;/p&gt;
&lt;h3&gt;Create a New GitHub OAuth App&lt;/h3&gt;
&lt;p&gt;Now we need to create a new OAuth app on GitHub. This app will act as a user with authentication permissions which can be easily revoked, if needed.&lt;/p&gt;
&lt;p&gt;The first step is to go to the settings page under your GitHub account profile. Then, on the left-hand menu, click on &amp;quot;Developer settings&amp;quot;. You&amp;#39;ll see a screen like the one below, where you can create a new OAuth app:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-08/create-new-github-oauth-app.png&quot; alt=&quot;GitHub developer settings page&quot;/&gt;&lt;/p&gt;
&lt;p&gt;When you click on &amp;quot;Register new application&amp;quot;, you&amp;#39;ll get a screen like this one:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-08/new-github-oauth-app.png&quot; alt=&quot;Create new OAuth app&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Fill in the form details as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Application name&lt;/strong&gt; - Give your new OAuth app an appropriate name.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Homepage url&lt;/strong&gt; - For now, use &lt;code&gt;http://localhost:3000/&lt;/code&gt;. In production, you would use your app&amp;#39;s actual homepage url.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application description&lt;/strong&gt; - Not necessary, but you can still have one if you have many apps and need to differentiate between them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization callback url&lt;/strong&gt; - This is required input and will generally follow an OAuth callback url format like &lt;code&gt;http://&amp;lt;app-url&amp;gt;/users/auth/&amp;lt;application-provider&amp;gt;/callback&lt;/code&gt;. That said, some OAuth providers like Google may not follow this format, so you need to take note of that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that done, click on &amp;quot;Register application&amp;quot;. In the following screen, generate a new app secret and note it down someplace safe (as it will only be shown to you once).&lt;/p&gt;
&lt;h3&gt;Configure the Devise Initializer&lt;/h3&gt;
&lt;p&gt;Open up the Devise initializer &lt;code&gt;config/initializers/devise.rb&lt;/code&gt; and navigate to the OmniAuth section specific to GitHub. It will likely be commented out, so uncomment it and edit it with your new GitHub OAuth app&amp;#39;s ID and secret:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/devise.rb

...
  config.omniauth :github, ENV[&amp;#39;GITHUB_APP_ID&amp;#39;], ENV[&amp;#39;GITHUB_APP_SECRET&amp;#39;], scope: &amp;#39;user,public_repo&amp;#39;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Create the OmniAuth Callbacks Controller&lt;/h3&gt;
&lt;p&gt;If you have already generated Devise controllers, you&amp;#39;ll find the &lt;code&gt;OmniauthCallbacksController&lt;/code&gt; ready for you to customize accordingly. If yours isn&amp;#39;t there, just create one manually, and edit it as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController &amp;lt; Devise::OmniauthCallbacksController
  # You can easily add more OmniAuth providers here
  ...

  def github
    @user = User.from_omniauth(request.env[&amp;quot;omniauth.auth&amp;quot;])
    sign_in_and_redirect @user # sign_in_and_redirect is an OAuth method
  end
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some things to note about the above code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;from_omniauth&lt;/code&gt; is a method we&amp;#39;ll implement inside our &lt;code&gt;User&lt;/code&gt; model.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sign_in_and_redirect&lt;/code&gt; is a method inside of OAuth.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Add a Migration to Modify the User Model&lt;/h3&gt;
&lt;p&gt;We now need to add some columns to the User model, specifically the &lt;code&gt;provider&lt;/code&gt; column and a &lt;code&gt;uid&lt;/code&gt; column:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails g migration AddOmniauthToUsers provider:string uid:string
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;bundle exec rails db:migrate&lt;/code&gt; to finalize this step.&lt;/p&gt;
&lt;h3&gt;Make the Devise Model Omniauthable&lt;/h3&gt;
&lt;p&gt;Here, we&amp;#39;ll need to edit our &lt;code&gt;User&lt;/code&gt; model by adding the Devise Omniauthable module to it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/user.rb

class User &amp;lt; ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :omniauthable
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After this, add the &lt;code&gt;from_omni_auth&lt;/code&gt; method. This will be called from the &lt;code&gt;Users::OmniauthCallbacksController&lt;/code&gt; that we just set up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/user.rb

class User &amp;lt; ApplicationRecord
  ...

  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user |
      user.provider = auth.provider
      user.uid = auth.uid
      user.email = auth.info.email
      user.password = Devise.friendly_token[0,20]
    end
  end

  ...

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&amp;#39;re left with just one more thing: adding the login links to our Devise views.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Setup the Login Links&lt;/h3&gt;
&lt;p&gt;By default, Devise will automatically add the appropriate provider&amp;#39;s login link for you in the user registration and login views. This link will use the &lt;code&gt;GET&lt;/code&gt; method, but we know that OmniAuth 2.0+ prefers &lt;code&gt;POST&lt;/code&gt; requests. So, we need to disable the link and insert our own using &lt;code&gt;POST&lt;/code&gt; requests:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/views/users/sessions/new.html.erb

# notice we use a button_to which results in POST requests by default

&amp;lt;%= button_to &amp;quot;Sign in with Github&amp;quot;, user_github_omniauth_authorize_path %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, we&amp;#39;ve successfully set up a Ruby on Rails 7 app with Devise and GitHub OAuth authentication. &lt;a href=&quot;https://github.com/iamaestimo/tasker_app&quot;&gt;You can grab the full source code of the companion app here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, we&amp;#39;ll get into another advanced use case: using Devise to authenticate an API call.&lt;/p&gt;
&lt;h2&gt;API Authentication with Devise for Ruby&lt;/h2&gt;
&lt;p&gt;Today, it&amp;#39;s not uncommon for users to expect to be able to connect to your app via an API. In this section, we&amp;#39;ll look at how we can safely authenticate such user requests using Devise.&lt;/p&gt;
&lt;p&gt;Where browser-based authentication is generally cookie-based, most API authentication happens via tokens called JSON Web Tokens (or simply JWTs), which are passed around in the headers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip&lt;/strong&gt;: For the purposes of this section, we&amp;#39;ll assume we&amp;#39;re working with a Rails API-only app. To follow along, create one with &lt;code&gt;rails new app_name --api&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;The JWT-based Authentication Flow&lt;/h3&gt;
&lt;p&gt;As we mentioned, API authentication is based on JWT tokens, and it&amp;#39;s important that we understand how a JWT-based authentication flow happens. Basically, it follows a general sequence of steps as outlined below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A user client makes a call to the API app.&lt;/li&gt;
&lt;li&gt;The API app responds with a JSON Web Token (JWT), an authentication token that can be used in place of cookies.&lt;/li&gt;
&lt;li&gt;Subsequent requests by the user client are made using this token in the &lt;code&gt;Authorization&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;The user can then hit the Devise &amp;#39;session destroy&amp;#39; action, which results in the destruction of the token and the user being logged out.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, let&amp;#39;s put this flow into action, starting with what is termed cross-origin resource sharing (CORS).&lt;/p&gt;
&lt;h3&gt;Setting Up CORS&lt;/h3&gt;
&lt;p&gt;CORS sets up our API app to allow requests from external sources. CORS is a HTTP-based security policy that defines how external requests will be handled by your application. By default, CORS will block any request that originates from a domain different to the one that made the initial request (in other words, a request coming from a different &amp;quot;origin&amp;quot;).&lt;/p&gt;
&lt;p&gt;To handle CORS properly, we&amp;#39;ll use the nifty gem &lt;code&gt;rack-cors&lt;/code&gt;. In the &lt;code&gt;Gemfile&lt;/code&gt;, uncomment the line below, then run &lt;code&gt;bundle install&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile
gem &amp;#39;rack-cors&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also, open up a corresponding CORS initializer file and modify it as below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins &amp;quot;*&amp;quot;

    resource &amp;quot;*&amp;quot;,
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head],
      expose: %w[Authorization Uid]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some important notes about what we&amp;#39;ve just done:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;origins &amp;quot;*&amp;quot;&lt;/code&gt; - Simply means that our API app can now receive requests from any other source.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expose: %w[Authorization Uid]&lt;/code&gt; - By default, the &lt;code&gt;rack-cors&lt;/code&gt; gem doesn&amp;#39;t expose the authorization and UID headers, but we need them since we&amp;#39;ll be passing authorization tokens through.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that done, let&amp;#39;s install Devise and the accompanying Devise-JWT gem.&lt;/p&gt;
&lt;h3&gt;Add Devise and Devise-JWT Gems to Your Rails App&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/waiting-for-dev/devise-jwt&quot;&gt;&lt;code&gt;devise-jwt&lt;/code&gt; gem&lt;/a&gt; is an extension of Devise that will allow us to work with JWT tokens. Add the gems to &lt;code&gt;Gemfile&lt;/code&gt;, then run &lt;code&gt;bundle install&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

gem &amp;#39;devise&amp;#39;
gem &amp;#39;devise-jwt&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the Devise install generator &lt;code&gt;bundle exec rails g devise:install&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Generate and Configure the Models&lt;/h3&gt;
&lt;p&gt;We need to set up two models: the normal Devise user model (&lt;code&gt;bundle exec rails g devise User&lt;/code&gt;), and a model that we&amp;#39;ll use for the revocation strategy (in other words, how a user will sign out of the API):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails g model jwt_revocation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We modify the normal Devise user model for API authentication by adding the JWT token authenticatable module and defining the token revocation strategy to use our second model, &lt;code&gt;JwtDenylist&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/user.rb
class User &amp;lt; ApplicationRecord
  devise :database_authenticatable, :registerable,
  :jwt_authenticatable, jwt_revocation_strategy: JwtDenylist
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we configure our second model by referencing the revocation strategy and the revocation table to be used:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/jwt_denylist.rb

class JwtDenylist &amp;lt; ApplicationRecord
  include Devise::JWT::RevocationStrategies::Denylist
  self.table_name = &amp;#39;jwt_denylist&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the following section, we&amp;#39;ll outline token revocation and why we need it.&lt;/p&gt;
&lt;h3&gt;The Importance of Token Revocation&lt;/h3&gt;
&lt;p&gt;Why is token revocation important? Because JWT tokens are stateless. The server knows nothing about them other than signing them. In such a scenario, the server has no way to sign out a user by revoking the corresponding token. Since there&amp;#39;s no way to revoke individual tokens, we need to build one out and tell the server to use it.&lt;/p&gt;
&lt;p&gt;When revoking a token, what&amp;#39;s really happening under the hood is that a unique piece of the token, the &lt;code&gt;jti&lt;/code&gt; (JWT ID), is extracted and used according to the defined revocation strategy.&lt;/p&gt;
&lt;p&gt;Of course, this raises another question of what a token revocation strategy is. In a nutshell, it&amp;#39;s a definition of how token revocation will be handled by your server. There are three basic revocation strategies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JTIMatcher strategy&lt;/strong&gt; - Here, a unique column called &amp;quot;jti&amp;quot; is added to the user model, which also acts as the revocation table. Whenever a user makes a request, the &lt;code&gt;jti&lt;/code&gt; in the header is matched against the stored tokens and access is allowed only when a match is found.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Denylist strategy&lt;/strong&gt; - For this one, the &lt;code&gt;jti&lt;/code&gt; and the token expiry (&lt;code&gt;exp&lt;/code&gt; of revoked tokens) are stored in a database table. For every request made by a user, a check is made against this table comparing the user&amp;#39;s current token &lt;code&gt;jti&lt;/code&gt; against the revoked ones in the database. If a match is found, that user&amp;#39;s requests are denied.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Allowlist strategy&lt;/strong&gt; - In a way, this strategy is similar to the first strategy, except that here the table storing the JWT IDs is in a one-to-many relationship with another table storing the user tokens. Whenever a request is made, the user&amp;#39;s &lt;code&gt;jti&lt;/code&gt; stored in the Allowlist table is matched against what is stored in the matching table with tokens. Access is only allowed if a match is found.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Obviously, this is a very simplified overview of token revocation, and &lt;a href=&quot;https://github.com/waiting-for-dev/devise-jwt&quot;&gt;you can learn more about it here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Set Up a Signing Key for JWT Tokens&lt;/h3&gt;
&lt;p&gt;Since we&amp;#39;ll be using secure tokens to authenticate users and their requests, we need a way of signing them. This is where our secret key comes in. It is recommended that you generate a new key different from the Rails secret key, the &lt;code&gt;secret_key_base&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;bundle exec rake secret&lt;/code&gt; to generate a unique key, then edit the Devise initializer to include this key:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/devise.rb
Devise.setup do |config|
  ...
  config.jwt do |jwt|
    jwt.secret = ENV[&amp;#39;DEVISE_JWT_SECRET&amp;#39;]
  end
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let&amp;#39;s set up the controllers.&lt;/p&gt;
&lt;h3&gt;Controller Set Up&lt;/h3&gt;
&lt;p&gt;The final step to implement Devise for API authentication is to set up our controllers. For the sake of simplicity, we&amp;#39;ll set up two controllers: one to handle registration and the other one for sessions.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s create these manually, starting with the registrations controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/users/registrations/registrations_controller.rb

class Users::RegistrationsController &amp;lt; Devise::RegistrationsController

 respond_to :json

 private

 def respond_with(resource, _opts={})
  successful_registration &amp;amp;&amp;amp; return if resource.persisted?
  failed_registration
 end

 def successful_registration
  render json: { message: &amp;quot;You&amp;#39;ve registered successfully&amp;quot;, user: current_user }, status: :ok
 end

 def failed_registration
  render json: { message: &amp;quot;Something went wrong. Please try again&amp;quot; }, status: :unprocessable_entity
 end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&amp;#39;s what&amp;#39;s going on with this controller:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We configure it to respond to requests with JSON.&lt;/li&gt;
&lt;li&gt;We specify a &lt;code&gt;respond_with&lt;/code&gt; action that returns the result of a successful or a failed registration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And now for the sessions controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/users/sessions/sessions_controller.rb

class Users::SessionsController &amp;lt; Devise::SessionsController

 respond_to :json

 private

 def respond_with(resource, _opts={})
  render json: { message: &amp;quot;Welcome, you&amp;#39;re in&amp;quot;, user: current_user }, status: :ok
 end

 def respond_to_on_destroy
  successful_logout &amp;amp;&amp;amp; return if current_user
  failed_logout
 end

 def successful_logout
  render json: { message: &amp;quot;You&amp;#39;ve logged out&amp;quot; }, status: :ok
 end

 def failed_logout
  render json: { message: &amp;quot;Something went wrong.&amp;quot; }, status: :unauthorized
 end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like the registrations controller, here we specify that the controller will respond with JSON. We also define a &lt;code&gt;respond_with&lt;/code&gt; action for when a user successfully logs in and a &lt;code&gt;respond_to_on_destroy&lt;/code&gt; to handle a user signing out.&lt;/p&gt;
&lt;p&gt;And with that, you should have a working API authentication flow powered by Devise and JWT tokens!&lt;/p&gt;
&lt;p&gt;Our final section will take a quick look at how you can track user logins using Devise and Authtrail.&lt;/p&gt;
&lt;h2&gt;Tracking Devise Logins with Authtrail&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s say you want to send your app users a notification email whenever someone logs in to their account, with details such as the IP address and the timestamp of the login. How can you accomplish this?&lt;/p&gt;
&lt;p&gt;You&amp;#39;ll need to track user logins, then use that information in the notification emails you send your users. But first, you&amp;#39;ll need a way of tracking user logins. This can be accomplished using a nifty gem called &lt;a href=&quot;https://github.com/ankane/authtrail&quot;&gt;Authtrail&lt;/a&gt;, which also pairs well with Devise.&lt;/p&gt;
&lt;h3&gt;Installing Authtrail&lt;/h3&gt;
&lt;p&gt;The first step is to install the gem with &lt;code&gt;bundle add authtrail&lt;/code&gt;. Additionally, since you&amp;#39;ll be storing user-identifiable information such as emails and IP addresses in your app database, it&amp;#39;s highly recommended that you encrypt this data in production using a combination of &lt;a href=&quot;https://github.com/ankane/lockbox&quot;&gt;Lockbox&lt;/a&gt; and &lt;a href=&quot;https://github.com/ankane/blind_index&quot;&gt;Blindindex&lt;/a&gt; gems.&lt;/p&gt;
&lt;p&gt;Next, run the Authtrail generator to create its initializer and an accompanying table migration that will store the login data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# Notice here we run the generator with the encryption flag set to &amp;#39;lockbox&amp;#39;, you can also indicate --encryption=none for no encryption

bundle exec rails g authtrail:install --encryption=lockbox
bundle exec rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;How Authtrail Works&lt;/h3&gt;
&lt;p&gt;Whenever a user tries to log in, a new Authtrail record is created with the following important details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The login email address used&lt;/li&gt;
&lt;li&gt;Whether the login was successful or not&lt;/li&gt;
&lt;li&gt;The reason a login failed (if the login was a failure)&lt;/li&gt;
&lt;li&gt;The user&amp;#39;s IP address, &lt;code&gt;referrer&lt;/code&gt;, and a lot more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can then use this information as you wish. For example, you can send a notification email to a user, including the email and IP address information, to let them know that a login attempt was made on their account.&lt;/p&gt;
&lt;p&gt;Go through &lt;a href=&quot;https://github.com/ankane/authtrail&quot;&gt;Authtrail&amp;#39;s documentation&lt;/a&gt; to see all the possibilities available to you.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this series, we took a deep dive into the Devise gem.&lt;/p&gt;
&lt;p&gt;First, we got to grips with the basics of Devise, including how its modules, helpers, views, controllers, and routes work. In this second and final part, we explored how to use Devise with OAuth, Authtrail, and for API authentication.&lt;/p&gt;
&lt;p&gt;Hopefully, this series will serve as a helpful guide for all things Devise authentication.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Metaprogramming in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/07/26/an-introduction-to-metaprogramming-in-ruby.html"/>
    <id>https://blog.appsignal.com/2023/07/26/an-introduction-to-metaprogramming-in-ruby.html</id>
    <published>2023-07-26T00:00:00+00:00</published>
    <updated>2023-07-26T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Explore some of the fundamentals of metaprogramming in Ruby.</summary>
    <content type="html">&lt;p&gt;You&amp;#39;ve heard of metaprogramming: code you write that generates other code dynamically. Without it, Rails as we know it wouldn&amp;#39;t (and couldn&amp;#39;t) exist. But there&amp;#39;s a good chance you&amp;#39;ve never done it yourself, and it&amp;#39;s not hard to see why; even a brief excursion into the realm of metaprogramming can leave you beset with strange and foreign methods, unfamiliar syntax, and downright mystifying blocks of code.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s true: metaprogramming &lt;em&gt;is&lt;/em&gt; a relatively advanced topic. If you want to &lt;em&gt;really&lt;/em&gt; dig in and leverage it on a deep level, it will take time, effort, and a different way of thinking than you&amp;#39;re used to. But there&amp;#39;s good news: You don&amp;#39;t need to wade too deeply into the metaprogramming waters to discover useful methods and techniques to make your life and workflow a little easier.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll take a look at metaprogramming methods like &lt;code&gt;send&lt;/code&gt;, &lt;code&gt;define_method&lt;/code&gt;, and &lt;code&gt;method_missing&lt;/code&gt; and show how they can solve problems we sometimes run into, even in normal Rails applications. But first, let&amp;#39;s briefly define metaprogramming and explore when it might come in handy.&lt;/p&gt;
&lt;h2&gt;When Is Metaprogramming Useful?&lt;/h2&gt;
&lt;p&gt;Maybe you&amp;#39;ve got a couple of models in your app that, while being very similar, aren&amp;#39;t identical — they have different attributes and methods. But both need to be acted on in a job, and you don&amp;#39;t want to write what is essentially the same code more than once.&lt;/p&gt;
&lt;p&gt;Maybe once (or twice), you hacked together a &lt;del&gt;kludge&lt;/del&gt; masterpiece for a tight deadline on a new feature that got the job done, but you modified an existing model rather than creating a new one the way you would have if you&amp;#39;d had more time. Now that feature needs to be expanded, requiring you to do it the &amp;quot;right&amp;quot; way. This can be intimidating, especially if your codebase references your current implementation all over your app.&lt;/p&gt;
&lt;p&gt;Or maybe you rely on a third-party gem that isn&amp;#39;t well-maintained, and you discover far too late that it&amp;#39;s got a bug in it — right in the middle of a line of code full of metaprogramming.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ve had all these things happen, and I solved them by leveraging some light metaprogramming, all without having to be an expert, and without too much effort. You can, too.&lt;/p&gt;
&lt;h2&gt;What Is Metaprogramming in Ruby?&lt;/h2&gt;
&lt;p&gt;Metaprogramming is a set of techniques that Ruby offers us to write code that dynamically writes other code for us. Rather than working on data, metaprogramming works on other code. This is great because it means we can write more dynamic, flexible, and adaptable code if the situation calls for it.&lt;/p&gt;
&lt;p&gt;It can help DRY up portions of our code, dynamically define methods we might need, and write useful and reusable pieces of software (like gems!) to make our lives easier.&lt;/p&gt;
&lt;p&gt;In fact, Rails leverages metaprogramming on a large scale and to great effect. Any time anyone talks about Rails&amp;#39; magic, they&amp;#39;re really talking about its use of metaprogramming.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;Despite how useful metaprogramming is, it isn&amp;#39;t without its drawbacks.&lt;/p&gt;
&lt;p&gt;One issue is &lt;strong&gt;readability&lt;/strong&gt;: a little metaprogramming here and there can go a long way and likely won&amp;#39;t cause many headaches, but lots of it will hurt the readability of your code.
Many Rails engineers probably are not very familiar with it, so relying on it too heavily can prove frustrating both to your future self and to others who need to work on your code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt; is also a concern; heavy use of metaprogramming can be mind-bending for even experienced Ruby developers. This means you should &lt;em&gt;document your code&lt;/em&gt;. Ruby is an incredibly expressive, intuitive, and readable language, but if you&amp;#39;re doing things others might find confusing, you should leave comments. You should also use access modifiers - e.g., &lt;code&gt;private&lt;/code&gt;, &lt;code&gt;protected&lt;/code&gt; - the way you would with other methods.&lt;/p&gt;
&lt;p&gt;Another potential source of trouble: &lt;strong&gt;monkeypatching&lt;/strong&gt;. While monkeypatching (dynamic modification of a class — typically, a Rails core class) can be useful, it should also be used sparingly and carefully. If we&amp;#39;re not mindful, we can modify behavior in ways that, while perhaps solving one of our problems, can easily create dozens of others. For example, rather than fixing a small bug, adding a new, uniquely-named method, or simply extending the capabilities of a class, we might modify the behavior of existing methods used not just by our own application but by our gems (and even Rails itself), with potentially disastrous consequences. Check out our post &lt;a href=&quot;https://blog.appsignal.com/2021/08/24/responsible-monkeypatching-in-ruby.html&quot;&gt;Responsible Monkeypatching in Ruby&lt;/a&gt; for more on monkeypatching.&lt;/p&gt;
&lt;p&gt;Lastly, metaprogramming can make it &lt;strong&gt;hard to find things you&amp;#39;re looking for&lt;/strong&gt;, especially if you didn&amp;#39;t write the code in the first place. If you find yourself trying to figure out how it&amp;#39;s possible &lt;code&gt;CompanySpecificClass#our_special_method&lt;/code&gt; exists on an object but isn&amp;#39;t defined anywhere, metaprogramming could be the culprit (in this case, probably the use of &lt;code&gt;define_method&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Basic Ruby Metaprogramming Techniques&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s explore some of the methods we mentioned earlier: &lt;code&gt;send&lt;/code&gt;, &lt;code&gt;define_method&lt;/code&gt;, and &lt;code&gt;method_missing&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, we&amp;#39;ll take a look at &lt;code&gt;send&lt;/code&gt;, what it does, and how it can help us DRY up our code.&lt;/p&gt;
&lt;h3&gt;Using &lt;code&gt;send&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Remember the example we mentioned earlier (under the header &amp;#39;When Is Metaprogramming Useful?&amp;#39;) of a couple of similar models with different attributes and methods? In this case, both models need to be acted on in a job in a more or less identical manner.&lt;/p&gt;
&lt;p&gt;Essentially, invoking &lt;code&gt;.send&lt;/code&gt; allows us to dynamically pass methods (along with arguments) to an object, without necessarily knowing what that object (or method) might be at the time we write the code.&lt;/p&gt;
&lt;p&gt;First, a word of caution: &lt;code&gt;send&lt;/code&gt; can actually call both public and private methods. If you want to be very explicit (and careful), you can use &lt;code&gt;public_send&lt;/code&gt; to ensure it&amp;#39;s clear you&amp;#39;re calling a public method. I&amp;#39;ve never done this, but I&amp;#39;ve also never used it to call any &lt;code&gt;private&lt;/code&gt; or &lt;code&gt;protected&lt;/code&gt; methods; if you do, you might want to use &lt;code&gt;public_send&lt;/code&gt; and &lt;code&gt;send&lt;/code&gt; for each use case.&lt;/p&gt;
&lt;p&gt;So how does it work? Pretty simply. You just pass the method&amp;#39;s name to the object you want to call like this: &lt;code&gt;my_object.send(:method_name)&lt;/code&gt;. If you need to pass parameters/arguments, you can do so: &lt;code&gt;my_object.send(:method_name, argument1, argument2...)&lt;/code&gt;. &lt;code&gt;send&lt;/code&gt; accepts parameters the same way other methods do, so you can use named arguments, too.&lt;/p&gt;
&lt;p&gt;So rather than doing something verbose like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def sell_or_refund_or_return_item(item, action)
  if action == &amp;quot;sell&amp;quot;
    item.sell
  elsif action == &amp;quot;refund&amp;quot;
    item.refund
  elsif action == &amp;quot;void&amp;quot;
    item.void
  elsif action == &amp;quot;return&amp;quot;
    item.return
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;We can simply pass our method as an argument and call it with &lt;code&gt;send&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def process_item(item, action)
  return unless item.respond_to?(action)

  item.send(action)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: For the sake of simplicity and brevity, we&amp;#39;re not raising an error if our object doesn&amp;#39;t &lt;code&gt;respond_to?&lt;/code&gt; the action passed into our above methods. But in a real app, we&amp;#39;d probably want to raise an error to bring our attention to the fact something in our codebase is calling a non-existent method/attribute on the object.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;send&lt;/code&gt;: An Example Scenario&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s consider the following example. We&amp;#39;ve got a &lt;code&gt;Purchase&lt;/code&gt; model, representing a given purchase from an online store. That purchase could be anything from a single item of one type to many different items.&lt;/p&gt;
&lt;p&gt;In the instance below, we&amp;#39;re interested in people who bought a particular subscription, which may contain tickets to events (&lt;code&gt;purchase_events&lt;/code&gt;), and/or vouchers for products (&lt;code&gt;purchase_products&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Products and events are different things, but there&amp;#39;s a lot of overlap, like &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt; (sold, returned), etc. In this example, we will refund all instances of a particular item from a subscription, because customers were unable to redeem their purchase (an event was rained off, a company we source from ran out of a collectible, etc.).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class RefundAllInstancesOfItemJob
  def perform(item)
    purchases = Purchase.joins(:subscriptions)
                        .includes(:purchase_events, :purchase_products)
                        .where(subscriptions: { id: item.subscription_id, status: &amp;quot;active&amp;quot; })

    association, foreign_key, foreign_key_value = item.is_a?(PurchaseEvent) ? [:purchase_events, :event_id, item.event_id] : [:purchase_products, :product_id, item.product_id]

    purchases.find_each do |purchase|
      purchase.send(association)
              .where(purchase_id: purchase.id)
              .where(foreign_key =&amp;gt; foreign_key_value)
              .each { |item| item.update(status: &amp;quot;refunded&amp;quot;) }
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What have we done here? Ordinarily, we might have ended up writing some if/else logic and duplicating most of this code or even two separate jobs. Instead, we&amp;#39;ve dynamically sent the appropriate association and foreign key to objects within our query, keeping our code DRY.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve laid out the keys and associations here explicitly, but they could be passed in as arguments if we set up our call to the job differently. Our customers have now been refunded for the particular item(s) in this subscription cycle! (Let&amp;#39;s assume there&amp;#39;s a callback on those models that refunds the user when the item is marked &lt;code&gt;&amp;quot;refunded&amp;quot;&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Using &lt;code&gt;define_method&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Now, let&amp;#39;s look at &lt;code&gt;define_method&lt;/code&gt; and how it can help us with our earlier example where we moved over from storing data on one model where it didn&amp;#39;t really belong to a new dedicated model.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say we currently have a &lt;code&gt;Presentation&lt;/code&gt; model. When we first added video capabilities to our app, we did it because our biggest customer had to play a single, long video about their business to their shareholders. We had a tight deadline, so rather than create a new, dedicated model, we just tacked on a few columns to our &lt;code&gt;Presentation&lt;/code&gt; table, and it got the job done.&lt;/p&gt;
&lt;p&gt;Now, though, because we&amp;#39;ve advertised our app&amp;#39;s recording and streaming capabilities, other clients are interested — and they need more than one video per &lt;code&gt;Presentation&lt;/code&gt;. Unfortunately, we&amp;#39;re still in a bit of a time crunch and have to roll this out fast.&lt;/p&gt;
&lt;p&gt;We decide the quickest way to accomplish this without changing tons of code is to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new &lt;code&gt;VideoAsset&lt;/code&gt; model&lt;/li&gt;
&lt;li&gt;Move existing columns over from &lt;code&gt;Presentation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set a boolean &lt;code&gt;current_asset&lt;/code&gt; which will update when our clients select the video they want&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Presentation
  has_many :video_assets

  def current_video_asset
    video_assets.find_by(current_asset: true)
  end

  # Replace our dropped columns as new instance methods on Presentation
  # We&amp;#39;ve used the safety operator (&amp;amp;) on send the way we would any other method with a potential nil return value
  [&amp;quot;status&amp;quot;, &amp;quot;playback_id&amp;quot;, &amp;quot;asset_id&amp;quot;].each do |column_suffix|
    define_method(column_suffix) do
      current_video_asset&amp;amp;.send(column_suffix)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&amp;#39;s going on here? We&amp;#39;re dynamically defining methods on our &lt;code&gt;Presentation&lt;/code&gt; model to replace the columns we dropped and moved over to &lt;code&gt;VideoAsset&lt;/code&gt;. The existing calls in our codebase to &lt;code&gt;@presentation&lt;/code&gt; objects will now first find the current &lt;code&gt;VideoAsset&lt;/code&gt; associated with our &lt;code&gt;Presentation&lt;/code&gt; and then call the corresponding column. We don&amp;#39;t have to go around updating controllers and views everywhere!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It&amp;#39;s worth noting that the methods defined above could also have been accomplished using ActiveSupport&amp;#39;s &lt;a href=&quot;https://api.rubyonrails.org/classes/Module.html#method-i-delegate&quot;&gt;delegate&lt;/a&gt; helper. &lt;code&gt;delegate&lt;/code&gt; allows you to use this pattern more often, abstracting away the metaprogramming implementation:&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Presentation
  has_many :video_assets
  delegate :status, :playback_id, :asset_id, to: :current_video_asset

  def current_video_asset
    video_assets.find_by(current_asset: true)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using &lt;code&gt;method_missing&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Finally, let&amp;#39;s look at &lt;code&gt;method_missing&lt;/code&gt;. It does pretty much what you&amp;#39;d expect, given its name — it allows you to account for methods that don&amp;#39;t exist but are called on an object or class. Let&amp;#39;s take a look at turning methods we&amp;#39;ve placed in our classes into methods that test for truthiness.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  def method_missing(method_name, *args)
    if method_name.ends_with?(&amp;quot;?&amp;quot;)
      regular_method = method_name.to_s.sub(&amp;quot;?&amp;quot;, &amp;quot;&amp;quot;)

      result = self.send(regular_method, *args)

      result.present? &amp;amp;&amp;amp; result != 0
    else
      super
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, what&amp;#39;s happening here?&lt;/p&gt;
&lt;p&gt;Well, we&amp;#39;ve basically recreated an &lt;code&gt;ActiveRecord&lt;/code&gt; feature for our &lt;code&gt;User&lt;/code&gt; model. Any column that exists on a table in Rails has an identically-named method ending in a &lt;code&gt;?&lt;/code&gt; added to instances of its corresponding class. We&amp;#39;ve done something similar with our &lt;code&gt;User&lt;/code&gt; class — any undefined instance method called on a user object ending in a &lt;code&gt;?&lt;/code&gt; will be tried against a method of the same name without its question mark, and turn the result into a boolean.&lt;/p&gt;
&lt;p&gt;So, for example, &lt;code&gt;@user.purchase_totals?&lt;/code&gt; will (rather than return a decimal representing the total amount of money a user has spent in our app) simply return &lt;code&gt;true&lt;/code&gt; if the number is nonzero — otherwise, &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note the use of &lt;code&gt;present?&lt;/code&gt; here. It accounts for things like empty strings and arrays, which otherwise would return true if we&amp;#39;d used a double-bang to assess if it was truthy or not.&lt;/p&gt;
&lt;p&gt;If there&amp;#39;s no match, we default to calling &lt;a href=&quot;https://www.rubyguides.com/2018/09/ruby-super-keyword/&quot;&gt;super&lt;/a&gt;, resulting in the expected behavior (a &lt;code&gt;NoMethodError&lt;/code&gt; being thrown).&lt;/p&gt;
&lt;h2&gt;More Advanced Metaprogramming Techniques in Ruby: An Overview&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve covered some useful methods that can be used sparingly to help us out in everyday situations. But what about more advanced uses of metaprogramming? What else is it good for?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Domain-specific_language&quot;&gt;DSLs&lt;/a&gt;&lt;/strong&gt;: As mentioned, ActiveRecord, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping&quot;&gt;ORM&lt;/a&gt; (and DSL) that ships with Rails heavily leverages metaprogramming. That&amp;#39;s where all those automatic methods on our objects come from. Every column we add to a table becomes two methods on our instances — &lt;code&gt;column&lt;/code&gt; and &lt;code&gt;column=&lt;/code&gt; (and &lt;code&gt;column?&lt;/code&gt; for those who were paying attention!). It isn&amp;#39;t magic that&amp;#39;s making this happen, it&amp;#39;s metaprogramming — in fact, the use of the same &lt;code&gt;method_missing&lt;/code&gt; method we talked about earlier! Metaprogramming is also responsible for Rails&amp;#39; ability to automatically create methods like &lt;code&gt;find_by_first_name&lt;/code&gt; and &lt;code&gt;find_by(first_name: &amp;quot;name&amp;quot;)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gems&lt;/strong&gt;: If you&amp;#39;re building a gem, chances are you&amp;#39;ll want it to be flexible, adaptable, and to work with more than just one specific Rails stack. Most of the gems you regularly use have at least some degree of metaprogramming, and many use a lot to achieve the level of flexibility they offer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic APIs&lt;/strong&gt;: Metaprogramming can help us design dynamic APIs by allowing us to define methods on the fly based on runtime information, rather than hardcoding every possible method. For example, a RESTful API might expose resources with dynamic paths and attributes based on the data model of the underlying application (this may sound familiar — Rails&amp;#39; routing system does this). We can generate methods for each resource at runtime based on the database schema and dynamically respond to HTTP requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frameworks&lt;/strong&gt;: As mentioned, Rails wouldn&amp;#39;t be Rails without metaprogramming. While it&amp;#39;s a particularly magical framework, almost any framework you build will need plenty of metaprogramming. If you&amp;#39;re going to build your own (presumably far more lightweight) framework, you&amp;#39;ll need to leverage metaprogramming.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve covered some real ground here! We learned about dynamic method definition with &lt;code&gt;define_method&lt;/code&gt; and &lt;code&gt;method_missing&lt;/code&gt;, and about dynamic method invocation with &lt;code&gt;send&lt;/code&gt; and &lt;code&gt;public_send&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We also talked about metaprogramming&amp;#39;s pitfalls, including its issues with readability, maintainability, and searchability. Finally, we touched on more advanced use cases like writing gems, DSLs, and frameworks.&lt;/p&gt;
&lt;h2&gt;Further Learning&lt;/h2&gt;
&lt;p&gt;There are a lot of resources out there for taking Ruby metaprogramming further.&lt;/p&gt;
&lt;p&gt;RubyMonk offers a &lt;a href=&quot;https://rubymonk.com/learning/books/2-metaprogramming-in-ruby&quot;&gt;short but free course&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A couple of other paid courses include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.udemy.com/course/ruby-metaprogramming/&quot;&gt;Ruby Metaprogramming — Complete Course&lt;/a&gt; from Udemy&lt;/li&gt;
&lt;li&gt;Chris Oliver&amp;#39;s comprehensive &lt;a href=&quot;https://courses.gorails.com/advanced-ruby-for-rails-devs&quot;&gt;Advanced Ruby: Behind the Magic&lt;/a&gt;. In addition to digging deep into Rails, this also has a section on metaprogramming &lt;em&gt;and&lt;/em&gt; DSLs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An oft-recommended book is &lt;a href=&quot;https://www.amazon.co.uk/Metaprogramming-Ruby-Program-Like-Facets/dp/1941222129&quot;&gt;Metaprogramming Ruby 2: Facets of Ruby&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I hope you found this post useful. Thanks for reading!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Delegate Methods in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/07/19/how-to-delegate-methods-in-ruby.html"/>
    <id>https://blog.appsignal.com/2023/07/19/how-to-delegate-methods-in-ruby.html</id>
    <published>2023-07-19T00:00:00+00:00</published>
    <updated>2023-07-19T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We&#039;ll explore three ways to delegate methods in Ruby: using explicit delegation, the Forwardable module, and ActiveSupport::Delegate (for Rails).</summary>
    <content type="html">&lt;p&gt;Delegation in programming refers to handing off a task from one part of a
program to another. It&amp;#39;s an essential technique in object-oriented programming,
empowering clean, maintainable code by ensuring
that each object or method is responsible for a specific task or behavior.&lt;/p&gt;
&lt;p&gt;Understanding and using delegation is key to mastering Ruby and
other object-oriented languages. Delegation promotes separation of concerns,
making your code more modular and easier to understand, test, and refactor.&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll dive into three ways to achieve delegation in Ruby: using explicit delegation, the &lt;code&gt;Forwardable&lt;/code&gt; module, and &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; (for Rails).&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Delegation in Ruby&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s
begin by discussing explicit delegation — calling a method within a method. Then we&amp;#39;ll
explore the built-in &lt;code&gt;Forwardable&lt;/code&gt; module and how it can streamline delegation.
Lastly, we&amp;#39;ll cover &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt;, a Rails-specific delegation tool with some
handy features.&lt;/p&gt;
&lt;h3&gt;Explicit Delegation&lt;/h3&gt;
&lt;p&gt;In its simplest form, delegation can be achieved by explicitly calling a method within another method. This
approach is often employed when the delegated method belongs to a separate object or when the delegation
is simple enough not to warrant the use of more advanced techniques.&lt;/p&gt;
&lt;p&gt;Implementing explicit delegation in Ruby is straightforward. Let&amp;#39;s take an example of a &lt;code&gt;Printer&lt;/code&gt; class
that uses an HP object to print text:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class HP
  def print(text)
    # Actual code to print
  end
end

class Printer
  def initialize(printer_model)
    @printer_model = printer_model
  end

  def print(text)
    @printer_model.print(text)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, the print method of the &lt;code&gt;Printer&lt;/code&gt; class explicitly delegates the formatting task to
the &lt;code&gt;HP&lt;/code&gt; class by calling its format method. This delegation outsources the printing responsibility to another
class in a simple and readable way.&lt;/p&gt;
&lt;p&gt;Explicit delegation is common in real-world applications, as it helps promote the separation of concerns
and maintainable code. For instance, you might find this technique used in applications that require communication
between different services, where one service calls another to perform a specific task.&lt;/p&gt;
&lt;p&gt;Additionally, explicit
delegation can be useful when building adapters or wrappers around third-party libraries, as it allows you to
isolate the interaction with the external code.&lt;/p&gt;
&lt;p&gt;In summary, explicit delegation is a straightforward and effective way to delegate tasks between objects in
Ruby. By calling a method within another method, you can easily delegate responsibilities, making your code
more maintainable and promoting the separation of concerns.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Ruby&amp;#39;s Forwardable Module&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://ruby-doc.org/3.2.0/stdlibs/forwardable/Forwardable.html&quot;&gt;Forwardable module&lt;/a&gt; is a built-in Ruby library that provides a more streamlined and flexible approach to
delegate methods compared to explicit delegation. By including the Forwardable module in your class, you
gain access to methods like &lt;code&gt;def_delegator&lt;/code&gt; and &lt;code&gt;def_delegators&lt;/code&gt;, making delegation a breeze.&lt;/p&gt;
&lt;p&gt;To get started with the Forwardable module, include it in your class and use the &lt;code&gt;def_delegator&lt;/code&gt; and
&lt;code&gt;def_delegators&lt;/code&gt; methods to delegate one or multiple methods, respectively. Let&amp;#39;s revisit our Printer example with
a new &lt;code&gt;Formatter&lt;/code&gt; class and implement the Forwardable module:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;forwardable&amp;#39;

class Formatter
  def format(text)
    # Perform formatting and return the formatted text
  end
end

class Printer
  extend Forwardable

  def_delegator :@formatter, :format

  def initialize(formatter)
    @formatter = formatter
  end

  def print(text)
    formatted_text = format(text)
    puts formatted_text
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, we&amp;#39;ve used &lt;code&gt;def_delegator&lt;/code&gt; to delegate the format method from the Formatter class. If you need
to delegate multiple methods at once, you can use &lt;code&gt;def_delegators&lt;/code&gt;. For example, if the &lt;code&gt;Formatter&lt;/code&gt; class had additional
methods such as &lt;code&gt;capitalize&lt;/code&gt;, you could delegate them like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Printer
  extend Forwardable

  def_delegators :@formatter, :format, :capitalize

  # rest of code
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using the Forwardable module, there are a few important potential drawbacks to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, ensure that
the target object is initialized before using delegation. Otherwise, you may encounter errors.&lt;/li&gt;
&lt;li&gt;Second, the overuse of
delegation can lead to potential confusion in code readability, as it may become unclear which methods belong to the
class and which are delegated.&lt;/li&gt;
&lt;li&gt;Finally, there may be a slight performance overhead when using Forwardable compared
to direct method calls. However, this overhead is generally negligible in most applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more in-depth information about the Forwardable module, see the &lt;a href=&quot;https://ruby-doc.org/3.2.0/stdlibs/forwardable/Forwardable.html&quot;&gt;Forwardable documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; for Rails Applications&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://api.rubyonrails.org/classes/Module.html#method-i-delegate&quot;&gt;&lt;code&gt;ActiveSupport::Delegate&lt;/code&gt;&lt;/a&gt; is a delegation utility provided by Rails, offering a concise syntax for delegating methods
to associated objects. While it shares similarities with Ruby&amp;#39;s built-in Forwardable module, &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt;
offers additional options and features tailored for Rails applications.&lt;/p&gt;
&lt;p&gt;To use &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt;, simply call the delegate method in your class and specify the method(s) to be delegated
along with the target object. Let&amp;#39;s revisit the Printer and Formatter example and implement delegation using
&lt;code&gt;ActiveSupport::Delegate&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Formatter
  def format(text)
    # Perform formatting and return the formatted text
  end
end

class Printer
  delegate :format, to: :@formatter

  def initialize(formatter)
    @formatter = formatter
  end

  def print(text)
    formatted_text = format(text)
    puts formatted_text
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; offers additional options and features that can be useful in various scenarios. For instance,
you can use the &lt;code&gt;:prefix&lt;/code&gt; option to prepend a prefix to the delegated method. If you&amp;#39;d like to delegate multiple methods at
once, simply list them before the &lt;code&gt;to:&lt;/code&gt; option:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Printer
  delegate :format, :bold, :italic, to: :@formatter, prefix: true

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This generates methods like &lt;code&gt;formatter_format&lt;/code&gt;, &lt;code&gt;formatter_bold&lt;/code&gt;, and &lt;code&gt;formatter_italic&lt;/code&gt; in the &lt;code&gt;Printer&lt;/code&gt; class, delegating
to the respective methods in the &lt;code&gt;Formatter&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; is commonly used in real-world Rails projects to delegate methods between associated
models or objects. For example, if you have a &lt;code&gt;User&lt;/code&gt; model that &lt;code&gt;belongs_to&lt;/code&gt; an organization, you could delegate the name method
from the &lt;code&gt;organization&lt;/code&gt; model to the &lt;code&gt;User&lt;/code&gt; model like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  belongs_to :organization
  delegate :name, to: :organization, prefix: :organization
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows you to access the organization&amp;#39;s name through a user object: &lt;code&gt;user.organization_name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In conclusion, &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; is a powerful tool for Rails applications that enables concise and expressive delegation
of methods. By leveraging its additional options and features, you can create maintainable and well-organized code that adheres
to the principles of object-oriented programming in the context of Rails projects.&lt;/p&gt;
&lt;p&gt;For more in-depth information about &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; and all the available options, see the &lt;a href=&quot;http://api.rubyonrails.org/classes/Module.html#method-i-delegate&quot;&gt;Rails ActiveSupport documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Comparison of Delegation Techniques&lt;/h2&gt;
&lt;p&gt;Explicit delegation is the most straightforward approach to delegation, requiring
manually defined methods that call upon the desired methods from the delegated object.
Although this technique is simple and doesn&amp;#39;t require any external libraries, it may be
too verbose when delegating a growing number of methods.&lt;/p&gt;
&lt;p&gt;The Forwardable module, built into Ruby, offers a more streamlined way to delegate methods.
By using the &lt;code&gt;def_delegator&lt;/code&gt; and &lt;code&gt;def_delegators&lt;/code&gt; methods, you can delegate one or multiple
methods concisely.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ActiveSupport::Delegate&lt;/code&gt;, part of Rails&amp;#39; ActiveSupport library, provides a declarative
way to handle method delegation. With a clean syntax and additional options like prefixing,
it&amp;#39;s an expressive tool in a Rails context. Compared to explicit delegation,
&lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; is more expressive and less verbose, particularly when delegating
multiple methods. However, it requires Rails, so it&amp;#39;s not suitable for non-Rails Ruby
projects.&lt;/p&gt;
&lt;p&gt;Choosing a delegation technique depends on your specific needs and application. While
explicit delegation offers simplicity and clarity, the Forwardable module provides a more
streamlined approach, and &lt;code&gt;ActiveSupport::Delegate&lt;/code&gt; offers a powerful, Rails-specific
solution. Understanding these options lets you choose the technique that best fits
your project and coding style.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;From explicit delegation to using the built-in Forwardable module or the Rails-specific
&lt;code&gt;ActiveSupport::Delegate&lt;/code&gt;, each technique offers its unique benefits and potential drawbacks.
The technique you should choose depends on your specific context, the nature of your project, and
your personal coding style.&lt;/p&gt;
&lt;p&gt;Understanding these techniques not only broadens your Ruby programming skills but also
equips you to write more modular, readable, and maintainable code. With this knowledge,
you are better prepared to tackle complex problems, with eloquent design.&lt;/p&gt;
&lt;p&gt;Happy delegating!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Devise for Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/07/12/an-introduction-to-devise-for-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2023/07/12/an-introduction-to-devise-for-ruby-on-rails.html</id>
    <published>2023-07-12T00:00:00+00:00</published>
    <updated>2023-07-12T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In part one of this series on Devise, we&#039;ll cover the basics of the Devise gem.</summary>
    <content type="html">&lt;p&gt;With over 20,000 GitHub stars and lots of integrations, the Devise gem is one of the most popular gems in the Ruby landscape. So why would we term it one of Ruby&amp;#39;s &amp;quot;hidden&amp;quot; gems? Well, as popular as it is, most developers only scratch the surface of the library&amp;#39;s capabilities.&lt;/p&gt;
&lt;p&gt;In this two-part series, we&amp;#39;ll take a deep dive into Devise.&lt;/p&gt;
&lt;p&gt;In this first part, we&amp;#39;ll learn some of the basics, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What Devise is and why you should use it in the first place, including some situations where it&amp;#39;s advisable &lt;em&gt;not&lt;/em&gt; to use it.&lt;/li&gt;
&lt;li&gt;How to install Devise and use it in your project.&lt;/li&gt;
&lt;li&gt;How to customize the library for your project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In part two, we&amp;#39;ll look at more advanced usages of Devise, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OmniAuth and using Devise for API-only applications.&lt;/li&gt;
&lt;li&gt;Integrating Devise with an authorization library.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Pre-requisites&lt;/h2&gt;
&lt;p&gt;In this tutorial, we&amp;#39;ll use a simple Ruby on Rails 7 application featuring users and tasks. Users can register, log in, and log out, with access to &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt; actions for tasks depending on their assigned role.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll use this app to progressively build ever more complex features and, in the process, show off Devise&amp;#39;s powerful features in action.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s kick off with a brief introduction to Devise.&lt;/p&gt;
&lt;h2&gt;Introducing Devise for Ruby&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/heartcombo/devise&quot;&gt;Devise&lt;/a&gt; is an authentication library built on top of &lt;a href=&quot;https://github.com/wardencommunity/warden&quot;&gt;Warden&lt;/a&gt;, a Rack-based authentication framework.&lt;/p&gt;
&lt;p&gt;Warden handles user sessions using secure session strings to verify the identities of logged-in users. It also handles users who are &lt;em&gt;not&lt;/em&gt; logged in to ensure they cannot access restricted resources.&lt;/p&gt;
&lt;p&gt;But since Warden is purely Rack-based, it does not add controller actions, views, helpers, or any other configuration options necessary for building a proper user authentication solution. Devise, on the other hand, does.&lt;/p&gt;
&lt;p&gt;Another notable feature of Devise is its modularity. The library comes with around 10 modules which allow you to specify exactly how you want authentication handled in your application. You don&amp;#39;t need to use all 10 modules. Instead, you activate and use only what you need for your app. We&amp;#39;ll go over these modules later, which include the &lt;code&gt;Registerable&lt;/code&gt; module, &lt;code&gt;Omniauthable&lt;/code&gt;, &lt;code&gt;Trackable&lt;/code&gt;, and others.&lt;/p&gt;
&lt;p&gt;With that in mind, let&amp;#39;s start building our Tasks app and install Devise.&lt;/p&gt;
&lt;h2&gt;Getting Started: Installing Devise&lt;/h2&gt;
&lt;p&gt;Generate a new Rails 7 app using &lt;code&gt;bundle exec rails new tasks_app&lt;/code&gt;. Alternatively, just pull &lt;a href=&quot;https://github.com/iamaestimo/tasker_app&quot;&gt;our example app&amp;#39;s code from the repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ensure you&amp;#39;re in the app&amp;#39;s root directory, then run &lt;code&gt;bundle add devise&lt;/code&gt;. This will add the Devise gem to your app&amp;#39;s Gemfile. After this, run &lt;code&gt;bundle exec rails g devise:install&lt;/code&gt; to install Devise and generate its initializer file (we&amp;#39;ll take a closer look at this file when we go through Devise&amp;#39;s modules).&lt;/p&gt;
&lt;p&gt;When you run this command, the generator will also give you a few setup suggestions for Devise to work as expected. Just follow the given instructions, and you should be good to go.&lt;/p&gt;
&lt;p&gt;Next, let&amp;#39;s generate a user model that Devise will work with.&lt;/p&gt;
&lt;h3&gt;Generating a Devise User Model&lt;/h3&gt;
&lt;p&gt;Devise needs a user model. What you call this model is totally dependent on your preferences and your app&amp;#39;s use case, but it&amp;#39;s usually either &lt;code&gt;User&lt;/code&gt; or &lt;code&gt;Admin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Go ahead and generate a Devise user model with &lt;code&gt;bundle exec rails g devise User&lt;/code&gt;. This will generate a &lt;code&gt;User&lt;/code&gt; model under &lt;code&gt;app/models/user.rb&lt;/code&gt;, a migration file to create the user table, and a route for accessing the user resource:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/config/routes.rb

Rails.application.routes.draw do
  devise_for :users

  root &amp;quot;home#index&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;bundle exec rails db:migrate&lt;/code&gt; to run the migration and set up the user table. With that done, we now have a user model we can use for all things authentication.&lt;/p&gt;
&lt;p&gt;Next, let&amp;#39;s set up a Task model to use moving forward.&lt;/p&gt;
&lt;h3&gt;Generating a Task Model&lt;/h3&gt;
&lt;p&gt;Remember, using Devise, our simple app needs to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enable a user to register.&lt;/li&gt;
&lt;li&gt;Enable a user to sign in and out of the app.&lt;/li&gt;
&lt;li&gt;Enable a user to create a Task that will belong to them.&lt;/li&gt;
&lt;li&gt;Allow (or disallow) a user from interacting with a Task, depending on their role.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that in mind, let&amp;#39;s now generate the Task model that users will interact with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails g scaffold Task user:references title body:text status:integer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we should have a Task model associated with the user who created it, a title, a body, and a status (showing whether it&amp;#39;s done or not).&lt;/p&gt;
&lt;p&gt;After that, run &lt;code&gt;bundle exec rails db:migrate&lt;/code&gt; to create the tasks table. Also ensure that you relate a Task to a User:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/user.rb

class User &amp;lt; ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tasks, dependent: :destroy # add this line
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you run the scaffold generator, the relation &lt;code&gt;belongs_to :users&lt;/code&gt; will automatically be added to the Task model. All the same, edit the Task model to use an &lt;code&gt;enum&lt;/code&gt; for the status as shown below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/task.rb

class Task &amp;lt; ApplicationRecord
  belongs_to :user

  enum :status, { draft: 0, underway: 1, done: 2, archived: 3 }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, we are now ready to dive deeper into Devise. Let&amp;#39;s start with a brief overview of Devise&amp;#39;s modules.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Modules in Devise for Ruby&lt;/h2&gt;
&lt;p&gt;As we mentioned in the introduction, one of Devise&amp;#39;s key features is its modularity. But what does &amp;quot;modularity&amp;quot; actually mean, in this case? It simply means separating the authentication process into different parts to be managed independently.&lt;/p&gt;
&lt;p&gt;The latest version of Devise (at the time of writing) contains 10 modules:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Database authenticatable&lt;/strong&gt; - This module will take a user-supplied password and convert it into a secure hash which is then stored in the database. The module also handles verification when a user signs in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Omniauthable&lt;/strong&gt; - Enables OmniAuth authentication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lockable&lt;/strong&gt; - This module will lock an account depending on the number of failed logins. The account is made accessible again after a certain time period or via email.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trackable&lt;/strong&gt; - Tracks the number of logins an account has made, the IP addresses used, and the login timestamps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confirmable&lt;/strong&gt; - Sends an email with confirmation instructions when an account is registered and will also check if a user&amp;#39;s account is confirmed when they log in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Registerable&lt;/strong&gt; - Allows users to register an account on your app, and handles account editing and destruction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recoverable&lt;/strong&gt; - Handles password resets and account recovery.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeoutable&lt;/strong&gt; - Expires user sessions after a specified amount of time has elapsed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validatable&lt;/strong&gt; - This lets you define custom validation rules for user-supplied emails and passwords during account creation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rememberable&lt;/strong&gt; - Uses a cookie to remember a user during authentication.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For more information, &lt;a href=&quot;https://www.rubydoc.info/github/heartcombo/devise/main/Devise/Models&quot;&gt;Devise&amp;#39;s documentation on modules&lt;/a&gt; will serve you well. For now, we&amp;#39;ll switch to Devise helpers and filters.&lt;/p&gt;
&lt;h2&gt;Devise Helpers and Filters&lt;/h2&gt;
&lt;p&gt;One of the reasons to use an authentication library like Devise is to manage access to controller resources and associated views. Devise comes loaded with a set of convenient helpers, which include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user_signed_in?&lt;/code&gt; - Checks whether there is a currently logged-in user.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;current_user&lt;/code&gt; - Allows you to reference the currently logged-in user. For example, you can use a code snippet like &lt;code&gt;current_user.email&lt;/code&gt; to fetch the currently logged-in user&amp;#39;s email.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user_session&lt;/code&gt; - For the currently logged-in user&amp;#39;s session.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;destroy_user_session_path&lt;/code&gt; - Destroys a logged-in user&amp;#39;s session and redirects to a specified path or the root path.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new_user_session_path&lt;/code&gt; - Responds with a user login view.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;edit_user_registration_path&lt;/code&gt; - Gives the currently logged-in user access to a view to edit their registration details.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new_user_registration_path&lt;/code&gt; - Responds with a view that has a registration form for registering new users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;#39;s about it for the helpers. To manage controller access, Devise gives you a nifty &lt;code&gt;before_action&lt;/code&gt; filter you can use like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

class PostsController &amp;lt; ApplicationController

  before_action :authenticate_user! # if your model is called &amp;quot;User&amp;quot;
  before_action :authenticate_member! # if your model is called &amp;quot;Member&amp;quot;

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, let&amp;#39;s now shift focus to how Devise integrates with Rails&amp;#39; strong parameters.&lt;/p&gt;
&lt;h2&gt;Devise and Ruby on Rails&amp;#39; Strong Parameters&lt;/h2&gt;
&lt;p&gt;Strong parameters is a well-known Ruby on Rails feature that prevents the mass assignment of request parameters to objects. Strong parameters require that request parameters be explicitly declared before any assignment is done, usually at the controller level.&lt;/p&gt;
&lt;p&gt;Devise also mirrors this functionality by requiring parameters to be explicitly defined under appropriate Devise-specific controller actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sign_up&lt;/code&gt; - By default, this is found in the Devise controller &lt;code&gt;Devise::RegistrationsController#create&lt;/code&gt;. The keys allowed by default are &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;, and &lt;code&gt;password_confirmation&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sign_in&lt;/code&gt; - This is found in the controller &lt;code&gt;Devise::SessionsController#new&lt;/code&gt;, with the default allowed authentication keys &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;account_update&lt;/code&gt; - Found in &lt;code&gt;Devise::Registrations#update&lt;/code&gt; — the authentication keys &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;current_password&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;, and &lt;code&gt;password_confirmation&lt;/code&gt; are allowed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most convenient way to add your own authentication keys is to use a &lt;code&gt;before_action&lt;/code&gt; filter in &lt;code&gt;ApplicationController&lt;/code&gt;, like here, where we add a &lt;code&gt;username&lt;/code&gt; key that is required when a user signs up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/application_controller.rb

class ApplicationController &amp;lt; ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An interesting use case for Devise&amp;#39;s strong parameters is when you need to pass an array of keys to the Devise sanitizer. For example, let&amp;#39;s say we have a freelancer marketplace app where a user can be a &amp;quot;contractor&amp;quot; for hire, or an &amp;quot;employer&amp;quot; who can hire other contractors, or a contractor and employer at the same time.&lt;/p&gt;
&lt;p&gt;Without going into too many technical details, we could achieve this functionality by allowing users to update their account using two select boxes for the respective roles — &amp;quot;contractor&amp;quot; and &amp;quot;employer&amp;quot; — so that a user can choose either or both options.&lt;/p&gt;
&lt;p&gt;A quick way of achieving this is to use an array with the roles as keys, but Rails&amp;#39; strong parameters only allow for the following scalar values: &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Symbol&lt;/code&gt;, &lt;code&gt;NilClass&lt;/code&gt;, &lt;code&gt;Numeric&lt;/code&gt;, &lt;code&gt;TrueClass&lt;/code&gt;, &lt;code&gt;FalseClass&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Time&lt;/code&gt;, &lt;code&gt;DateTime&lt;/code&gt;, &lt;code&gt;StringIO&lt;/code&gt;, &lt;code&gt;IO&lt;/code&gt;, &lt;code&gt;ActionDispatch::Http::UploadedFile&lt;/code&gt;, and &lt;code&gt;Rack::Test::UploadedFile&lt;/code&gt;. As you can see, arrays, hashes, and other objects are, by default, not permitted.&lt;/p&gt;
&lt;p&gt;To use an array that permits multiple roles for our user, we can modify our code as shown below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/application_controller.rb

class ApplicationController &amp;lt; ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:account_update) do |user_params|
      user_params.permit({ roles: [] },
      :email, :password, :password_confirmation, :current_password)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/heartcombo/devise#controller-filters-and-helpers&quot;&gt;Devise documentation on helpers&lt;/a&gt; covers the topic in more detail.&lt;/p&gt;
&lt;p&gt;Next, we&amp;#39;ll learn how to customize Devise&amp;#39;s views and controllers.&lt;/p&gt;
&lt;h2&gt;Customizing Devise Views&lt;/h2&gt;
&lt;p&gt;Because Devise is built as a Rails engine, almost all of its components are pre-packaged. Good examples of this include the views for logging in, signing up, updating account details, resetting your password, etc. At some point in your journey to build an app, you may need to customize these built-in views to suit your needs.&lt;/p&gt;
&lt;p&gt;The first step is to generate views using the command &lt;code&gt;bundle exec rails g devise:views&lt;/code&gt;. When you do that, the respective views are generated and placed in the &lt;code&gt;app/views/devise&lt;/code&gt; folder:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-07/tree-structure-devise-folder.png&quot; alt=&quot;Devise folder structure&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s use a practical example of customizing Devise&amp;#39;s views. In the previous section, we learned how to add extra authentication keys to the Devise sanitizer. Let&amp;#39;s now extend that example to add a &lt;code&gt;username&lt;/code&gt; field to the user registration view.&lt;/p&gt;
&lt;p&gt;Since the &lt;code&gt;username&lt;/code&gt; field is not available to our default user model, we&amp;#39;ll add it using a &lt;code&gt;bundle exec rails g migration add_column_username_to_user username&lt;/code&gt; migration. Run the command &lt;code&gt;bundle exec rails db:migrate&lt;/code&gt; to start the migration and add the column to the user table.&lt;/p&gt;
&lt;p&gt;Next, open up the new user registration file and edit it as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/devise/registrations/new.html.erb

&amp;lt;h2&amp;gt;Sign up&amp;lt;/h2&amp;gt;

&amp;lt;%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %&amp;gt;
  ...

  &amp;lt;!-- username field added --&amp;gt;

  &amp;lt;div class=&amp;quot;field&amp;quot;&amp;gt;
    &amp;lt;%= f.label :username %&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;%= f.text_field :username, autofocus: true %&amp;gt;
  &amp;lt;/div&amp;gt;
  ...
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generating Devise views this way is fine if you&amp;#39;re working with all views. But say you only want to customize a couple of views. How can you do this? Well, you simply pass the generator command a list of the views you want by indicating the view flag &lt;code&gt;bundle exec rails g devise:views -v sessions registrations&lt;/code&gt;. In this case, this only generates views associated with the login/logout process and those that handle sign-ups.&lt;/p&gt;
&lt;p&gt;Moving on, let&amp;#39;s dig further into Devise customization by learning how to customize the library&amp;#39;s controllers and routes.&lt;/p&gt;
&lt;h2&gt;Customizing Devise Controllers and Routes&lt;/h2&gt;
&lt;p&gt;Customizing Devise&amp;#39;s views can only get you so far. If you want real customization, you need to go into the library&amp;#39;s controllers and routes.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say we want to send an email to our admin notifying them of every new user registration on the app. (This is not the ideal way to go about things, but let&amp;#39;s go with this example to illustrate what we want).&lt;/p&gt;
&lt;p&gt;To begin with, we need to modify the signup action of the &lt;code&gt;Devise::RegistrationsController&lt;/code&gt;. Normally, Devise&amp;#39;s controllers are not directly accessible for editing, so, just like we did with the views, we can generate them like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails generate devise:controllers users
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last bit of that command is the scope - in this case, &lt;code&gt;users&lt;/code&gt;. If you want it to be something else, you can change it accordingly. When you are done, the generated controllers should be in a structure similar to this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-07/devise-controller-folder-structure.png&quot; alt=&quot;Devise controllers folder structure&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Next, open up the routes file and modify Devise&amp;#39;s routes to reflect the changes in the controller structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/routes.rb

Rails.application.routes.draw do
  resources :tasks

  devise_for :users, controllers: {
        sessions: &amp;#39;users/sessions&amp;#39;,
        registrations: &amp;#39;users/registrations&amp;#39;
      }

  root &amp;quot;tasks#index&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now open up the newly generated &lt;code&gt;Users::RegistrationsController&lt;/code&gt; and modify it to send an email to the admin whenever a new user signs up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/users/registrations_controller.rb
class Users::RegistrationsController &amp;lt; Devise::RegistrationsController
  ...

  def create
    super do |resource|
    # use a custom mailer to send an email to the admin
    AdminNotifierMailer.user_signup_notification(resource).deliver_later
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, with access to Devise&amp;#39;s controllers and actions, you can modify your app&amp;#39;s authentication flow in any way you want.&lt;/p&gt;
&lt;h2&gt;Coming Up Next: Advanced Use of Devise&lt;/h2&gt;
&lt;p&gt;In the first part of this two-part series, we covered the basics of the Devise gem. We learned about the Devise&amp;#39;s different modules, as well as how to customize its views and controllers.&lt;/p&gt;
&lt;p&gt;In part two, we&amp;#39;ll dive into more advanced usages of Devise, including API authentication and how to use OmniAuth with Devise.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Keep Your Ruby App Secure with Bundler</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/06/28/keep-your-ruby-app-secure-with-bundler.html"/>
    <id>https://blog.appsignal.com/2023/06/28/keep-your-ruby-app-secure-with-bundler.html</id>
    <published>2023-06-28T00:00:00+00:00</published>
    <updated>2023-06-28T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover how you can use Bundler-audit and Bundler outdated to help keep your Ruby apps secure.</summary>
    <content type="html">&lt;p&gt;This article covers the use of &lt;code&gt;bundler&lt;/code&gt; features to secure Ruby applications. In this day and age, we have to be
more and more careful about software supply chain security.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll show you how to start this journey by
relying on a Gemfile and &lt;code&gt;bundler&lt;/code&gt; to manage your project&amp;#39;s dependencies.&lt;/p&gt;
&lt;p&gt;By the end of the post, you will better understand how &lt;code&gt;bundler audit&lt;/code&gt; and &lt;code&gt;bundler outdated&lt;/code&gt; work. Both can
help you monitor the security state of your
project&amp;#39;s dependency tree.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive in!&lt;/p&gt;
&lt;h2&gt;An Introduction to Bundler for Ruby&lt;/h2&gt;
&lt;p&gt;The history of &lt;a href=&quot;https://bundler.io/&quot;&gt;Bundler&lt;/a&gt; is linked to &lt;a href=&quot;https://rubygems.org/&quot;&gt;RubyGems&lt;/a&gt;. RubyGems, first released in 2004 by Chad Fowler, is a package manager that makes it possible to distribute and manage Ruby libraries, applications, and their dependencies.&lt;/p&gt;
&lt;p&gt;Bundler, on the other hand, is a dependency manager. It was first released in 2009 by Carl Lerche.&lt;/p&gt;
&lt;p&gt;Bundler helps developers manage dependencies between different Ruby libraries and applications. It uses a Gemfile to define
the libraries and versions that a project depends on, then installs and loads those libraries in the correct order.
Bundler provides a simple way to manage dependencies and ensure that all the required libraries are installed and
loaded correctly.&lt;/p&gt;
&lt;h2&gt;Supply Chain Security&lt;/h2&gt;
&lt;p&gt;RubyGems and Bundler can only solve some security issues. For example, in 2013, 2018, and 2020, several security issues emerged directly related
to RubyGems. Each time, gems were compromised, potentially exposing thousands of projects and products to malicious
actors.&lt;/p&gt;
&lt;p&gt;Nowadays, the security of the software supply chain has become
more high profile, due to major incidents like 2020&amp;#39;s SolarWinds hack, EventStream in 2018, and Equifax, Maersk, Merck, and FedEx in 2017.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see how Bundler can help us avoid some security issues.&lt;/p&gt;
&lt;h2&gt;Bundler&amp;#39;s Security-Related Features for Ruby&lt;/h2&gt;
&lt;p&gt;Bundler comes with several features that allow us to secure a Ruby project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Source validation:&lt;/strong&gt; ensures gems are installed from a specific, trusted source.&lt;/li&gt;
&lt;li&gt;With &lt;strong&gt;a dependency graph&lt;/strong&gt;, we can see a whole list of dependencies for each gem, helping us to identify
potential security risks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A &lt;code&gt;Gemfile.lock&lt;/code&gt; file&lt;/strong&gt; records the specific versions of each gem used in the project. As it&amp;#39;s packaged with the
project, it ensures that the same versions of gems are used across different environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vulnerability detection:&lt;/strong&gt; Thanks to an integration with the Ruby Advisory Database (RDB), Bundler can detect known security
vulnerabilities in gems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An audit command&lt;/strong&gt; lets you list known security vulnerabilities related to gems a project depends on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Checksum verification:&lt;/strong&gt; Bundler verifies the checksum of each gem before installing it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gem signing and signature verification:&lt;/strong&gt; Gems can be signed cryptographically by a developer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But, alas, signing gems is complicated and requires a lot of steps. As a consequence, not
all gems are signed, thus putting many projects at risk.&lt;/p&gt;
&lt;p&gt;Yet, in the meantime, we can rely on two features in particular to ensure that a project doesn&amp;#39;t rely on
packages that are too old or that have been identified as carrying a security risk.&lt;/p&gt;
&lt;p&gt;These features are &lt;code&gt;bundler audit&lt;/code&gt; and &lt;code&gt;bundler outdated&lt;/code&gt;. Let&amp;#39;s look at &lt;code&gt;bundler audit&lt;/code&gt; first.&lt;/p&gt;
&lt;h2&gt;About Bundler-audit&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;bundle audit&lt;/code&gt; is a command-line tool that helps you identify security vulnerabilities in the Ruby libraries that your
project depends on. It is a plugin for the Ruby dependency manager Bundler and is included with Bundler version 1.10 and
above.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bundle audit&lt;/code&gt; works by analyzing your Gemfile.lock file, which lists all the dependencies for your project and their
versions. It then compares this information with a database of known vulnerabilities in Ruby gems (maintained by Rubysec).&lt;/p&gt;
&lt;p&gt;If a vulnerability is found, &lt;code&gt;bundle audit&lt;/code&gt; will provide you with information about the vulnerability, including its
severity level and which gem versions have been affected. It will also suggest actions you can take to address the
vulnerability, such as upgrading to a patched version of the gem or removing it entirely.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bundle audit&lt;/code&gt; is a useful tool for ensuring the security of your Ruby projects, especially if you are using third-party
libraries or dependencies. By regularly running &lt;code&gt;bundle audit&lt;/code&gt; as part of your development workflow, you can stay
up-to-date on any vulnerabilities that may affect your project and take proactive steps to address them.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Usage of Bundler-audit in Ruby&lt;/h3&gt;
&lt;p&gt;The basic use of Bundler-audit is through the &lt;code&gt;bundle audit&lt;/code&gt; command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$&amp;gt; bundle-audit

Name: actionpack
Version: 3.2.10
Advisory: OSVDB-91452
Criticality: Medium
URL: http://www.osvdb.org/show/osvdb/91452
Title: XSS vulnerability in sanitize_css in Action Pack
Solution: upgrade to ~&amp;gt; 2.3.18, ~&amp;gt; 3.1.12, &amp;gt;= 3.2.13

$&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, it outputs the name and version of a package used by the project we are testing, which contains a
medium-level security issue. It also outputs the URL of the security advisory and a possible solution.&lt;/p&gt;
&lt;h3&gt;Keeping Bundler-audit&amp;#39;s Database Up to Date&lt;/h3&gt;
&lt;p&gt;As the audit is basically comparing a list of gems used in the project with a list of known security issues in a
local copy of the security advisory database, you need to keep that database up to date.&lt;/p&gt;
&lt;p&gt;To do so, you just need to run the &lt;code&gt;bundle audit update&lt;/code&gt; command. This will fetch the latest version of the database.
You should only have to run the audit again to check for any new security risks.&lt;/p&gt;
&lt;h3&gt;Integration In a CI Pipeline&lt;/h3&gt;
&lt;p&gt;As with other tools aimed at keeping your project safe, it&amp;#39;s best to ensure an audit runs automatically on a regular
basis (if not after every commit and push to the Git repository).&lt;/p&gt;
&lt;p&gt;You can rely on a git post commit hook to run the audit locally if the &lt;code&gt;Gemfile.lock&lt;/code&gt; changes. Here is an
example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# .git/hooks/post-commit
if git diff --name-only HEAD~1 | grep -q Gemfile.lock; then
    echo &amp;quot;Gemfile.lock has changed, running audit...&amp;quot;
    bundle audit update &amp;amp;&amp;amp; bundle audit
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a bit radical, but it works. You can rely on the same approach within the CI pipeline. As the &lt;code&gt;bundle audit&lt;/code&gt;
command will return a non-zero exit status, the CI pipeline will consider it to have failed. Unfortunately, we
don&amp;#39;t have a proper audit report out of the box. Instead, you have to direct the output of the &lt;code&gt;bundle audit&lt;/code&gt;
command towards a file and save it as an artifact of the build for the CI pipeline.&lt;/p&gt;
&lt;p&gt;Here is how to direct the command&amp;#39;s output to a file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$&amp;gt; bundle audit update &amp;amp;&amp;amp; bundle audit &amp;gt; /tmp/bundle-audit-report.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As each CI service handles artifacts differently, check the relevant documentation to see how
to do this.&lt;/p&gt;
&lt;h3&gt;Bundler-audit in Summary&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;bundle audit&lt;/code&gt; is not a complete solution, yet it still lets your team know if your project relies on
unsafe gems (without costing you an arm and a leg). Your team should put tools such as git hooks or
CI tasks in place, and relevant integrations to make any issues visible.&lt;/p&gt;
&lt;p&gt;All things considered, I&amp;#39;d say it&amp;#39;s not too much
trouble. It probably requires a few hours to get started and have the basic
information spit out as a comment in your favorite git hosting solution, or as a notice in a Slack channel.&lt;/p&gt;
&lt;p&gt;Yet &lt;code&gt;bundle audit&lt;/code&gt; is only one part of what you can do. It only tells you if there is a security issue related to a gem
relied on by a project. It does not tell you if a gem is outdated. To handle that, &lt;code&gt;bundler outdated&lt;/code&gt; is the command to
use.&lt;/p&gt;
&lt;h2&gt;Bundler Outdated for Ruby Gems&lt;/h2&gt;
&lt;p&gt;Auditing for security risks is very important, yet listing outdated gems is also a big issue. The longer you wait to
update a dependency, the higher the risk that your code breaks.&lt;/p&gt;
&lt;p&gt;You should aim to work in small iterations, deployed frequently, to limit the amount of change contained in each
deployment. Equally, you should aim to update any gem you use as soon as it&amp;#39;s updated, or as close to that as possible.&lt;/p&gt;
&lt;h3&gt;Usage of Bundler Outdated&lt;/h3&gt;
&lt;p&gt;Just like &lt;code&gt;bundler audit&lt;/code&gt;, &lt;code&gt;bundler outdated&lt;/code&gt; is very simple to use. Just call it at the root of your Ruby project. It
will compare the &lt;code&gt;Gemfile.lock&lt;/code&gt; content to the current gem releases listed in it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$&amp;gt; bundle outdated
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies......

Gem           Current    Latest    Requested   Groups
addressable    2.8.1      2.8.4
capybara       3.38.0     3.39.0    &amp;gt;= 0        test
devise         4.9.0      4.9.2     &amp;gt;= 4.6.0    default

$&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, three gems are outdated in this project. Let&amp;#39;s see how we should read this table:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first column contains the name of the gem.&lt;/li&gt;
&lt;li&gt;The second column shows the currently installed version.&lt;/li&gt;
&lt;li&gt;The third column shows the latest version available.&lt;/li&gt;
&lt;li&gt;The fourth column shows the request version (from the Gemfile).&lt;/li&gt;
&lt;li&gt;The fifth column shows the group the gem is part of (in the Gemfile).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here, we can see we don&amp;#39;t have much to worry about for &lt;code&gt;addressable&lt;/code&gt; and &lt;code&gt;devise&lt;/code&gt;, as the difference in releases is
within the patch versions (third part of the release number). The version of &lt;code&gt;capybara&lt;/code&gt; is only behind one minor release, which
implies a few more changes, but this rarely means breaking changes.&lt;/p&gt;
&lt;p&gt;Still, the logical thing to do here is to update all three as soon as possible.&lt;/p&gt;
&lt;h3&gt;Usage In a CI Pipeline&lt;/h3&gt;
&lt;p&gt;As &lt;code&gt;bundle outdated&lt;/code&gt; will return a non-zero exit status for outdated gems, a CI task it&amp;#39;s based on is
considered failed. So you can have a simple and direct flag in your CI pipeline in case of outdated
dependencies.&lt;/p&gt;
&lt;p&gt;Yet you might want to rely on a pair of flags to improve your use of &lt;code&gt;bundle outdated&lt;/code&gt;. The command can filter its
output to only list gems that have pending patch-level updates:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$&amp;gt; bundle outdated --filter-patch
...
Gem           Current    Latest    Requested   Groups
addressable    2.8.1      2.8.4
devise         4.9.0      4.9.2     &amp;gt;= 4.6.0    default
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the same manner, you can list only gems with pending minor version updates:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$&amp;gt; bundle outdated --filter-minor
Gem           Current    Latest    Requested   Groups
capybara       3.38.0     3.39.0    &amp;gt;= 0        test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in the same way, you can list only gems with pending major release updates:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$&amp;gt; bundle outdated --filter-major
Gem                Current    Latest    Requested   Groups
sidekiq-scheduler   4.0.3      5.0.1      &amp;gt;= 0       default
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Those three flags let you easily tailor a CI task to only warn you that a patch-level update is available but not
block the build (or the reverse), for example.&lt;/p&gt;
&lt;p&gt;A complimentary option allows you to only list outdated gems within the default group:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$&amp;gt; bundle outdated --group default
...
Gem                Current  Latest  Requested          Groups
devise             4.9.0    4.9.2   &amp;gt;= 4.6.0           default
faker              3.1.1    3.2.0   &amp;gt;= 0               default
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As gems within test and development groups don&amp;#39;t impact the production release, they could be considered less of a
priority to keep up to date, for example. Thus you can use the flag only to raise a warning or block the CI pipeline if
gems within the default group are outdated.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Maintaining the security of your Ruby application is not just the result of one action. Rather, it&amp;#39;s a cumulative effect of several actions
put together. As we&amp;#39;ve covered in this post, Bundler provides us with two commands that can help you to improve your
application&amp;#39;s security:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bundle audit&lt;/code&gt; quickly lets you know if any gem you add or use in your project could pose a security threat and
what to do about it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bundle outdated&lt;/code&gt; allows you to flag gems that should be updated. It complements &lt;code&gt;bundle audit&lt;/code&gt;, and if you keep on top of it, you avoid having an update bundle that&amp;#39;s too large.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two commands are easy to use and integrate with local feedback loops and CI pipelines.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Lambdas in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/06/21/an-introduction-to-lambdas-in-ruby.html"/>
    <id>https://blog.appsignal.com/2023/06/21/an-introduction-to-lambdas-in-ruby.html</id>
    <published>2023-06-21T00:00:00+00:00</published>
    <updated>2023-06-21T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Lambdas can make your code easier to use and more powerful. Discover how to use Lambda functions in Ruby.</summary>
    <content type="html">&lt;p&gt;Lambdas are a powerful programming construct used in many languages. These functions allow developers to write code that is more concise, efficient, and easier to maintain, making Lambdas an essential part of any developer&amp;#39;s toolkit.&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll explore how you can use Lambda functions in Ruby to simplify your code and make it more powerful.&lt;/p&gt;
&lt;h2&gt;What Is a Lambda Function?&lt;/h2&gt;
&lt;p&gt;Lambdas are expressions whose value is a function. For this reason, they are also called anonymous functions. The really nice part about this is that we can reference them using a variable in our code, just like a string, integer, or any other object.&lt;/p&gt;
&lt;p&gt;Everything in Ruby is an object, and Lambdas are no exception. Specifically, a Lambda is an instance of the &lt;a href=&quot;https://ruby-doc.org/core-2.7.1/Proc.html&quot;&gt;Ruby Proc class&lt;/a&gt;. They behave the same way as a Proc, except that a Lambda validates the number of parameters. Its use case is targeted towards specific functions, albeit those without a proper name. In fact, the Ruby Proc class has a &lt;code&gt;lambda?&lt;/code&gt; method which returns true if argument handling is rigid (as is the case for a Proc generated by a Lambda).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Side-note&lt;/em&gt;: Because of its popularity, it is also important to clarify that AWS Lambda is a different concept to a Lambda in Ruby. AWS Lambda is a cloud-computing service that implements functions without managing the underlying server infrastructure. You can simply write the function in any number of programming languages, including Ruby.&lt;/p&gt;
&lt;h2&gt;How to Define and Invoke a Lambda Function in Ruby&lt;/h2&gt;
&lt;p&gt;The canonical form of a Lambda expression is as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;a_lambda = lambda { puts &amp;quot;Hello world!&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More commonly in Ruby, you will see the &lt;code&gt;-&amp;gt;&lt;/code&gt; shorthand notation used to define a Lambda expression.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;a_lambda = -&amp;gt; { puts &amp;quot;Hello world!&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you simply &lt;code&gt;puts a_lambda&lt;/code&gt;, here is what you get.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;puts a_lambda
=&amp;gt; #&amp;lt;Proc:0x0000000108f77bb0 10.rb:6 (lambda)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lambdas can use a block definition notation as well.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;a_lambda = lambda do
  puts &amp;quot;Hello world!&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Lambda is invoked as shown below, and the output is as expected:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;a_lambda.call
=&amp;gt; Hello world!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The mechanism to invoke a Lambda is the same as it is with a Proc. However, it is different from the way in which traditional functions are called. Note that, if you simply reference &lt;code&gt;a_lambda&lt;/code&gt; in your code, it will not invoke the function. This is equivalent to referencing a variable but doing nothing with it.&lt;/p&gt;
&lt;p&gt;Lambdas can also accept parameters. The following Lambda returns the square of a number:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;a_lambda_with_params = lambda {|val| val**2 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The call method can take parameters to match your Lambda signature:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;three_squared = a_lambda_with_params.call(3)
puts &amp;quot;Three squared is #{three_squared}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This outputs the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;Three squared is 9
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Ruby offers other syntax options for invoking a Lambda. All of the following statements are equivalent:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;val1 = a_lambda_with_params.call(3)
val2 = a_lambda_with_params.(3)
val3 = a_lambda_with_params[3]
val4 = a_lambda_with_params===3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last few notation alternatives seem quite obtuse, but they work nonetheless.&lt;/p&gt;
&lt;p&gt;Now consider that we want to calculate the square of a set of numbers. We might typically write Ruby code such as this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;numbers = [1, 2, 3, 4, 5]
squares = numbers.map { |n| n**2 }
puts squares.join(&amp;quot;, &amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this code, what is the expression passed to the map function? It is a block that accepts a single parameter. Thus, it is equivalent to an anonymous function or Lambda. We can rewrite our code as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;numbers = [1, 2, 3, 4, 5]
square_lambda = lambda { |n| n**2 }
squares = numbers.map &amp;amp;square_lambda
puts squares.join(&amp;quot;, &amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This outputs the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;1, 4, 9, 16, 25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the ampersand that precedes the Lambda expression passed to &lt;code&gt;map&lt;/code&gt;. Why is this needed? Well, the ampersand tells Ruby that this is the special block parameter. If we omit this, Ruby thinks that &lt;code&gt;square_lambda&lt;/code&gt; is a &amp;#39;regular&amp;#39; parameter. Using that approach, we would get the following error.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;in `map&amp;#39;: wrong number of arguments (given 1, expected 0) (ArgumentError)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One of the main benefits of a Lambda is the convenience and ease of using the function only once. We could also define a traditional method here, but if this is the only place it would be used, a Lambda function provides a cleaner solution for such a &amp;#39;disposable&amp;#39; function.&lt;/p&gt;
&lt;p&gt;Another primary benefit of Lambdas is that they provide a portable mechanism to execute code anywhere in your program. This supports some interesting use cases that we&amp;#39;ll explore next.&lt;/p&gt;
&lt;h2&gt;Lambda Use Cases in Ruby&lt;/h2&gt;
&lt;p&gt;There are a number of use cases that work well as Lambda functions in Ruby.&lt;/p&gt;
&lt;h3&gt;First Class Functions&lt;/h3&gt;
&lt;p&gt;Lambda functions can be passed to other functions for use. This concept is referred to as a first-class function. Consider a use case where you need to execute code at a later point in time, once a certain condition is met.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Example: Use a lambda to defer execution of code until later
def do_something_later(&amp;amp;a_lambda)
  @deferred_action = a_lambda
end

# Later, when the condition is met, call the deferred action
if some_condition_is_met
  @deferred_action.call
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Callbacks&lt;/h3&gt;
&lt;p&gt;Callbacks are another related use case for which Lambdas are useful. Lambdas can be used to implement logic invoked at certain points in the application lifecycle, or a specific object’s lifecycle.&lt;/p&gt;
&lt;p&gt;A good example of this pattern is found in &lt;a href=&quot;https://github.com/mongodb/mongoid&quot;&gt;MongoDB&amp;#39;s Mongoid framework&lt;/a&gt;. You can set a &lt;code&gt;:default&lt;/code&gt; at the field level, and Lambdas allow you to defer execution until runtime.&lt;/p&gt;
&lt;p&gt;Consider a document with a last modified timestamp. The first implementation runs when the class is loaded. However, execution of the Lambda is deferred until the document is created — the outcome you likely want in your application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Static implementation at class load time
field :last_modified, type: Time, default: Time.now

# The implementation below defers the lambda execution until document creation time
field :last_modified, type: Time, default: -&amp;gt;{ Time.now }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The Visitor Pattern&lt;/h3&gt;
&lt;p&gt;The visitor pattern in Ruby is another use case that can take advantage of the flexibility of Lambdas. If the visitor logic is not known a priori or otherwise fits the Lambda use cases, visitors can be implemented as Lambda functions.&lt;/p&gt;
&lt;p&gt;Consider the following code snippet for a visitor to a hierarchy of nodes representing mathematical expressions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class PrintVisitor &amp;lt; Visitor
  NODE_TYPES = {
    IntegerNode =&amp;gt; -&amp;gt;(node) { puts &amp;quot;IntegerNode: #{node.value}&amp;quot; },
    FloatNode =&amp;gt; -&amp;gt;(node) { puts &amp;quot;FloatNode: #{node.value}&amp;quot; },
    ExpressionNode =&amp;gt; -&amp;gt;(node) {
      puts &amp;quot;ExpressionNode:&amp;quot;
      node.left.accept(self)
      node.right.accept(self)
    }
  }
  def visit(node)
    handler = NODE_TYPES[node.class]
    handler.call(node)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implementation uses Lambda functions to avoid defining separate methods for each element subclass. Instead, the &lt;code&gt;Visitor&lt;/code&gt; class defines a single visit method that can handle any element subclass by using a Lambda function to call the appropriate method.&lt;/p&gt;
&lt;p&gt;This may be a bit over-engineered, but if you consider cases where the visitor logic is not predefined, Lambdas become much more useful.&lt;/p&gt;
&lt;h3&gt;Functional Programming Paradigms&lt;/h3&gt;
&lt;p&gt;Lambdas in Ruby enable developers to adopt a functional programming paradigm by creating anonymous functions that can be used as arguments in other functions. This approach allows for creating complex functionality by combining smaller, reusable functions. The use of Lambdas in Ruby code results in concise, readable code. They also leverage key functional programming concepts, such as closures and higher-order functions.&lt;/p&gt;
&lt;p&gt;Lambdas can support functional programming features such as currying if you want to take them to that level. Currying permits functions to be partially applied and reused in diverse contexts.&lt;/p&gt;
&lt;h3&gt;Metaprogramming&lt;/h3&gt;
&lt;p&gt;Lambdas are used in metaprogramming to generate code dynamically. Consider the following code that adds a method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Example: Use a lambda for dynamic code generation
def create_method(name, block)
  define_method(name, &amp;amp;block)
end

a_lambda = -&amp;gt; { puts &amp;quot;Hello, world!&amp;quot; }
create_method(:hello, a_lambda)

hello   # Output: &amp;quot;Hello, world!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Lambdas as Closures&lt;/h3&gt;
&lt;p&gt;A powerful capability of Lambdas is their ability to create a closure. This encapsulates the function as well as the runtime environment, including variables from the surrounding scope. Variables are closed (or bound) to their values in a Lambda. This is why it is called a closed expression or closure. The execution context is stored in an instance of a Ruby Binding object.&lt;/p&gt;
&lt;p&gt;Consider the following example that creates a Lambda which implements an incremental counter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Example: Use a lambda as a closure to capture runtime variables
def create_counter(start)
  lambda { start += 1 }
end

counter = create_counter(0)
puts counter.call   # Output: 1
puts counter.call   # Output: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Impact of Lambdas on Performance&lt;/h2&gt;
&lt;p&gt;Performance can be a consideration if you have extremely tight operational requirements. In a tight loop, for example, you will see that Lambdas perform a bit slower than other Ruby code. It is easy to understand why. A Proc object is created to implement a Lambda (as opposed to the runtime engine delegating control to a method).&lt;/p&gt;
&lt;p&gt;In the vast majority of use cases, you will likely not notice much difference in performance (considering that computers can execute thousands of instructions in the time it takes to perform a single database query). Nonetheless, this is something to keep in mind.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we explored some of the fundamentals of Lambdas and Lambda use cases for Ruby.&lt;/p&gt;
&lt;p&gt;Lambdas provide a powerful fundamental programming construct for Ruby developers. They can be convenient one-time use functions, implementation mechanisms for callbacks, and used for several other use cases. Lambdas can also come in handy on projects that adhere to functional programming paradigms.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Setting Up Business Logic with DCI in Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/06/14/setting-up-business-logic-with-dci-in-rails.html"/>
    <id>https://blog.appsignal.com/2023/06/14/setting-up-business-logic-with-dci-in-rails.html</id>
    <published>2023-06-14T00:00:00+00:00</published>
    <updated>2023-06-14T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In part two of this series, we&#039;ll organize business logic in your Rails application using DCI.</summary>
    <content type="html">&lt;p&gt;In our last post, we examined the most common ways to organize business logic in Ruby on Rails. They all have advantages and drawbacks, and essentially, most do not leverage the full power of Object Oriented Programming in Ruby.&lt;/p&gt;
&lt;p&gt;This time, we will introduce another alternative that more naturally fits the mental models we apply when reasoning about the behavior of our applications: DCI.&lt;/p&gt;
&lt;h2&gt;Enter DCI (Data, Context, Interaction) for Rails&lt;/h2&gt;
&lt;p&gt;DCI is an often overlooked paradigm that can circumvent a lot of the issues outlined in the last post, while still adhering to MVC.&lt;/p&gt;
&lt;p&gt;More importantly, though, it&amp;#39;s a code architecture style that simultaneously lets us consider an application as a whole and avoids introducing objects with unclear responsibilities.&lt;/p&gt;
&lt;p&gt;Without further ado, here&amp;#39;s the &lt;a href=&quot;https://en.wikipedia.org/wiki/Data,_context_and_interaction&quot;&gt;definition of DCI from Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The paradigm separates the &lt;strong&gt;domain model&lt;/strong&gt; (data) from &lt;strong&gt;use cases&lt;/strong&gt; (context) and &lt;strong&gt;roles&lt;/strong&gt; that objects play (interaction). DCI is &lt;strong&gt;complementary&lt;/strong&gt; to model–view–controller (MVC). MVC as a pattern language is still used to separate the data and its processing from presentation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One of the main objectives of DCI is to improve a developer&amp;#39;s understanding of &lt;em&gt;system-level state and behavior&lt;/em&gt; by putting their code representation into different modules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;slowly changing domain knowledge (&lt;em&gt;what a system is&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;rapidly changing system behavior (&lt;em&gt;what a system does&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Moreover, it tries to simplify the mental models of an application by grouping them into &lt;em&gt;use cases&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s break this down into its individual parts: data, context, and interaction.&lt;/p&gt;
&lt;h3&gt;Data&lt;/h3&gt;
&lt;p&gt;In DCI terms, &lt;em&gt;Data&lt;/em&gt; encompasses the static, descriptive parts of what we call the &lt;em&gt;Model&lt;/em&gt; in MVC. It &lt;strong&gt;explicitly lacks functionality&lt;/strong&gt; that involves any interaction with other objects. In other words, it&amp;#39;s devoid of &lt;em&gt;business logic&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class BankAccount
  attr_reader :balance

  def increase(by)
    @balance += by
  end

  def decrease(by)
    @balance -= by
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consider the simple &lt;code&gt;BankAccount&lt;/code&gt; class above: it allows you to query the account balance, and increase or decrease it. But there is no such concept as a &lt;em&gt;transfer&lt;/em&gt; to another account.&lt;/p&gt;
&lt;p&gt;&amp;#39;Wait a second!&amp;#39; I hear you say! Isn&amp;#39;t that a description of an &lt;a href=&quot;https://www.martinfowler.com/bliki/AnemicDomainModel.html&quot;&gt;&lt;em&gt;anemic model&lt;/em&gt;&lt;/a&gt;? And isn&amp;#39;t that the most horrific anti-pattern of all time 👻? Bear with me for a moment.&lt;/p&gt;
&lt;h3&gt;Context&lt;/h3&gt;
&lt;p&gt;The point of DCI is not to strip models of any domain logic, but to dynamically attach it when it&amp;#39;s needed, and tear it down afterward. &lt;em&gt;Context&lt;/em&gt; realizes this.&lt;/p&gt;
&lt;p&gt;A context is responsible for identifying a &lt;em&gt;use case&lt;/em&gt; and mapping data objects onto the &lt;em&gt;roles&lt;/em&gt; that those play.&lt;/p&gt;
&lt;p&gt;The nice thing, by the way, is that they align nicely with our &lt;em&gt;mental models&lt;/em&gt; of everyday processes. People don&amp;#39;t carry every role around with them all the time either.&lt;/p&gt;
&lt;h3&gt;Real-World Examples&lt;/h3&gt;
&lt;p&gt;A school classroom, for example, is composed of people, but some are &lt;em&gt;teachers&lt;/em&gt; and some &lt;em&gt;students&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Similarly, some people are &lt;em&gt;passengers&lt;/em&gt; on public transport, and some are &lt;em&gt;conductors&lt;/em&gt;. Some even play multiple roles at once that may change over time — e.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;#39;m a &lt;em&gt;passenger&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If it&amp;#39;s a long ride, I might also assume the role of &lt;em&gt;book reader&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Suppose someone calls me on the phone. I&amp;#39;m simultaneously a &lt;em&gt;conversation participant&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If I travel with my daughter, I&amp;#39;m also her &lt;em&gt;dad&lt;/em&gt; and have to look after her.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And so on.&lt;/p&gt;
&lt;h3&gt;Back to Our &lt;code&gt;BankAccount&lt;/code&gt; Example in Rails&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s continue with the &lt;code&gt;BankAccount&lt;/code&gt; example from above, and expand it with a requirement to transfer money from one person to another. Consider the following module, which just defines a method to transfer the money:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module MoneyTransferring
  def transfer_money_to(destination:, amount:)
    self.decrease amount
    destination.increase amount
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key notion in this snippet is the reference to &lt;code&gt;self&lt;/code&gt;, as we shall see in a moment.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The beauty of applying this pattern to Ruby is the ability to inject modules at &lt;em&gt;run time&lt;/em&gt;. A &lt;em&gt;context&lt;/em&gt; can then map &lt;em&gt;source&lt;/em&gt; and &lt;em&gt;destination&lt;/em&gt; roles:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Transfer
  delegate :transfer_money_to, to: :source

  def initialize(source:)
    @source = source
    @source.include(MoneyTransferring)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, now &lt;code&gt;BankAccount&lt;/code&gt; is equipped with the ability to &lt;code&gt;transfer_money_to&lt;/code&gt; another account in the &lt;code&gt;Transfer&lt;/code&gt; context.&lt;/p&gt;
&lt;h3&gt;Interaction&lt;/h3&gt;
&lt;p&gt;The final part of DCI — interaction — comprises everything &lt;em&gt;the system does&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It is here that the &lt;em&gt;use cases&lt;/em&gt; of an application are enacted through &lt;em&gt;triggers&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;Transfer.new(source: source_account)
  .transfer_money_to(destination: destination_account, amount: 1_000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;em&gt;source&lt;/em&gt; and &lt;em&gt;destination&lt;/em&gt; roles are mapped to their respective domain models, and a transfer takes place. In a typical Rails app, this would happen in a controller or a job — sometimes even in model callbacks.&lt;/p&gt;
&lt;p&gt;A critical constraint of DCI is that these bindings are guaranteed to be in place &lt;strong&gt;only at run time&lt;/strong&gt;. In other words, the &lt;code&gt;Transfer&lt;/code&gt; object will be picked up by the garbage collector afterward, and no trace of the mapped roles remains with the domain models.&lt;/p&gt;
&lt;p&gt;If you flip this around, this ensures that DCI roles are &lt;em&gt;generic&lt;/em&gt;, making them both easier to reason about and test. In other words, the &lt;code&gt;Transfer&lt;/code&gt; context makes no assumptions about the &lt;em&gt;kind of&lt;/em&gt; objects its roles are mapped to. It only expects &lt;code&gt;increase&lt;/code&gt;/&lt;code&gt;decrease&lt;/code&gt; methods. &lt;strong&gt;The fact that they are &lt;code&gt;BankAccounts&lt;/code&gt; with an attached state is irrelevant!&lt;/strong&gt; They could equally be other types of objects (e.g., &lt;code&gt;Wallets&lt;/code&gt;/&lt;code&gt;StockPortfolios&lt;/code&gt;/&lt;code&gt;MoneyBox&lt;/code&gt;). The context does not care. Only through its enactment in a certain use case are the roles associated with certain types. As the snippet above shows, it&amp;#39;s succinct and readable.&lt;/p&gt;
&lt;h2&gt;Case Study Using DCI in a Rails Application&lt;/h2&gt;
&lt;p&gt;I want to conclude this article with an example from a real-world app where I used DCI to organize parts of the business logic. I will attempt to show how regular Rails MVC can &lt;em&gt;enact a DCI use case&lt;/em&gt;. Note that I&amp;#39;m using Jim Gay&amp;#39;s &lt;a href=&quot;https://github.com/saturnflyer/surrounded&quot;&gt;surrounded gem&lt;/a&gt; to strip away some of the boilerplate.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s a &lt;code&gt;Checkout&lt;/code&gt; context that includes methods to create and fulfill a &lt;code&gt;Stripe::Checkout:Session&lt;/code&gt; object:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/contexts/checkout.rb
class Checkout
  # ...

  role :payable do
    def create_session
      Stripe::Checkout::Session.create({
        line_items: line_items, # provided by model
        metadata: {
          gid: to_gid.to_s
        },
        success_url: polymorphic_url(self)
        # ...
      })
    end

    def fulfill
      # ...
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Imagine that the class method &lt;code&gt;role :payable&lt;/code&gt; is just a wrapper around the manual &lt;code&gt;.include(SomeModule)&lt;/code&gt; we did above; &lt;code&gt;create_session&lt;/code&gt; and &lt;code&gt;fulfill&lt;/code&gt; are called the &lt;strong&gt;RoleMethods&lt;/strong&gt; of this context. Note that in this case, the &lt;code&gt;create_session&lt;/code&gt; method only relies on &lt;code&gt;self&lt;/code&gt;, &lt;code&gt;to_gid&lt;/code&gt; (present on any &lt;code&gt;ActiveRecord::Base&lt;/code&gt; subclass), and a &lt;code&gt;line_items&lt;/code&gt; accessor.&lt;/p&gt;
&lt;p&gt;We can now write a test to enact this context:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class CheckoutTest &amp;lt; ActiveSupport::TestCase
  # VCR/Stub setup omitted

  test &amp;quot;created stripe checkout session includes gid in metadata&amp;quot; do
    @quote = quotes(:accepted_quote)

    session = Checkout.new(payable: @quote).create_session

    assert_equal @quote, GlobalID::Locator.locate(session.metadata.gid)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks good! Now let&amp;#39;s look at two separate use cases for this context.&lt;/p&gt;
&lt;h3&gt;Use Case 1: Quote Checkout&lt;/h3&gt;
&lt;p&gt;In my app, a &lt;code&gt;Quote&lt;/code&gt; is a bespoke offer to a certain customer. It typically contains only one line item, which Stripe will use to create a price:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# model
class Quote
  def line_items
    [{price: price_id, quantity: 1}] # Stripe::Price
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now a Stripe Checkout session can be created in a controller as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# controller
@checkout_session = Checkout.new(payable: @quote).checkout_session
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is then used in the view to send the customer to a Stripe Checkout form via a simple link:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- view --&amp;gt;
&amp;lt;%= link_to @checkout_session.url, target: &amp;quot;_top&amp;quot;, class: &amp;quot;btn btn-primary&amp;quot;, id: dom_id(@quote, :checkout_button) do %&amp;gt;
  &amp;lt;%= t(&amp;quot;.checkout&amp;quot;) %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Use Case 2: Review Checkout&lt;/h3&gt;
&lt;p&gt;Another payable in my app is a &lt;code&gt;Review&lt;/code&gt;, which contains many chapters, each with its own line item:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# model
class Review
  def line_items
    chapters.map do |chapter|
      {price: chapter.price_id, quantity: 1} # Stripe::Price
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apart from exchanging the &lt;code&gt;payable&lt;/code&gt;, the code for enacting the checkout use case stays exactly the same:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# controller
@checkout_session = Checkout.new(payable: @review).create_session
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- view --&amp;gt;
&amp;lt;%= link_to @checkout_session.url, target: &amp;quot;_top&amp;quot;, class: &amp;quot;btn btn-primary&amp;quot;, id: dom_id(@review, :checkout_button) do %&amp;gt;
  &amp;lt;%= t(&amp;quot;.checkout&amp;quot;) %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;DCI certainly isn&amp;#39;t a grab-all solution to code organization, but compared to some other approaches, it feels like a natural inhabitant of a Rails app&amp;#39;s ecosystem. What enthralls me the most is that it provides a clear structure for separating descriptive structure (&amp;quot;what the app &lt;em&gt;is&lt;/em&gt;&amp;quot;) from behavior (&amp;quot;what the app &lt;em&gt;does&lt;/em&gt;&amp;quot;) without compromising the &lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID&quot;&gt;SOLID principles&lt;/a&gt; for OOP design. This separation makes it a breeze to refactor key parts of such an app.&lt;/p&gt;
&lt;p&gt;The actual business logic design also feels more streamlined because DCI attempts to closely reflect our mental models of the software we build.&lt;/p&gt;
&lt;p&gt;That said, like any design pattern or paradigm out there, it&amp;#39;s not a hammer that fits every nail. You might find that this separation of behavior from data is something that diminishes code cohesion in your app, for example.&lt;/p&gt;
&lt;p&gt;If you want to try it on for size, I recommend using DCI for integrating third-party APIs, or with fringe concerns that don&amp;#39;t directly touch your app&amp;#39;s core functionality. That&amp;#39;s because those areas typically don&amp;#39;t change very often and are thus ideal playgrounds for experimenting with new tools.&lt;/p&gt;
&lt;h2&gt;Wrapping Up and References&lt;/h2&gt;
&lt;p&gt;In part one of this two-part series, we examined common approaches for building business logic in your Rails application, including fat models, service objects, jobs, and event sourcing.&lt;/p&gt;
&lt;p&gt;In this second and final part, we turned our attention to DCI specifically, exploring each individual part: Data, Context, and Interaction. We showed how to use the DCI paradigm in a real-world Rails application.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.clean-ruby.com/&quot;&gt;Clean Ruby eBook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/saturnflyer/surrounded&quot;&gt;Surrounded Gem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Use Sinatra to Build a Ruby Application</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/05/31/how-to-use-sinatra-to-build-a-ruby-application.html"/>
    <id>https://blog.appsignal.com/2023/05/31/how-to-use-sinatra-to-build-a-ruby-application.html</id>
    <published>2023-05-31T00:00:00+00:00</published>
    <updated>2023-05-31T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Sinatra is a powerful framework for Ruby applications. Let&#039;s see it in action.</summary>
    <content type="html">&lt;p&gt;In this article, we&amp;#39;ll introduce Ruby on Rails&amp;#39; lesser-known but powerful cousin &lt;a href=&quot;https://sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt;. We&amp;#39;ll use the framework to build a cost-of-living calculator app.&lt;/p&gt;
&lt;p&gt;By the end of the article, you&amp;#39;ll know what Sinatra is and how to use it.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s go!&lt;/p&gt;
&lt;h2&gt;Our Scenario&lt;/h2&gt;
&lt;p&gt;Imagine this: you&amp;#39;ve just landed a job as a Ruby developer for a growing startup and your new boss has agreed to let you work remotely for as long as you like.&lt;/p&gt;
&lt;p&gt;You start dreaming of all the cool cities where you could move to begin your digital-nomad life. You want to go somewhere nice but, most importantly, affordable. And to help you decide, you hit upon an idea to build a small app that shows cost-of-living data for almost any city or country you enter.&lt;/p&gt;
&lt;p&gt;With so many languages, frameworks, and no-code tools available today, what will you use to go from idea to app?&lt;/p&gt;
&lt;p&gt;Enter Sinatra!&lt;/p&gt;
&lt;h2&gt;Overview of Sinatra&lt;/h2&gt;
&lt;p&gt;Compared to Ruby on Rails, a full-stack web framework, &lt;a href=&quot;https://rubygems.org/gems/sinatra/versions/1.3.3&quot;&gt;Sinatra&lt;/a&gt; is a very lean micro-framework originally developed by Blake Mizerany to help Ruby developers build applications with &amp;quot;minimal effort&amp;quot;.&lt;/p&gt;
&lt;p&gt;With Sinatra, there is no Model-View-Controller (MVC) pattern, nor does it encourage you to use &amp;quot;convention over configuration&amp;quot; principles. Instead, you get a flexible tool to build simple, fast Ruby applications.&lt;/p&gt;
&lt;h2&gt;What Is Sinatra Good For?&lt;/h2&gt;
&lt;p&gt;Because of its lightweight and &lt;a href=&quot;https://github.com/rack/rack&quot;&gt;Rack&lt;/a&gt;-based architecture, Sinatra is great for building APIs, mountable app engines, command-line tools, and simple apps like the one we&amp;#39;ll build in this tutorial.&lt;/p&gt;
&lt;h2&gt;Our Example Ruby App&lt;/h2&gt;
&lt;p&gt;The app we are building will let you input how much you earn as well as the city and country you&amp;#39;d like to move to. Then it will output a few living expense figures for that city.&lt;/p&gt;
&lt;h3&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;To follow along, ensure you have the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ruby development environment (at least version 3.0.0+) already set up.&lt;/li&gt;
&lt;li&gt;Bundler and Sinatra installed on your development environment. If you don&amp;#39;t have Sinatra, simply run &lt;code&gt;gem install Sinatra&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A free &lt;a href=&quot;https://rapidapi.com/auth/sign-up&quot;&gt;RapidAPI&lt;/a&gt; account since we&amp;#39;ll use one of their APIs for our app project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also &lt;a href=&quot;https://github.com/iamaestimo/cost_calc_app&quot;&gt;get the full code for the example app here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before proceeding with our build, let&amp;#39;s discuss something very important: the structure of Sinatra apps.&lt;/p&gt;
&lt;h3&gt;Regular (Classical) Vs. Modular Sinatra Apps&lt;/h3&gt;
&lt;p&gt;When it comes to structure in Sinatra apps, you can have regular — sometimes referred to as &amp;quot;classical&amp;quot; — apps, or &amp;quot;modular&amp;quot; ones.&lt;/p&gt;
&lt;p&gt;In a classical Sinatra app, all your code lives in one file. You&amp;#39;ll almost always find that you can only run one Sinatra app per Ruby process if you choose the regular app structure.&lt;/p&gt;
&lt;p&gt;The example below shows a simple classical Sinatra app.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# main.rb

require &amp;#39;sinatra&amp;#39;
require &amp;#39;json&amp;#39;

get &amp;#39;/&amp;#39; do
# here we specify the content type to respond with
  content_type :json

  { item: &amp;#39;Red Dead Redemption 2&amp;#39;, price: 19.79, status: &amp;#39;Available&amp;#39;  }.to_json
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one file contains everything needed for this simplified app to run. Run it with &lt;code&gt;ruby main.rb&lt;/code&gt;, which should spin up an instance of the &lt;a href=&quot;https://github.com/macournoyer/thin&quot;&gt;Thin&lt;/a&gt; web server (the default web server that comes with Sinatra). Visit &lt;code&gt;localhost:4567&lt;/code&gt; and you&amp;#39;ll see the JSON response.&lt;/p&gt;
&lt;p&gt;As you can see, it is relatively easy to extend this simple example into a fairly-complex API app with everything contained in one file (the most prominent feature of the classical structure).&lt;/p&gt;
&lt;p&gt;Now let&amp;#39;s turn our attention to modular apps.&lt;/p&gt;
&lt;p&gt;The code below shows a basic modular Sinatra app. At first glance, it looks pretty similar to the classic app we&amp;#39;ve already looked at — apart from a rather simple distinction. In modular apps, we subclass &lt;code&gt;Sinatra::Base&lt;/code&gt;, and each &amp;quot;app&amp;quot; is defined within this subclassed scope.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# main.rb

require &amp;#39;sinatra/base&amp;#39;
require &amp;#39;json&amp;#39;
require_relative &amp;#39;lib/fetch_game_data&amp;#39;

# main module/class defined here
class GameStoreApp &amp;lt; Sinatra::Base

  get &amp;#39;/&amp;#39; do
    content_type :json

    { item: &amp;#39;Red Dead Redemption 2&amp;#39;, price: 19.79, status: &amp;#39;Available&amp;#39;  }.to_json
  end

  not_found do
    content_type :json

    { status: 404, message: &amp;quot;Nothing Found!&amp;quot; }.to_json
  end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Have a look at the &lt;a href=&quot;https://sinatrarb.com/extensions.html&quot;&gt;Sinatra documentation&lt;/a&gt; in case you need more information on this.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s now continue with our app build.&lt;/p&gt;
&lt;h3&gt;Structuring Our Ruby App&lt;/h3&gt;
&lt;p&gt;To begin with, we&amp;#39;ll take the modular approach with this build so it&amp;#39;s easy to organize functionality in a clean and intuitive way.&lt;/p&gt;
&lt;p&gt;Our cost-of-living calculator app needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A root page, which will act as our landing page.&lt;/li&gt;
&lt;li&gt;Another page with a form where a user can input their salary information.&lt;/li&gt;
&lt;li&gt;Finally, a results page that displays some living expenses for the chosen city.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The app will fetch cost-of-living data from an API hosted on RapidAPI.&lt;/p&gt;
&lt;p&gt;We won&amp;#39;t include any tests or user authentication to keep this tutorial brief.&lt;/p&gt;
&lt;p&gt;Go ahead and create a folder structure like the one shown below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;.
├── app.rb
├── config
│   └── database.yml
├── config.ru
├── db
│   └── development.sqlite3
├── .env
├── Gemfile
├── Gemfile.lock
├── .gitignore
├── lib
│   └── user.rb
├── public
│   └── css
│       ├── bulma.min.css
│       └── style.css
├── Rakefile
├── README.md
├── views
│   ├── index.erb
│   ├── layout.erb
│   ├── navbar.erb
│   ├── results.erb
│   └── start.erb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&amp;#39;s what each part does in a nutshell (we&amp;#39;ll dig into the details as we proceed with the app build):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app.rb&lt;/code&gt; - This is the main file in our modular app. In here, we define the app&amp;#39;s functionality.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Gemfile&lt;/code&gt; - Just like the Gemfile in a Rails app, you define your app&amp;#39;s gem dependencies in this file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Rakefile&lt;/code&gt; - Rake task definitions are defined here.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.ru&lt;/code&gt; - For modular Sinatra apps, you need a Rack configuration file that defines how your app will run.&lt;/li&gt;
&lt;li&gt;Views folder - Your app&amp;#39;s layout and view files go into this folder.&lt;/li&gt;
&lt;li&gt;Public folder - Files that don&amp;#39;t change much — such as stylesheets, images, and Javascript files — are best kept here.&lt;/li&gt;
&lt;li&gt;Lib folder - In here, you can have model files and things like specialized helper files.&lt;/li&gt;
&lt;li&gt;DB folder - Database migration files and the &lt;code&gt;seeds.rb&lt;/code&gt; will go in here.&lt;/li&gt;
&lt;li&gt;Config folder - Different configurations can go into this folder: for example, database settings.&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;The Main File (&lt;code&gt;app.rb&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;app.rb&lt;/code&gt; is the main entry point into our app where we define what the app does. Notice how we&amp;#39;ve subclassed &lt;code&gt;Sinatra::Base&lt;/code&gt; to make the app modular.&lt;/p&gt;
&lt;p&gt;As you can see below, we include some settings for fetching folders as well as defining the public folder (for storing static files). Another important note here is that we register the &lt;code&gt;Sinatra::ActiveRecordExtension&lt;/code&gt; which lets us work with ActiveRecord as the ORM.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app.rb

# Include all the gems listed in Gemfile
require &amp;#39;bundler&amp;#39;
Bundler.require

module LivingCostCalc

  class App &amp;lt; Sinatra::Base

    # global settings
    configure do
      set :root, File.dirname(__FILE__)
      set :public_folder, &amp;#39;public&amp;#39;

      register Sinatra::ActiveRecordExtension
    end

    # development settings
    configure :development do
      # this allows us to refresh the app on the browser without needing to restart the web server
      register Sinatra::Reloader
    end

  end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we define the routes we need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The root, which is just a simple landing page.&lt;/li&gt;
&lt;li&gt;A &amp;quot;Start here&amp;quot; page with a form where a user inputs the necessary information.&lt;/li&gt;
&lt;li&gt;A results page.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app.rb

class App &amp;lt; Sinatra::Base
...

  # root route
  get &amp;#39;/&amp;#39;  do
    erb :index
  end

  # start here (where the user enters their info)
  get &amp;#39;/start&amp;#39; do
    erb :start
  end

  # results
  get &amp;#39;/results&amp;#39; do
    erb :results
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might notice that each route includes the line &lt;code&gt;erb :&amp;lt;route&amp;gt;&lt;/code&gt;, which is how you tell Sinatra the respective view file to render from the &amp;quot;views&amp;quot; folder.&lt;/p&gt;
&lt;h3&gt;Database Setup for the Sinatra App&lt;/h3&gt;
&lt;p&gt;The database setup for our Sinatra app consists of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A database config file — &lt;code&gt;database.yml&lt;/code&gt; — where we define the database settings for the development, production, and test databases.&lt;/li&gt;
&lt;li&gt;Database adapter and ORM gems included in the Gemfile. We are using ActiveRecord for our app. &lt;a href=&quot;https://github.com/jmkeyes/sinatra-datamapper&quot;&gt;Datamapper&lt;/a&gt; is another option you could use.&lt;/li&gt;
&lt;li&gt;Registering the ORM extension and the database config file in &lt;code&gt;app.rb&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;#39;s the database config file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# config/database.yml

default: &amp;amp;default
  adapter: sqlite3
  pool: 5
  timeout: 5000

development:
  &amp;lt;&amp;lt;: *default
  database: db/development.sqlite3

test:
  &amp;lt;&amp;lt;: *default
  database: db/test.sqlite3

production:
  adapter: postgresql
  encoding: unicode
  pool: 5
  host: &amp;lt;%= ENV[&amp;#39;DATABASE_HOST&amp;#39;] || &amp;#39;db&amp;#39; %&amp;gt;
  database: &amp;lt;%= ENV[&amp;#39;DATABASE_NAME&amp;#39;] || &amp;#39;sinatra&amp;#39; %&amp;gt;
  username: &amp;lt;%= ENV[&amp;#39;DATABASE_USER&amp;#39;] || &amp;#39;sinatra&amp;#39; %&amp;gt;
  password: &amp;lt;%= ENV[&amp;#39;DATABASE_PASSWORD&amp;#39;] || &amp;#39;sinatra&amp;#39; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the ORM and database adaptor gems in the Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

source &amp;quot;https://rubygems.org&amp;quot;

# Ruby version
ruby &amp;quot;3.0.4&amp;quot;

gem &amp;#39;sinatra&amp;#39;
gem &amp;#39;activerecord&amp;#39;
gem &amp;#39;sinatra-activerecord&amp;#39; # ORM gem
gem &amp;#39;sinatra-contrib&amp;#39;
gem &amp;#39;thin&amp;#39;
gem &amp;#39;rake&amp;#39;
gem &amp;#39;faraday&amp;#39;

group :development do
  gem &amp;#39;sqlite3&amp;#39; # Development database adaptor gem
  gem &amp;#39;tux&amp;#39; # gives you access to an interactive console similar to &amp;#39;rails console&amp;#39;
  gem &amp;#39;dotenv&amp;#39;
end

group :production do
  gem &amp;#39;pg&amp;#39; # Production database adaptor gem
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here&amp;#39;s how you register the ORM and database config in &lt;code&gt;app.rb&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app.rb

module LivingCostCalc

  class App &amp;lt; Sinatra::Base
    # global settings
    configure do
      ...
      register Sinatra::ActiveRecordExtension
    end

    # database settings
    set :database_file, &amp;#39;config/database.yml&amp;#39;

    ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Connecting to the Cost-of-Living API&lt;/h3&gt;
&lt;p&gt;For our app to show relevant cost-of-living data for whatever city a user inputs, we have to fetch it via an API call to &lt;a href=&quot;https://rapidapi.com/ugurerdal93/api/cost-of-living-prices-by-city-country&quot;&gt;this API&lt;/a&gt;. Create a free RapidAPI account to access it if you haven&amp;#39;t done so.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll make the API call using the Faraday gem. Add it to the Gemfile and run &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

gem &amp;#39;faraday&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that done, we now include the API call logic in the &lt;code&gt;results&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app.rb
...
  get &amp;#39;/results&amp;#39; do
    city = params[:city]
    country = params[:country]

    # if country or city names have spaces, process accordingly
    esc_city = ERB::Util.url_encode(country) # e.g. &amp;quot;St Louis&amp;quot; becomes &amp;#39;St%20Louis&amp;#39;
    esc_country = ERB::Util.url_encode(country) # e.g. &amp;quot;United States&amp;quot; becomes &amp;#39;United%20States&amp;#39;

    url = URI(&amp;quot;https://cost-of-living-prices-by-city-country.p.rapidapi.com/get-city?city=#{esc_city}&amp;amp;country=#{esc_country}&amp;quot;)

    conn = Faraday.new(
      url: url,
      headers: {
        &amp;#39;X-RapidAPI-Key&amp;#39; =&amp;gt; ENV[&amp;#39;RapidAPIKey&amp;#39;],
        &amp;#39;X-RapidAPI-Host&amp;#39; =&amp;gt; ENV[&amp;#39;RapidAPIHost&amp;#39;]
      }
    )

    response = conn.get

    @code = response.status
    @results = response.body

    erb :results
  end

  ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Views and Adding Styles&lt;/h3&gt;
&lt;p&gt;All our views are located in the &amp;quot;views&amp;quot; folder. In here, we also have a layout file — &lt;code&gt;layout.erb&lt;/code&gt; — which all views inherit their structure from. It is similar to the layout file in Rails.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;# views/layout.erb

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;ie=edge&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Cost of living calc app&amp;lt;/title&amp;gt;
    &amp;lt;link
      rel=&amp;quot;stylesheet&amp;quot;
      href=&amp;quot;css/bulma.min.css&amp;quot;
      type=&amp;quot;text/css&amp;quot;
      rel=&amp;quot;stylesheet&amp;quot;
    /&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;css/style.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;!-- navbar partial --&amp;gt;
    &amp;lt;%= erb :&amp;#39;navbar&amp;#39; %&amp;gt;
    &amp;lt;!-- //navbar --&amp;gt;

    &amp;lt;div&amp;gt;&amp;lt;%= yield %&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also add a local copy of &lt;a href=&quot;https://bulma.io/&quot;&gt;Bulma CSS&lt;/a&gt; and a custom stylesheet in &lt;code&gt;public/css&lt;/code&gt; to provide styling for our app.&lt;/p&gt;
&lt;h2&gt;Running the Sinatra App&lt;/h2&gt;
&lt;p&gt;To run a modular Sinatra app, you need to include a &lt;code&gt;config.ru&lt;/code&gt; file where you specify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The main file that will be used as the entry point.&lt;/li&gt;
&lt;li&gt;The main module that will run (remember that modular Sinatra apps can have multiple &amp;quot;apps&amp;quot;).&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config.ru
require File.join(File.dirname(__FILE__), &amp;#39;app.rb&amp;#39;)
run LivingCostCalc::App
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Deploying Your Sinatra App to Production&lt;/h2&gt;
&lt;p&gt;A step-by-step guide for deploying a Sinatra app to production would definitely make this tutorial too long. But to give you an idea of the options you have, consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using a PaaS like &lt;a href=&quot;https://devcenter.heroku.com/articles/rack&quot;&gt;Heroku&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Using a cloud service provider like AWS Elastic Cloud or the likes of Digital Ocean and Linode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you use Heroku, one thing to note is that you will need to include a Procfile in your app&amp;#39;s root:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;web: bundle exec rackup config.ru -p $PORT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To deploy to a cloud service like AWS&amp;#39;s Elastic Cloud, the easiest method is to Dockerize your app and deploy the container.&lt;/p&gt;
&lt;h2&gt;Monitoring Your Sinatra App with AppSignal&lt;/h2&gt;
&lt;p&gt;Another thing that&amp;#39;s very important and shouldn&amp;#39;t be overlooked is application monitoring.&lt;/p&gt;
&lt;p&gt;Once you&amp;#39;ve successfully deployed your Sinatra app, you can easily use &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;Appsignal&amp;#39;s Ruby APM service&lt;/a&gt;. AppSignal offers an integration for Rails and Rack-based apps like &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/sinatra.html&quot;&gt;Sinatra&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When you integrate AppSignal, you&amp;#39;ll get incident reports and dashboards for everything going on.&lt;/p&gt;
&lt;p&gt;The screenshot below shows our Sinatra app&amp;#39;s memory usage dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-05/sinatra-dashboard.png&quot; alt=&quot;Sinatra dashboard in AppSignal&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping Up and Next Steps&lt;/h2&gt;
&lt;p&gt;In this post, we learned what Sinatra is and what you can use the framework for. We then built a modular app using Sinatra.&lt;/p&gt;
&lt;p&gt;You can take this to the next level by building user authentication functionality for the app.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Manage Your Ruby Logs Like a Pro</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/05/17/manage-your-ruby-logs-like-a-pro.html"/>
    <id>https://blog.appsignal.com/2023/05/17/manage-your-ruby-logs-like-a-pro.html</id>
    <published>2023-05-17T00:00:00+00:00</published>
    <updated>2023-05-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover the ins and outs of logging in Ruby, including how to customize your logs and use logging libraries like Lograge.</summary>
    <content type="html">&lt;p&gt;Logs are essential to any application&amp;#39;s development. Most Ruby logs are
verbose and chunky, so digging for exactly what you need can be difficult.
Even though they contain useful information, you
might not get as much value as you should from logs if you don&amp;#39;t know how to
use them effectively.&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll explore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The importance of logging&lt;/li&gt;
&lt;li&gt;How to use Ruby&amp;#39;s inbuilt logging utilities — &lt;code&gt;puts&lt;/code&gt; and Logger&lt;/li&gt;
&lt;li&gt;Log levels and their differences&lt;/li&gt;
&lt;li&gt;Customizing your logs using JSON&lt;/li&gt;
&lt;li&gt;Integrating a logging library, using Lograge as an example&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get started.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To follow along with this tutorial, you should have the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A local installation of Ruby (we&amp;#39;ll be using Ruby 3.2.0 for the tutorial).&lt;/li&gt;
&lt;li&gt;Some experience with Ruby.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s start by answering the question: &amp;quot;Why do we need to log, anyway?&amp;quot;&lt;/p&gt;
&lt;h2&gt;Why We Log&lt;/h2&gt;
&lt;p&gt;Before we dive in, it helps to know exactly why we log in
the first place.&lt;/p&gt;
&lt;p&gt;Logs provide us with information on what&amp;#39;s happening inside our Ruby
app. This is very useful for tasks like debugging errors and collecting critical
app metrics. Log data depends on the type of app we have and the environment
it is running on.&lt;/p&gt;
&lt;p&gt;With that out of the way, let&amp;#39;s introduce Ruby&amp;#39;s inbuilt logging utilities.&lt;/p&gt;
&lt;h2&gt;Ruby’s Inbuilt Logging Utilities: &lt;code&gt;puts&lt;/code&gt; and Logger&lt;/h2&gt;
&lt;p&gt;Ruby comes with two logging utilities. One is &lt;code&gt;puts&lt;/code&gt;, a method provided
by the &lt;a href=&quot;https://ruby-doc.org/3.2.0/IO.html&quot;&gt;IO class&lt;/a&gt; (other methods include
&lt;code&gt;print&lt;/code&gt;, &lt;code&gt;printf&lt;/code&gt;, and &lt;code&gt;write&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The other
inbuilt logging utility is the Logger class.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s explore each in more detail, starting with &lt;code&gt;puts&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Ruby&amp;#39;s &lt;code&gt;puts&lt;/code&gt; Method&lt;/h2&gt;
&lt;p&gt;Ruby&amp;#39;s &lt;code&gt;puts&lt;/code&gt; method — which is actually syntactic sugar for &lt;code&gt;STDOUT.puts&lt;/code&gt; — is a way
of writing Ruby program messages to the &lt;code&gt;STDOUT&lt;/code&gt; output stream (usually a
terminal output). &lt;code&gt;puts&lt;/code&gt; will print out the given message string and add a
new line to the end of the output.&lt;/p&gt;
&lt;p&gt;The example below shows a simple use case where we use &lt;code&gt;puts&lt;/code&gt; to output each line&amp;#39;s length from a given text file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# line_lengths.rb
ARGV.each do |f|
  File.open(f).each_line do |line|
    puts line.length
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# MyFile.txt
Ruby is an awesome language to learn.
It&amp;#39;s very simple to get started.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we run the above Ruby script and pass in the text file as the argument:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;ruby line_lengths.rb MyFile.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And once the script has run, each line&amp;#39;s length is written out to the console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
38
33
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple example provides a glimpse into the world of the input/output (IO)
streams at the heart of logging. Basically, anytime you run a Ruby
program, you spawn the following IO streams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;STDIN&lt;/code&gt; - This stream accepts commands into your Ruby program — for example, a
key pressed on a user&amp;#39;s keyboard.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STDOUT&lt;/code&gt; writes to an output — for example, a terminal displayed on a
user&amp;#39;s screen.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STDERR&lt;/code&gt; writes to an output just like the &lt;code&gt;STDOUT&lt;/code&gt; one, but with
a subtle difference that we&amp;#39;ll cover in the next section.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these is integral to logging and understanding how they work
forms the basis of properly applying logs in Ruby apps.&lt;/p&gt;
&lt;h3&gt;The Limitations of &lt;code&gt;puts&lt;/code&gt; in Ruby&lt;/h3&gt;
&lt;p&gt;Even though &lt;code&gt;puts&lt;/code&gt; is a convenient and easy
logging method for simple Ruby programs, you
will very quickly need a proper logging library instead.&lt;/p&gt;
&lt;p&gt;Here are a couple of its limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatic timestamps aren&amp;#39;t included by default&lt;/strong&gt; - Unless you manually customize &lt;code&gt;puts&lt;/code&gt;, you will not get any timestamps in your log output.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Output is not categorized into appropriate log levels&lt;/strong&gt; - We&amp;#39;ll cover
this in more detail later, but the &lt;code&gt;puts&lt;/code&gt;
method has no way to categorize output messages according to standard
log levels.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These features are basic requirements for any logging solution. The fact that
they are missing from the &lt;code&gt;puts&lt;/code&gt; method by default (coupled with the manual
customization needed to have meaningful output messages) means that our
ability to properly log using the method is limited.&lt;/p&gt;
&lt;p&gt;So let&amp;#39;s now consider a more robust solution, the Ruby Logger
class.&lt;/p&gt;
&lt;h2&gt;Logging with Ruby Logger&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.ruby-lang.org/en/master/Logger.html&quot;&gt;Logger&lt;/a&gt; class is a
sophisticated logging utility that can create single or multiple
event logs from a Ruby application. Logger comes bundled in Ruby, so no gem installation is needed to get it up and running. You only need a
simple &lt;code&gt;require&lt;/code&gt; statement somewhere at the top of your script:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;logger&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, instantiate a new Logger object as below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;logger&amp;#39;

logger = Logger.new(STDOUT)
logger.info(&amp;quot;This is an info level log message&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Logger class can accept a number of arguments, but the first one is
almost always the log stream output, followed by other arguments like the
retention policy (how long logs should be kept, how much the
log files should grow in size before they are rotated, etc.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You will get output similar to this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
I, [2023-01-11T17:45:49.067481 #67559]  INFO -- : This is an info level log message
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the printed log message, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The log level&amp;#39;s shortened format&lt;/strong&gt; - Indicated by the leading &amp;quot;D&amp;quot;. In this
case, it shows that this is an &lt;code&gt;info&lt;/code&gt; level log entry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A timestamp&lt;/strong&gt; - A full timestamp of the log entry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A process ID&lt;/strong&gt; - The ID of the server process running the program.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The log level type in full&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A program name&lt;/strong&gt; - An optional argument that can be added to a Logger
entry (we&amp;#39;ll see how in a later code example).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The log message&lt;/strong&gt; - The message output that will be printed in the log
entry (also optional).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these log entry components is important, and
together they give context to app events that lead to the
creation of log entries.&lt;/p&gt;
&lt;p&gt;That said, one aspect of log entry that deserves more coverage is the log
level. Let&amp;#39;s take a look at it next.&lt;/p&gt;
&lt;h2&gt;Understanding Log Levels&lt;/h2&gt;
&lt;p&gt;A log level setting tells the Logger a log
message&amp;#39;s severity. The severity level depends upon the event that
resulted in that log output.&lt;/p&gt;
&lt;p&gt;There are six levels of severity, ranging from the
least to the most severe:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# example.rb
require &amp;#39;logger&amp;#39;

logger = Logger.new(STDOUT)

logger.add(Logger::DEBUG, &amp;#39;Least severe - debugging info&amp;#39;)
logger.add(Logger::INFO, &amp;#39;Next level - informational log&amp;#39;)
logger.add(Logger::WARN, &amp;#39;Warning level - warning log info&amp;#39;)
logger.add(Logger::ERROR, &amp;#39;Non-fatal error log info&amp;#39;)
logger.add(Logger::FATAL, &amp;#39;Fatal error log info&amp;#39;)
logger.add(Logger::UNKNOWN, &amp;#39;Most severe log level&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here&amp;#39;s the output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
D, [2023-01-11T18:19:47.919567 #69315] DEBUG -- : Least severe - debugging info
I, [2023-01-11T18:19:47.919622 #69315]  INFO -- : Next level - informational log
W, [2023-01-11T18:19:47.919645 #69315]  WARN -- : Warning level - warning log info
E, [2023-01-11T18:19:47.919661 #69315] ERROR -- : Non-fatal error log info
F, [2023-01-11T18:19:47.919675 #69315] FATAL -- : Fatal error log info
A, [2023-01-11T18:19:47.919689 #69315]   ANY -- : Most severe log level
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Log levels can help filter out app and server
events that warrant your attention. To put this into context,
consider these differences between log levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;debug&lt;/code&gt; - This is the default log level and the most information-rich. When
this log level is enabled, your log entries will include pretty much every
request happening in your app. This is particularly useful for a debugging session.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;info&lt;/code&gt; - A level above &lt;code&gt;debug&lt;/code&gt;. Just like the previous log level,
enabling this level will result in some very information-rich log entries of
your app&amp;#39;s events. &lt;code&gt;info&lt;/code&gt; log entries will tell you what&amp;#39;s going on with
your app — for instance, running processes and method calls.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;warn&lt;/code&gt; - Any log entries at this level usually mean that your app
encountered a problem, but nothing so severe that your app can&amp;#39;t continue to
function as expected (for example, you may have used an outdated method call for a gem or
library.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error&lt;/code&gt; - At this level, your app has encountered an error that stops it from
proceeding with the action that caused the error. However, this does not stop
other parts of the app from working as expected. For example, if your app references
a recently moved file, an &lt;code&gt;error&lt;/code&gt; log entry will likely be
generated.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fatal&lt;/code&gt; - Your app has encountered an error that
stops it from working any further. This could be caused by anything from running
out of memory on the production server or a crashed background job.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unknown&lt;/code&gt; - The
actual cause of an error cannot be reliably traced.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that we have more information on log levels, let&amp;#39;s see how to practically use them in a Ruby app.&lt;/p&gt;
&lt;h2&gt;Using Log Levels in a Ruby Application&lt;/h2&gt;
&lt;p&gt;In a development environment, the default log level is set to &lt;code&gt;debug&lt;/code&gt;.
As mentioned earlier, if your app&amp;#39;s log level is set to &lt;code&gt;debug&lt;/code&gt;
(or even &lt;code&gt;info&lt;/code&gt;), your output will include everything happening in the app.
You will have very information-rich log entries that make
for easier debugging in development.&lt;/p&gt;
&lt;p&gt;However, in production, it isn&amp;#39;t wise to use the &lt;code&gt;debug&lt;/code&gt; or &lt;code&gt;info&lt;/code&gt; log
levels.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;For starters, a production app will likely have too many processes, users, and
method calls going on, so could very easily generate thousands (if not millions)
of log entries. If these entries are being saved to log files on a production
server, you can quickly run out of disk space.&lt;/p&gt;
&lt;p&gt;Secondly, any attempt to sift for important &lt;code&gt;error&lt;/code&gt; and &lt;code&gt;fatal&lt;/code&gt; entries is extremely inefficient in production simply because you will have too much
information to go through. So, you should only log entries
when something &amp;quot;important&amp;quot; happens or when your app malfunctions in such a way
that the user experience is affected.&lt;/p&gt;
&lt;p&gt;It therefore makes
more sense to set the log level at something more manageable like the &lt;code&gt;error&lt;/code&gt;
level, for example.&lt;/p&gt;
&lt;p&gt;Although setting the log level can reduce the amount of information
in the log entries, it doesn&amp;#39;t help format the entries in any way. To do that,
you need to customize the log output.&lt;/p&gt;
&lt;h2&gt;Customizing Log Output in Ruby&lt;/h2&gt;
&lt;p&gt;Customizing log output involves changing the format of how log
entries look and the information they contain when printed
out.&lt;/p&gt;
&lt;p&gt;By default, log entries printed to the terminal will be in black and white text. But for some logs, a little color can go a long
way. You can colorize default log output using ANSI color codes.&lt;/p&gt;
&lt;p&gt;In Ruby, this is possible using a gem like
&lt;a href=&quot;https://github.com/fazibear/colorize&quot;&gt;Colorize&lt;/a&gt; or
&lt;a href=&quot;https://github.com/sickill/rainbow&quot;&gt;Rainbow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can format the printed output from your Ruby
program using either of these gems. This example code uses &lt;code&gt;Rainbow&lt;/code&gt; to colorize some sample log
outputs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# example.rb
require &amp;#39;logger&amp;#39;
require &amp;#39;rainbow&amp;#39;

logger = Logger.new(STDOUT)
logger.debug(&amp;quot;This is a #{Rainbow(&amp;#39;debug message!&amp;#39;).blue}&amp;quot;)
logger.info(&amp;quot;This is a #{Rainbow(&amp;#39;info message!&amp;#39;).green}&amp;quot;)
logger.warn(&amp;quot;This is a #{Rainbow(&amp;#39;warning message!&amp;#39;).orange}&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here&amp;#39;s the colorized log output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-05/colorized-output.png&quot; alt=&quot;Colorized output&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The other way to format log entries goes beyond just customizing the
look and feel. First, consider the simple Logger entry below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
D, [2023-01-23T15:29:35.924904 #11021] DEBUG -- my_prog: This is a debug level log entry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s good to note that you are not limited to using the log entry as is. It is
entirely possible to customize a log message by tweaking the log entry
components. Let&amp;#39;s see how.&lt;/p&gt;
&lt;p&gt;The first step is to call &lt;code&gt;Logger.new&lt;/code&gt;, and then add some log entries:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# example.rb
logger = Logger.new(STDOUT)

logger.info(&amp;#39;This is an informational level log&amp;#39;)
logger.error(&amp;#39;This is just an error log&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which gives us this output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
I, [2023-01-23T16:24:34.315029 #12604]  INFO -- : This is an informational level log
E, [2023-01-23T16:24:34.315112 #12604] ERROR -- : This is just an error log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s also possible to customize a program name in the log entry, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# example.rb
logger = Logger.new(STDOUT)

logger.progname = &amp;#39;sending emails&amp;#39;

logger.info(&amp;#39;This is an informational level log&amp;#39;)
logger.error(&amp;#39;This is just an error log&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here&amp;#39;s the subsequent output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
I, [2023-01-23T16:45:55.156600 #14183]  INFO -- sending emails: This is an informational level log
E, [2023-01-23T16:45:55.156660 #14183] ERROR -- sending emails: This is just an error log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By customizing the log entry&amp;#39;s program name, you can add even more contextual
information to your logs.&lt;/p&gt;
&lt;p&gt;Now, let&amp;#39;s look at how to customize all of the log entry&amp;#39;s components all at
once:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# example.rb
logger = Logger.new(STDOUT)

logger.formatter = proc do |severity, datetime, progname, msg|
  date_format = datetime.strftime(&amp;quot;%Y-%m-%d&amp;quot;)
  &amp;quot;date=[#{date_format}] severity=#{severity.ljust(5)} pid=##{Process.pid} message=&amp;#39;#{msg}&amp;#39;\n&amp;quot;
end

logger.debug(&amp;quot;This is a formatted log message!&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
date=[2023-01-12] severity=DEBUG pid=#75142 message=&amp;#39;This is a formatted log message!&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we use the logger entry formatter &lt;code&gt;proc&lt;/code&gt;, which takes four arguments — severity, datetime, the program&amp;#39;s name, and the message — to format the log
entry and output a string formatted in the way specified.&lt;/p&gt;
&lt;p&gt;Great — but we can still do better! So far, everything we&amp;#39;ve done has produced
log outputs in an unstructured format. But log entries can build up quickly in a production app that has thousands of users and
millions of processes. Parsing unstructured logs can take up significant time and is very inefficient.&lt;/p&gt;
&lt;p&gt;What
if there was a way to quickly and efficiently search through log files? Enter JSON.&lt;/p&gt;
&lt;h2&gt;Logging into JSON&lt;/h2&gt;
&lt;p&gt;The Javascript Object Notation format — or JSON — is a structured text format that uses key/value pairs.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s not the only format that has structured text — there are others like YAML and even TOML. Both YAML and TOML function best as configuration files, though, while JSON&amp;#39;s structure makes
it really good for logging.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s some code that outputs a JSON-formatted log entry:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# example.rb
require &amp;#39;logger&amp;#39;
logger = Logger.new(STDOUT)
logger.formatter = proc do |_severity, datetime, _progname, msg|
  %({timestamp: &amp;quot;#{datetime}&amp;quot;, message: &amp;quot;#{msg}&amp;quot;}\n)
end
logger.info &amp;#39;Hello, world!&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
{timestamp: &amp;quot;2023-02-13 17:47:13 +0300&amp;quot;, message: &amp;quot;Hello, world!&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first glance, it&amp;#39;s not easy to see why one would even want to do something like
this. But with JSON formatted logs, it&amp;#39;s possible to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easily parse logs&lt;/strong&gt; as they are now structured in a particular format (compared to the
mass of unstructured entries characteristic of default logs). You can search for particular pieces of information using
JSON keys tied to specific values.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forward these logs to a proper log processing tool&lt;/strong&gt;
that can process log outputs of almost any size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we&amp;#39;ve seen how useful Logger can be. But that does not mean the library
is perfect.&lt;/p&gt;
&lt;p&gt;Logger does have its imperfections, and we&amp;#39;ll look at these next.&lt;/p&gt;
&lt;h2&gt;Limitations of Logger&lt;/h2&gt;
&lt;p&gt;If you use Logger to write output to log files frequently, one challenge you&amp;#39;ll
likely face is unreadable characters messing up your log
files. These are caused by colorization ANSI codes not being parsed correctly in
the log output files.&lt;/p&gt;
&lt;p&gt;For example, consider this example code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# example.rb
require &amp;#39;logger&amp;#39;
require &amp;#39;rainbow&amp;#39;

msg = Rainbow(&amp;quot;this is red&amp;quot;).red + &amp;quot; and &amp;quot; + Rainbow(&amp;quot;this on yellow bg&amp;quot;).bg(:yellow) + &amp;quot; and &amp;quot; + Rainbow(&amp;quot;even bright underlined!&amp;quot;).underline.bright

logger = Logger.new(&amp;#39;colorized.log&amp;#39;)

logger.debug(msg)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you try to open the resulting log file, you&amp;#39;ll notice it contains a bunch
of unreadable characters in the log message section:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# output
# Logfile created on 2023-01-23 23:09:12 +0300 by logger.rb/v1.5.3
D, [2023-01-23T23:09:12.920635 #31599] DEBUG -- : ^[[31mthis is red^[[0m and ^[[43mthis on yellow bg^[[0m and ^[[4m^[[1meven bright underlined!^[[0m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One way to fix this problem is by using a string regex to escape these
characters. For example, the &lt;code&gt;Colorize&lt;/code&gt; gem contains a convenient &lt;code&gt;uncolorize&lt;/code&gt;
block:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# example.rb
...
def uncolorize
  self.scan(REGEXP_PATTERN).inject(&amp;quot;&amp;quot;) do |str, match|
    str &amp;lt;&amp;lt; (match[3] || match[4])
  end
end
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another limitation of Logger, especially for busy
production apps, is that default Logger entries can end up as
information-rich multi-line entries (even when the log level has been set to
something above the &lt;code&gt;debug&lt;/code&gt; and &lt;code&gt;info&lt;/code&gt; levels, like the &lt;code&gt;error&lt;/code&gt; level).&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re in charge of such an app, debugging can easily become a messy
affair as you have to go through a mass of unstructured text to get what you
want.&lt;/p&gt;
&lt;p&gt;Considering these limitations, let&amp;#39;s seek a better logging
solution — one that has the features we&amp;#39;ve been lacking
in most of the solutions we&amp;#39;ve outlined so far.&lt;/p&gt;
&lt;h2&gt;Integrating a Third-Party Logging Library&lt;/h2&gt;
&lt;p&gt;You can choose from a number of third-party logging libraries, including &lt;a href=&quot;https://github.com/twp/logging&quot;&gt;Logging&lt;/a&gt; — based on Java&amp;#39;s log4j library — and &lt;a href=&quot;https://github.com/roidrage/lograge&quot;&gt;Lograge&lt;/a&gt;.
Lograge is a feature-rich logging library meant to simplify the often messy and verbose Rails logs characteristic of the default application logger.&lt;/p&gt;
&lt;p&gt;Also worth mentioning is &lt;a href=&quot;https://github.com/trusche/httplog&quot;&gt;httplog&lt;/a&gt;,
a specialized logging library for tracking third-party API calls that might
not be captured in regular logs.&lt;/p&gt;
&lt;p&gt;For this article, we&amp;#39;ll look at Lograge.&lt;/p&gt;
&lt;h2&gt;Lograge for Logging in Rails&lt;/h2&gt;
&lt;p&gt;According to &lt;a href=&quot;https://github.com/roidrage/lograge&quot;&gt;Lograge&amp;#39;s GitHub page&lt;/a&gt;, Lograge is an attempt at bringing sanity to Rails&amp;#39; unusable and unparsable log output.&lt;/p&gt;
&lt;p&gt;Lograge replaces the default Rails logger and simplifies output to single lines, with all the important information included. The resulting output is much easier to read through than the Rails logger.&lt;/p&gt;
&lt;h2&gt;Getting Started with Lograge&lt;/h2&gt;
&lt;p&gt;To get started with Lograge, open up your project&amp;#39;s Gemfile and add the following line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

gem &amp;quot;lograge&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run &lt;code&gt;bundle install&lt;/code&gt; to finalize the installation.&lt;/p&gt;
&lt;h2&gt;Logging in Rails: Using Lograge with AppSignal&lt;/h2&gt;
&lt;p&gt;With Lograge installed, you&amp;#39;re halfway through setting up a capable logging solution for your app. The next obvious question is what you&amp;#39;ll use to read and parse the logs. SSHing into your server to read through log tails is highly inefficient, so what to do?&lt;/p&gt;
&lt;p&gt;Well, &lt;a href=&quot;https://www.appsignal.com/tour/log-management&quot;&gt;AppSignal has a new logging feature&lt;/a&gt; that is perfect for this.&lt;/p&gt;
&lt;p&gt;To get started, install the latest version of the AppSignal for Ruby gem by adding it to your Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

gem &amp;#39;appsignal&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run &lt;code&gt;bundle install&lt;/code&gt;. If you already have the gem installed in your project, just run &lt;code&gt;bundle update appsignal&lt;/code&gt; to get the latest version.&lt;/p&gt;
&lt;p&gt;Next, add an initializer with the following information:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/lograge.rb

Rails.application.configure do
  config.lograge.enabled = true
  config.lograge.keep_original_rails_log = true
  config.lograge.logger = Appsignal::Logger.new(
    &amp;quot;rails&amp;quot;,
    format: Appsignal::Logger::LOGFMT
  )
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&amp;#39;s it! You can now conveniently access your app&amp;#39;s logs from AppSignal&amp;#39;s logging dashboard:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-05/appsignal-logging.png&quot; alt=&quot;AppSignal Logging&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, we took a deep dive into the world of Ruby logs. We
learned about &lt;code&gt;puts&lt;/code&gt;, the Logger class, and how to customize log messages using
JSON. We finally explored logging using Lograge and AppSignal.&lt;/p&gt;
&lt;p&gt;Obviously, logging in Ruby goes much deeper. Use this post as a stepping stone to learn more. You might also find our &lt;a href=&quot;https://blog.appsignal.com/2023/03/01/making-the-most-of-your-logs-in-rails.html&quot;&gt;Making the Most of Your Logs in Rails&lt;/a&gt; and &lt;a href=&quot;https://blog.appsignal.com/2023/04/12/audit-logging-in-ruby-and-rails.html&quot;&gt;Audit Logging in Ruby and Rails&lt;/a&gt; posts useful.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Organize Business Logic in Your Ruby on Rails Application</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/05/10/organize-business-logic-in-your-ruby-on-rails-application.html"/>
    <id>https://blog.appsignal.com/2023/05/10/organize-business-logic-in-your-ruby-on-rails-application.html</id>
    <published>2023-05-10T00:00:00+00:00</published>
    <updated>2023-05-10T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the first part of this two-part series, we&#039;ll run through popular methods to organize your business logic.</summary>
    <content type="html">&lt;p&gt;With its strong emphasis on &lt;em&gt;convention over configuration&lt;/em&gt;, Ruby on Rails has counteracted many architectural considerations that caused &lt;a href=&quot;https://en.wikipedia.org/wiki/Law_of_triviality&quot;&gt;bikeshedding&lt;/a&gt; when building web applications.&lt;/p&gt;
&lt;p&gt;Still, one area that has continuously piqued developers&amp;#39; interest is how to handle &lt;em&gt;business logic&lt;/em&gt;, i.e., code that epitomizes &amp;quot;what an app does.&amp;quot; Another way to phrase this question is: &lt;em&gt;Where do we put all the transactional code?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this first part of a two-part series, we&amp;#39;ll explore the most well-known methods to organize your business logic in a Ruby on Rails application.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;Where to Put Transactional Code in Rails&lt;/h2&gt;
&lt;p&gt;There has been an ongoing debate about this topic in the Rails community, with two extremes being advocated for, and a couple of intermediate solutions.&lt;/p&gt;
&lt;p&gt;On one hand, there&amp;#39;s the &lt;strong&gt;&amp;quot;Golden Path&amp;quot;&lt;/strong&gt;, which fully embraces MVC and is most prominently followed by &lt;a href=&quot;https://dev.37signals.com/vanilla-rails-is-plenty/&quot;&gt;37Signals&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the other hand, there is &lt;strong&gt;Domain Driven Design (DDD)&lt;/strong&gt;, practiced by companies like &lt;a href=&quot;https://shopify.engineering/shopify-made-patterns-in-our-rails-apps&quot;&gt;Shopify&lt;/a&gt;, and consultancies like &lt;a href=&quot;https://products.arkency.com/domain-driven-rails/&quot;&gt;Arkency&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These alternative approaches look pretty heavy-handed. The one you should choose depends very much on the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how many business domains your app spans (DDD)&lt;/li&gt;
&lt;li&gt;organizational structure&lt;/li&gt;
&lt;li&gt;app architecture/infrastructure (microservices/monolith, k8s/cloud, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s take a closer look at some of the familiar alternatives, and their pros and cons, starting with fat models.&lt;/p&gt;
&lt;h3&gt;Fat Models&lt;/h3&gt;
&lt;p&gt;Fat models are models equipped with methods for processing their data, possibly resulting in side effects and updating other records. Consider this example, simplified from &lt;a href=&quot;https://dev.37signals.com/vanilla-rails-is-plenty/&quot;&gt;Jorge Manrubia&amp;#39;s post about rich models&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/recording
class Recording &amp;lt; ApplicationRecord
  def copy_to(bucket, parent: nil)
    copies.create! destination_bucket: bucket, destination_parent: parent
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One problem with fat models is that unless you are really disciplined in your code hygiene, they can tend towards an assortment of code smells:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Feature Envy&lt;/strong&gt;: Models reaching out to other models, either to query their internals (a &lt;em&gt;&amp;quot;tell, don&amp;#39;t ask&amp;quot;&lt;/em&gt; violation) or to actually mutate them, are a red flag 🚩. To achieve low coupling, objects should be confident just &lt;em&gt;sending messages&lt;/em&gt; to other objects without querying them for the results.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single Responsibility Principle Violations&lt;/strong&gt;: Models encoding interactions with other models may break the rule of single responsibility. Keep rigorously asking yourself if the code you are adding to a class really belongs to the &lt;em&gt;representation of the object&lt;/em&gt; it describes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logic in Callbacks&lt;/strong&gt;: An enduring controversy, model callbacks are really a double-edged sword. A powerful tool, they can simplify a lot of imperative code, but can also hide complexity and lead to bugs that are hard to track down. Here is a rule of thumb you can follow: only use model callbacks to prepare or post-process &lt;code&gt;self&lt;/code&gt;. Never trigger any jobs, mailers, or other services from them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tendency towards God Objects&lt;/strong&gt;: Rich models act as &lt;em&gt;attractors&lt;/em&gt; for functionality. Wait a few development cycles, and I&amp;#39;ll take any bet there&amp;#39;ll be at least one model accumulating a few dozen methods. Even splitting up such an object into model concerns merely covers up the mess 🌶️.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The points listed above can be summarized as a question of &lt;em&gt;perspective&lt;/em&gt;. To paraphrase &lt;a href=&quot;http://www.clean-ruby.com&quot;&gt;Jim Gay&lt;/a&gt;, is a look from &lt;em&gt;within an individual model&lt;/em&gt; actually a good vantage point to design &lt;em&gt;what your app does&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;This has been picked up by a recent advance in Rails development: namely, service objects.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Rails Service Objects&lt;/h3&gt;
&lt;p&gt;Really another title for the &lt;a href=&quot;https://refactoring.guru/design-patterns/command&quot;&gt;Command Pattern&lt;/a&gt;, service objects encapsulate a &amp;quot;unit of work&amp;quot;, or &amp;quot;action&amp;quot;, that usually involves several steps of transactional logic. Following the above example, you would typically formulate something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/services/recording_copier.rb
class RecordingCopier
  def call(source, destination)
    source.copies.create! destination_bucket: destination
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are multiple problematic facets of this pattern, but the focal point is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This object does not encapsulate an instance state.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a sign that service objects do not actually describe any concept of our domain, as &lt;a href=&quot;https://www.codewithjason.com/rails-service-objects/&quot;&gt;Jason Swett has noticed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Others - like &lt;a href=&quot;https://avdi.codes/service-objects/&quot;&gt;Avdi Grimm&lt;/a&gt; - have observed that service objects run counter to the accepted practice of representing your domain &lt;em&gt;objects&lt;/em&gt; with &lt;em&gt;nouns&lt;/em&gt;, and sends &lt;em&gt;messages&lt;/em&gt; to these objects with &lt;em&gt;verbs&lt;/em&gt;. Classes named using the nominalization of a verb indicate that you have identified a &lt;em&gt;message&lt;/em&gt; you want to send but can&amp;#39;t come up with a &lt;em&gt;receiver&lt;/em&gt;, i.e., a matching &lt;em&gt;role&lt;/em&gt; to send it to. Objects with such fuzzy responsibilities can be the hardest to refactor.&lt;/p&gt;
&lt;p&gt;First and foremost, though, I always found this concept a bit confusing, because Rails already has a built-in primitive for this: jobs.&lt;/p&gt;
&lt;h3&gt;Jobs in Rails&lt;/h3&gt;
&lt;p&gt;In my consulting practice, I&amp;#39;ve observed that jobs are generally underused because their limits and requirements can seem daunting. Let&amp;#39;s first rewrite our example as a job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/jobs/copy_record_job.rb
class CopyRecordJob &amp;lt; ApplicationJob
  def perform(source, destination)
    source.copies.create! destination_bucket: destination
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, there&amp;#39;s almost no syntactical difference between a job and a service object. What, then, sets them apart?&lt;/p&gt;
&lt;p&gt;A couple of things, actually:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The code above isn&amp;#39;t &lt;strong&gt;idempotent&lt;/strong&gt;. If you run it twice, it will create two copies, which is probably not what you intended. Why is this important? Because most backend job processors like &lt;a href=&quot;https://sidekiq.org/&quot;&gt;Sidekiq&lt;/a&gt; don&amp;#39;t make any guarantees that your jobs will run &lt;em&gt;exactly&lt;/em&gt; once.&lt;/li&gt;
&lt;li&gt;Jobs cannot return a value or indicate when they are done out of the box (although you can do this manually, via callbacks, for example).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are several workarounds for this, like the magnificent &lt;a href=&quot;https://github.com/fractaledmind/acidic_job&quot;&gt;Acidic Job&lt;/a&gt; gem or Sidekiq Pro/Enterprise features around &lt;a href=&quot;https://github.com/sidekiq/sidekiq/wiki/Pro-Reliability-Client&quot;&gt;enhanced reliability&lt;/a&gt; and &lt;a href=&quot;https://github.com/sidekiq/sidekiq/wiki/Ent-Unique-Jobs&quot;&gt;unique jobs&lt;/a&gt;. Still, if they occur, bugs related to missing jobs and/or job idempotency are hard to track down and even harder to fix.&lt;/p&gt;
&lt;h3&gt;Event Sourcing&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Event Sourcing ensures that all &lt;strong&gt;changes to application state&lt;/strong&gt; are stored as a &lt;strong&gt;sequence of events&lt;/strong&gt;. Not just can we query these events, we can also use the event log to &lt;strong&gt;reconstruct past states.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Source: &lt;a href=&quot;https://martinfowler.com/eaaDev/EventSourcing.html&quot;&gt;Event Sourcing by Martin Fowler&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Essentially, event sourcing boils down to a publish/subscribe algorithm with integrated versioning. It&amp;#39;s a high-level Domain Driven Design concept that I will not discuss in detail here.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s not to say it&amp;#39;s not an interesting pattern. You should use it if you have advanced reporting requirements, for example. If you want to learn more about it, look at &lt;a href=&quot;https://railseventstore.org/&quot;&gt;Rails Event Store&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Up Next: DCI (Data, Context, Interaction) for Rails&lt;/h2&gt;
&lt;p&gt;We looked at a few of the most popular approaches to organize your business logic in a Ruby on Rails application: fat models, service objects, jobs, and (briefly) event sourcing.&lt;/p&gt;
&lt;p&gt;In the next and final part of this series, we will look at a convenient alternative called &lt;em&gt;DCI (Data, Context, Interaction)&lt;/em&gt;. DCI caters particularly well to the mental models we employ as engineers when we reason about application behavior.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Integrate and Troubleshoot Inbound Emails with Action Mailbox in Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/05/03/integrate-and-troubleshoot-inbound-emails-with-action-mailbox-in-rails.html"/>
    <id>https://blog.appsignal.com/2023/05/03/integrate-and-troubleshoot-inbound-emails-with-action-mailbox-in-rails.html</id>
    <published>2023-05-03T00:00:00+00:00</published>
    <updated>2023-05-03T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out how you can use Action Mailbox to integrate and troubleshoot inbound emails.</summary>
    <content type="html">&lt;p&gt;If you’ve ever looked at the Request for Comments (RFCs) around &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc5321&quot;&gt;sending and receiving emails&lt;/a&gt;, you’ll see the technical complications involved when hitting send in your email inbox.&lt;/p&gt;
&lt;p&gt;Thankfully, many existing tools provide the Simple Mail Transfer Protocol (SMTP) service for us developers — from a Postfix server you manage to a fully scalable sending service such as SendGrid, Amazon SES, or Postmark. However, moving between providers for deliverability or pricing reasons means rewriting or refactoring our apps to meet the peculiarities of each service.&lt;/p&gt;
&lt;p&gt;Rails helps us out here by providing Action Mailbox. In this post, we&amp;#39;ll dive into how you can use Action Mailbox to integrate and troubleshoot inbound emails.&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s quickly define what Action Mailbox is.&lt;/p&gt;
&lt;h2&gt;What Is Action Mailbox for Rails?&lt;/h2&gt;
&lt;p&gt;Action Mailbox uses conceptual compression for receiving email in Ruby on Rails. Conceptual compression means it encapsulates all the small differences between every SMTP service and writes your inbound processing code just once. You can even write a provider for a new service.&lt;/p&gt;
&lt;p&gt;The key concept of ActionMailbox is routing based on email recipients. By setting up an inbound email provider, mail going to a domain will be routed into your app. You can look at the recipient address to determine how each mail message should be processed.&lt;/p&gt;
&lt;p&gt;If you go through the rails conductor action to send a test email, as I have here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-05/dragged-image.png&quot; alt=&quot;Rails Conductor Screenshot&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Then your inbound email will have recipients from the &lt;code&gt;To&lt;/code&gt;, &lt;code&gt;CC&lt;/code&gt;, &lt;code&gt;BCC&lt;/code&gt;, and &lt;code&gt;X-Original-To&lt;/code&gt; fields.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;gt; inbound_email = ActionMailbox::InboundEmail.last
&amp;gt; inbound_email.mail.recipients
[&amp;quot;to@john.onrails.blog&amp;quot;, &amp;quot;cc@john.onrails.blog&amp;quot;, &amp;quot;bcc@john.onrails.blog&amp;quot;, &amp;quot;original@john.onrails.blog&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each address is tested to determine where it will be routed, but the mail message is only routed once.&lt;/p&gt;
&lt;p&gt;One key aspect of development is actually testing email from your system. Rails has a set of development pages under the routes &lt;code&gt;/rails/conductor/&lt;/code&gt; that allow you to input emails locally into your development setup.&lt;/p&gt;
&lt;p&gt;You can enter the email manually, like I did in the above example, or you can upload an email complete with all the headers.&lt;/p&gt;
&lt;p&gt;A great way to get a complete email (with headers, a message body, and attachments) is to use an email client like &lt;a href=&quot;https://www.thunderbird.net/en-GB/&quot;&gt;Thunderbird&lt;/a&gt;. Save the individual email in a &lt;code&gt;.eml&lt;/code&gt;, open the file with a text editor, and copy the complete contents into the conductor page.&lt;/p&gt;
&lt;p&gt;Now you can test more complicated email processing.&lt;/p&gt;
&lt;h2&gt;Posting and Commenting Demo for a Rails App&lt;/h2&gt;
&lt;p&gt;Let’s put together a small demo to show how this all works. I’m a great admirer of &lt;a href=&quot;https://37signals.com/&quot;&gt;37Signals&lt;/a&gt;, and I especially like their blogging with Hey World. But they don’t allow comments, so let’s create a clone that includes comments for each blog post.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/OnRailsBlog/BlogWorld&quot;&gt;Follow along with the code in this post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Create a new app (I’m using &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt;, but you can pick what makes sense to you). I’ll also add &lt;a href=&quot;https://guides.rubyonrails.org/action_text_overview.html&quot;&gt;Action Text&lt;/a&gt; for the &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;Comment&lt;/code&gt; models.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rails new BlogWorld -c tailwind
$ cd BlogWorld
$ bin/rails action_text:install
$ rails g scaffold Post title:string author:string content:rich_text
$ rails g scaffold Comment author:string content:rich_text post:references
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scaffolding gives us a quick way to see the Posts and Comments. Add the association in &lt;code&gt;post.rb&lt;/code&gt;, and the post will display related comments:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Post &amp;lt; ApplicationRecord
  has_many :comments
  has_rich_text :content
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;posts/_post.html.erb&lt;/code&gt;, let&amp;#39;s add the comments partial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;div class=&amp;quot;ml-12 pl-4 my-4 border-l-2 border-green-500&amp;quot;&amp;gt;
  &amp;lt;%= render post.comments %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have a sparse post and comment view. Set up an inbound email for posting to the blog:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ bin/rails action_mailbox:install
$ bin/rails g mailbox Post
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate the &lt;code&gt;ApplicationMailbox&lt;/code&gt;. We’ll set up a route so that anything for &lt;code&gt;blog@&lt;/code&gt; goes to our Post Mailbox and creates a post.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ApplicationMailbox &amp;lt; ActionMailbox::Base
  routing /blog@/i =&amp;gt; :post
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can test this quickly by going to &lt;code&gt;http://localhost:3000/rails/conductor/action_mailbox/inbound_emails&lt;/code&gt; and sending some emails to your service. If you send something to &lt;code&gt;blog@whatever.com&lt;/code&gt;, the email should be delivered to an inbox on our app. If you email any other address, the message will bounce.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Receive Email with Post Mailbox&lt;/h3&gt;
&lt;p&gt;Let’s set up the Post Mailbox to receive the email and post it to the blog. Each Mailbox has access to the original &lt;code&gt;inbound_email&lt;/code&gt; and &lt;code&gt;mail&lt;/code&gt; objects. The InboundEmail is a wrapper around the &lt;a href=&quot;http://github.com/mikel/mail&quot;&gt;&lt;code&gt;mail&lt;/code&gt;&lt;/a&gt; class used throughout rails.&lt;/p&gt;
&lt;p&gt;For our purposes, we’re interested in who the email is from, its subject, and its body copy. We can extract these and create a &lt;code&gt;Post&lt;/code&gt; record that will show up on our blog&amp;#39;s front page.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class PostMailbox &amp;lt; ApplicationMailbox
  def process
    Post.create title: mail[&amp;#39;subject&amp;#39;].to_s, author: mail[&amp;#39;from&amp;#39;].to_s, content: mail.body.to_s
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Send another email to your blog address and then refresh the index page. You should see the post!&lt;/p&gt;
&lt;h3&gt;Add Comments for a Post in Action Mailbox&lt;/h3&gt;
&lt;p&gt;Now to add comments for a post. First, any email commenter needs to refer to the correct post when sending an email. A simple way to do this is to encode the post ID in the inbound email address (like &lt;code&gt;comment+123@whatever.com&lt;/code&gt;, where the 123 in the email address refers to a Post element).&lt;/p&gt;
&lt;p&gt;Generate the &lt;code&gt;CommentMailbox&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ bin/rails g mailbox Comment
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a route in Action Mailbox to send any emails with &lt;code&gt;comment+123&lt;/code&gt; to the &lt;code&gt;CommentMailbox&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;routing /^comment\+\d+@/i =&amp;gt; :comment
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;_post.html.erb&lt;/code&gt;, add a link to generate the email address, so someone can open their email app and send an email:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;div class=&amp;quot;ml-12 pl-4 my-4 border-l-2 border-green-500&amp;quot;&amp;gt;
  &amp;lt;%= render post.comments %&amp;gt;
  &amp;lt;%= mail_to &amp;quot;comment+#{post.id}@whatever.com&amp;quot;, class: &amp;quot;rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium&amp;quot; %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The incoming email will be routed to the &lt;code&gt;CommentMailbox&lt;/code&gt; and parsed into a comment attached to the correct blog post.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CommentMailbox &amp;lt; ApplicationMailbox
  def process
    Comment.create author: mail[&amp;quot;from&amp;quot;].to_s, content: mail.body.to_s, post: post
  end

  def post
    return @post unless @post.nil?
    email = mail.recipients.reject { |address| address.blank? }.first
    match = email.match(/^comment\+(.*)@/i)
    token = match[1]

    begin
      if token
        @post = Post.find_by_id(token)
      else
        bounced!
      end
    rescue RecordNotFound
      bounced!
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;process&lt;/code&gt; method creates a comment from the email body and sender email. It references the &lt;code&gt;Post&lt;/code&gt; queried in the &lt;code&gt;post&lt;/code&gt; method. This method gets the first recipient&amp;#39;s email address and uses a regular expression to pull out the post ID.&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;Post&lt;/code&gt; doesn’t exist or a token can’t be parsed, the email bounces, which stops the processing.&lt;/p&gt;
&lt;p&gt;Now go to the Rails conductor form and send a comment to the address for each Post. The comment will appear underneath the post on the index page!&lt;/p&gt;
&lt;h2&gt;A More Complex Example Using Action Mailbox&lt;/h2&gt;
&lt;p&gt;Emails are actually really complicated. Imagine you&amp;#39;ve got an application monitoring tool set up, you deploy something like this to your app, and you start seeing errors in your APM dashboard.&lt;/p&gt;
&lt;p&gt;You may see a parsing error, or that posts/comments have a lot of weird formatting errors.&lt;/p&gt;
&lt;p&gt;Your app receives HTML emails, and you take the raw body source and post it to the website. The mail gem allows us to see if the incoming email has an HTML body, and we can pull whatever parts from the message we need.&lt;/p&gt;
&lt;p&gt;Let’s change the &lt;code&gt;CommentMailbox&lt;/code&gt; and &lt;code&gt;PostMailbox&lt;/code&gt; to check for multipart emails and pull out the HTML part, falling back to text if that’s the only thing left.&lt;/p&gt;
&lt;p&gt;Each email either has no parts or multiple parts. The preferred order is to see if there is an HTML part and use it, and if not, try to get and use the text part. If there aren’t parsed HTML or text sections, we’ll use the email body as before.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;PostMailbox&lt;/code&gt; is now a little more complicated:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class PostMailbox &amp;lt; ApplicationMailbox
  def process
    post = Post.new title: mail[&amp;quot;subject&amp;quot;].to_s, author: mail[&amp;quot;from&amp;quot;].to_s
    post.content = if mail.html_part
      mail.html_part.decoded
    elsif mail.text_part
      mail.text_part.decoded
    else
      mail.decoded
    end
    post.save
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;CommentMailbox&lt;/code&gt; also has a different process method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def process
  comment = Comment.new author: mail[&amp;quot;from&amp;quot;].to_s, post: post
  comment.content = if mail.html_part
    mail.html_part.decoded
  elsif mail.text_part
    mail.text_part.decoded
  else
    mail.decoded
  end
  comment.save
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can handle emails coming from someone’s phone.&lt;/p&gt;
&lt;h2&gt;Adding Action Mailbox to Your Rails App&lt;/h2&gt;
&lt;p&gt;Thanks to Action Mailbox, we can consider emails as another I/O avenue for our Rails app. We can write code independent of email service providers using conceptual compression. I’ve even been able to move email providers with minimal work since I don’t have to worry about the underlying infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;APM tools like AppSignal&lt;/a&gt; also provide a convenient dashboard to monitor all your outgoing ActionMailers and keep an eye on deliverability.&lt;/p&gt;
&lt;p&gt;Here’s an example, showing one of my apps that sends and receives lots of emails:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-05/actionmailer-dashboard.png&quot; alt=&quot;AppSignal ActionMailer Dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This gives you more visibility into what’s happening inside your app.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we first defined the capabilities of Action Mailer for Rails. We then set up a demo project where we integrated inbound emails and parsed them to create posts for a blog.&lt;/p&gt;
&lt;p&gt;I hope you&amp;#39;ve found this useful. Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Load Code in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/04/19/how-to-load-code-in-ruby.html"/>
    <id>https://blog.appsignal.com/2023/04/19/how-to-load-code-in-ruby.html</id>
    <published>2023-04-19T00:00:00+00:00</published>
    <updated>2023-04-19T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s look into three options for code loading in Ruby: using load, require, and autoload.</summary>
    <content type="html">&lt;p&gt;There are many ways to load code and access file-related constants in Ruby. We can create a clear architecture by separating and handling concerns into classes and pulling in only the classes we depend on.&lt;/p&gt;
&lt;p&gt;Many full-stack frameworks like Rails and Hanami offer a built-in method to access the classes we want, as long as we stick with a certain convention. How does this work?&lt;/p&gt;
&lt;p&gt;In this post, we will explore three different options for loading code: using &lt;code&gt;load&lt;/code&gt;, &lt;code&gt;require&lt;/code&gt;, and &lt;code&gt;autoload&lt;/code&gt;. We will also look into the Ruby gem &lt;code&gt;Zeitwerk&lt;/code&gt;. &lt;code&gt;Zeitwerk&lt;/code&gt; is the default code-loading mechanism for many new projects, and a lot of established projects are switching to this great gem. Rails and Hanami both utilize &lt;code&gt;Zeitwerk&lt;/code&gt; as their code-loading tool.&lt;/p&gt;
&lt;p&gt;Before we dive into the code loading options on offer in Ruby, let&amp;#39;s take a quick look at &lt;code&gt;$LOAD_PATH&lt;/code&gt; to get to grips with how code loading works.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;$LOAD_PATH&lt;/code&gt; in Ruby&lt;/h2&gt;
&lt;p&gt;Before we dive deeper into this topic, let&amp;#39;s summarize the &lt;code&gt;$LOAD_PATH&lt;/code&gt; in Ruby, as it is essential in understanding how loading works.&lt;/p&gt;
&lt;p&gt;The global variable &lt;code&gt;$LOAD_PATH&lt;/code&gt; or shorter &lt;code&gt;$:&lt;/code&gt; references all directories with Ruby source files registered in an array. So when you prepend &lt;code&gt;bundle exec&lt;/code&gt; with your program name, Ruby adds all the program gems to the load path.&lt;/p&gt;
&lt;p&gt;Then, when we load or require a file, Ruby iterates over this array and searches for the file name. This can add significant time to the booting process, as we might have to search for a given file in many places on the hard disk.&lt;/p&gt;
&lt;p&gt;Keeping that in mind, we&amp;#39;ll now explore some Ruby code-loading options in detail.&lt;/p&gt;
&lt;h2&gt;Options for Loading Code in Ruby&lt;/h2&gt;
&lt;p&gt;We have three main options for loading code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;load&lt;/code&gt; method. This loads and parses a Ruby program in a specified file every time you call the method.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;require&lt;/code&gt; method. With this, we load and parse a given file only once.&lt;/li&gt;
&lt;li&gt;The third option is &lt;code&gt;autoload&lt;/code&gt;. We declare upfront that Ruby should require a specified file when we use a constant that doesn&amp;#39;t exist yet.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;code&gt;load&lt;/code&gt; a File in Ruby&lt;/h2&gt;
&lt;p&gt;As mentioned, with &lt;code&gt;load&lt;/code&gt; we parse and execute a Ruby file.
If the filename we set as an argument is a relative path, i.e., it starts with &lt;code&gt;./&lt;/code&gt; or &lt;code&gt;../&lt;/code&gt;, the file will be loaded relative to the current working directory.&lt;/p&gt;
&lt;p&gt;Remember: This doesn&amp;#39;t mean it&amp;#39;s relative to the file where the load is located — it&amp;#39;s instead relative to the file that started the Ruby process. If you want to know what the working directory is, run &lt;code&gt;Dir.pwd&lt;/code&gt;. Otherwise, Ruby searches for a file with the given name in the &lt;code&gt;$LOAD_PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see how load works:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# foo.rb
$x = 4 # define global Variable

class Foo
  def bar
    puts &amp;quot;I&amp;#39;m doing it now&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we load the file with &lt;code&gt;load &amp;quot;foo.rb&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What happens when we load this file? The global variable &lt;code&gt;$x&lt;/code&gt; is created and set to the integer 4. The constant &lt;code&gt;Foo&lt;/code&gt; is also created, and we can instantiate an object from it. So far, this seems just like the more familiar &lt;code&gt;require&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;But what happens when we &lt;code&gt;load&lt;/code&gt; this file again? If we have changed our global value &lt;code&gt;$x&lt;/code&gt;, it resets to 4.&lt;/p&gt;
&lt;p&gt;In older Ruby versions, we also might get a warning that the constant &lt;code&gt;Foo&lt;/code&gt; is being redefined.&lt;/p&gt;
&lt;p&gt;Another problem with &lt;code&gt;load&lt;/code&gt; is that every reload takes some time to run as the Ruby VM is &lt;code&gt;eval&lt;/code&gt;-ing the file. &lt;code&gt;require&lt;/code&gt;, on the other hand, loads and parses a file just once, during the first run.&lt;/p&gt;
&lt;h3&gt;Wrapping the File with &lt;code&gt;load&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Right now, every time we load or require a file, we pollute the global namespace. To avoid this, we can use the &lt;code&gt;load&lt;/code&gt; method with the wrap parameter set to &lt;code&gt;true&lt;/code&gt;. This will create an anonymous module where all the constants and methods defined in a loaded file are placed. Here is an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# file to be loaded foo.rb

class Foo
  puts self.name # =&amp;gt; Here we will see where our Foo class is located in
  def bar
    puts &amp;quot;I&amp;#39;m doing it now&amp;quot;
  end
end

###############################
# Now in another file run this:

load &amp;quot;foo.rb&amp;quot;, true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run this, Ruby will output something like this: &lt;code&gt;#&amp;lt;Module:0x00007fa3430682e8&amp;gt;::Foo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Why is this useful? For one thing, we are not polluting the global namespace. But since Ruby creates an anonymous module, we can only access the stuff we load with some trickery.&lt;/p&gt;
&lt;p&gt;Nevertheless, wrapping the file is still useful if we want to configure something in our application or set some things up. This way, we can achieve what we want without leaving anything behind.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Accessing the Wrapped File Constants&lt;/h3&gt;
&lt;p&gt;There are two ways to access the constants we create in the wrapped file. The first and most obvious is to set a module name as the argument for the wrap parameter.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Foo
  def bar
    puts &amp;quot;I&amp;#39;m doing it now&amp;quot;
  end
end

###############################
# Now in another file run this:

module Parent
end
load &amp;quot;./code_loading/file.rb&amp;quot;, Parent

Parent::Foo.new
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is how we can have access to the anonymous Ruby module:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Foo
  def bar
    puts &amp;quot;I&amp;#39;m doing it now&amp;quot;
  end
end

throw :wrapper, Module.nesting.last


############################################
# When we load the file we catch the  error
# and make the module accessible

mod  = catch :wrapper do
  load &amp;quot;./code_loading/file.rb&amp;quot;, true
end

mod::Foo.new
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is a hidden problem with &lt;code&gt;wrap&lt;/code&gt;. If the author of the loaded file does something like this, it will break the sandbox:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ::Foo
  # stuff
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;Foo&lt;/code&gt; is in the global namespace, and there is no way to prevent this pollution.&lt;/p&gt;
&lt;h2&gt;Ruby Code Loading with &lt;code&gt;require&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Just like the &lt;code&gt;load&lt;/code&gt; method, &lt;code&gt;require&lt;/code&gt; is a method in the &lt;code&gt;Kernel&lt;/code&gt; module. The big difference between these two methods is how they behave on multiple calls.&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;load&lt;/code&gt; always reevaluates a file, &lt;code&gt;require&lt;/code&gt; doesn&amp;#39;t. Its response is also different. If you call &lt;code&gt;require&lt;/code&gt; for the first time and the method actually loads something, it returns &lt;code&gt;true&lt;/code&gt;, otherwise, it returns &lt;code&gt;false&lt;/code&gt;. &lt;code&gt;load&lt;/code&gt;, on the other hand, always returns &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Some pseudo-code for &lt;code&gt;require&lt;/code&gt; would look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def require(file_path)
  if !already_loaded_files? file_path
    eval(File.read(find_file_in_load_path(file_path)))
    mark_file_as_loaded! file_path
    true
  else
    false
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;require&lt;/code&gt; accepts absolute paths for files as well as just a simple name. If we need a simple file, &lt;code&gt;require&lt;/code&gt; also searches in the &lt;code&gt;$LOAD_PATH&lt;/code&gt;. This, of course, means that we still have the same disadvantages we had with &lt;code&gt;load&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Simpler File Searching with &lt;code&gt;require_relative&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;require_relative&lt;/code&gt; always looks for a file that is relative to the current file you are working on. &lt;code&gt;require_relative&lt;/code&gt; doesn&amp;#39;t iterate over a directory, and no real search is involved. Ruby looks into the path specified and tries to load the file. If it isn&amp;#39;t there, we get a &lt;code&gt;LoadError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, as of now, &lt;code&gt;require&lt;/code&gt; doesn&amp;#39;t have a &lt;code&gt;wrap&lt;/code&gt; parameter like &lt;code&gt;load&lt;/code&gt; does, so &lt;code&gt;require&lt;/code&gt; always pollutes the global namespace. But there has been &lt;a href=&quot;https://bugs.ruby-lang.org/issues/10320&quot;&gt;some recent discussion on the Ruby issue tracker&lt;/a&gt; to add something like &lt;code&gt;wrap&lt;/code&gt; to the &lt;code&gt;require&lt;/code&gt; method.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;autoload&lt;/code&gt; Code Loading in Ruby&lt;/h2&gt;
&lt;p&gt;Our third option for code loading in Ruby is &lt;code&gt;autoload&lt;/code&gt;. With &lt;code&gt;autoload&lt;/code&gt;, we tell the Ruby VM upfront what constants might be accessed and where to find and load them.&lt;/p&gt;
&lt;p&gt;A big advantage of this approach is that we only load the files we really need. Therefore, the booting process can be really fast as we just pay for what we use. When we actually need the constant, &lt;code&gt;autoload&lt;/code&gt; requires the file with the method &lt;code&gt;require&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s how we set up an autoload:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;### lib/services/foo.rb
module Services
  class Foo
    def bar
      puts &amp;quot;bar&amp;quot;
    end
  end
end

### lib/services.rb
module Services
  autoload :Foo, &amp;quot;#{__dir__}/services/foo.rb&amp;quot;

  # Now we have access to the Foo constant in this module
  # and we will load the file when we actually need it.

  # Foo.new.bar =&amp;gt; loads the file and prints &amp;quot;bar&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most popular gem for managing loading files is &lt;code&gt;Zeitwerk&lt;/code&gt;, and it works with &lt;code&gt;autoload&lt;/code&gt;. Now let&amp;#39;s see how Zeitwerk works and how to set it up.&lt;/p&gt;
&lt;h2&gt;Code Loading with Zeitwerk&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/fxn/zeitwerk&quot;&gt;Zeitwerk&lt;/a&gt; takes a directory and makes every file underneath it available to load. The convention is that every new sub-directory is a new module, and every file defines a class with the same name as the file.&lt;/p&gt;
&lt;p&gt;Zeitwerk offers an option for code reloading that is useful during development. Rails uses this mode in an active development environment.&lt;/p&gt;
&lt;p&gt;How does it work under the hood? Here is some &lt;em&gt;very, very&lt;/em&gt; simplified pseudo-code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Zeitwerk
  class Loader
    def initialize()
      @root_dir = []
    end

    def push_dir(dir)
      @root_dirs &amp;lt;&amp;lt; dir
    end

    def setup
      # scan root dirs and define autoloads for each file
      @root_dirs.each do |dir|
        Dir.foreach(dir) do |file|
          next if file == &amp;quot;.&amp;quot; || file == &amp;quot;..&amp;quot;

          autoload file.class_name.to_sym, File.expand_path(file, dir)
        end
      end
    end
  end
end

loader = Zeitwerk::Loader.new
loader.push_dir(__dir__)
loader.setup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, with the last call to &lt;code&gt;loader.setup&lt;/code&gt;, we get access to every constant defined in one of the files relative to the current file.
Of course, this simplified code doesn&amp;#39;t highlight many of the other great features that &lt;code&gt;Zeitwerk&lt;/code&gt; has, like eager loading and handling namespaces. That&amp;#39;s outside of the scope of this post, and I&amp;#39;ll leave it to you to dig deeper and explore all &lt;code&gt;Zeitwerk&lt;/code&gt; has to offer.&lt;/p&gt;
&lt;h3&gt;How to Set Up &lt;code&gt;Zeitwerk&lt;/code&gt; with Roda&lt;/h3&gt;
&lt;p&gt;Since Rails and Hanami use &lt;code&gt;Zeitwerk&lt;/code&gt; as their default, let&amp;#39;s see how we can configure &lt;code&gt;Zeitwerk&lt;/code&gt; for Roda:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;### in config.ru

loader = Zeitwerk::Loader.new
loader.push_dir(__dir__)
loader.push_dir(&amp;quot;#{__dir__}/lib&amp;quot;)

if ENV[&amp;#39;RACK_ENV&amp;#39;] == &amp;#39;production&amp;#39;
  loader.setup
  Zeitwerk::Loader.eager_load_all
  run App
else
  loader.enable_reloading
  loader.setup
  run -&amp;gt;(env) {
    loader.reload
    App.call(env)
  }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we configure our app like this, we have code reloading during development and eager loading in production. This way, the boot time during development is fast, as only the files that are immediately needed are loaded. In production, we load everything upfront and have a faster response time.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we first took a quick look at &lt;code&gt;$LOAD_PATH&lt;/code&gt; in Ruby to lay the foundations of how code loading works. We then explored three options to load code in some detail: &lt;code&gt;load&lt;/code&gt;, &lt;code&gt;require&lt;/code&gt;, and &lt;code&gt;autoload&lt;/code&gt; (which works well with the &lt;code&gt;Zeitwerk&lt;/code&gt; gem).&lt;/p&gt;
&lt;p&gt;So, how should we load our code? Most of the time, we should use &lt;code&gt;require&lt;/code&gt; and &lt;code&gt;Zeitwerk&lt;/code&gt; to automate that. If you have special requirements and want to put loaded code into a namespace, the &lt;code&gt;load&lt;/code&gt; method with a wrap parameter is a good option.&lt;/p&gt;
&lt;p&gt;Happy code loading!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Audit Logging in Ruby and Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/04/12/audit-logging-in-ruby-and-rails.html"/>
    <id>https://blog.appsignal.com/2023/04/12/audit-logging-in-ruby-and-rails.html</id>
    <published>2023-04-12T00:00:00+00:00</published>
    <updated>2023-04-12T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore some options to implement audit logs, including PaperTrail, Audited, AuditLog, AppSignal, and a custom implementation.</summary>
    <content type="html">&lt;p&gt;An audit log is a documented record of events and activity within an information technology system. Audit logging is critical for application owners and administrators to track activity within their systems.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll first dive into what auditing entails and what to consider when audit logging. We&amp;#39;ll then explore some options for implementing audit logs, including PaperTrail, Audited, AuditLog, AppSignal, and a custom implementation.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Auditing and Audit Logs: An Introduction&lt;/h2&gt;
&lt;p&gt;Auditing provides an understanding of system behavior using verifiable, well-defined events. It has a different goal than application logging, which records general-purpose messages often used to diagnose or troubleshoot issues within an application.&lt;/p&gt;
&lt;p&gt;Auditing is typically a foundational requirement for many systems, yet it is often one of the last concerns of development teams. The use of simple, effective patterns upfront can change this trend.&lt;/p&gt;
&lt;p&gt;One motivation for teams to tackle this capability early on is the feedback it provides in terms of metrics for testing and evaluating systems. The observability of audit events can highlight system issues or even missed requirements.&lt;/p&gt;
&lt;p&gt;Audit logs typically have a defined structure that includes the following event information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;li&gt;Event type&lt;/li&gt;
&lt;li&gt;Description&lt;/li&gt;
&lt;li&gt;User or system process that initiated or requested the event&lt;/li&gt;
&lt;li&gt;Impacted entities in the system&lt;/li&gt;
&lt;li&gt;Any additional relevant detail&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any system, service, or device can have its own audit log. A collection of audit logs spanning a particular dimension is considered an audit trail of related activity. An audit trail could be specific to a user, application, or system entity. For example, the &lt;a href=&quot;https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-user-guide.html&quot;&gt;AWS CloudTrail&lt;/a&gt; service records all activity within an AWS account.&lt;/p&gt;
&lt;p&gt;Audit logs support, among others, the following key functions and use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compliance and regulatory requirements&lt;/li&gt;
&lt;li&gt;Security reviews and investigations of potential security breaches&lt;/li&gt;
&lt;li&gt;Determining what users make particular changes in a system and when&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Design Considerations for Audit Capabilities&lt;/h2&gt;
&lt;p&gt;Audit logging solutions typically involve a centralized log management system. Each application instance can directly send audit events to the centralized repository, or the solution may include agents running on compute nodes that facilitate this communication.&lt;/p&gt;
&lt;p&gt;Distributed architectures can make auditing challenging because there are many sources of audit events throughout the infrastructure. Centralized solutions aim to funnel all of the disparate audit events through a central pipeline for processing.&lt;/p&gt;
&lt;p&gt;Audit management systems provide storage and search capabilities for the collection of audit events. There are several persistence options, although many Ruby and Rails-based solutions use database persistence to store audit events as a natural extension of ActiveRecord capabilities.&lt;/p&gt;
&lt;p&gt;As an alternative, cloud data streaming services can also be used. For example, &lt;a href=&quot;https://aws.amazon.com/kinesis/&quot;&gt;AWS Kinesis&lt;/a&gt; is designed to handle extremely large volumes of streaming data and is well suited for the audit use case. Another option is to store audit data in &lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;AWS S3 buckets&lt;/a&gt; and then query it using &lt;a href=&quot;https://aws.amazon.com/athena&quot;&gt;AWS Athena&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What Constitutes an Audit Event in Ruby and Rails?&lt;/h2&gt;
&lt;p&gt;Countless events occur within a system, so one of the most important decisions is determining what exactly to audit. Audit events can be based on functional requirements (e.g., a user submits an order) or non-functional requirements (e.g., a security-relevant event occurs, such as a failed login attempt). Both types are important and serve different purposes. Certainly, security auditing is of foremost concern in today’s operating environment.&lt;/p&gt;
&lt;p&gt;The level of detail is another key consideration. A single business transaction may involve updates to multiple database tables. While a change in an individual database entity could be the subject of an audit event, by itself this may not contain enough information about the overarching event taking place. It is often beneficial to audit the business function in addition to, or even in place of, individual database rows. Business events typically occur within the service or controller level code.&lt;/p&gt;
&lt;p&gt;Some use cases require all of the detail you have. For example, requirements may dictate that all user HTTP requests must be audited. You may need to know which users queried for particular information in comprehensive security audits. As is often the case in software engineering, let your requirements be your guide in making these design decisions.&lt;/p&gt;
&lt;h2&gt;Ruby Audit Log Implementation Options&lt;/h2&gt;
&lt;p&gt;Most Ruby gems in this space are focused on the Rails use case, as it has ORM capabilities to identify changes to ActiveRecord instances and store them as audit events. This enables basic audit capabilities without much development effort. Keep in mind, however, the level of detail of your audit events. You may need to add code to specifically audit business events in your service or controller classes.&lt;/p&gt;
&lt;p&gt;AppSignal also has a new &lt;a href=&quot;https://www.appsignal.com/tour/log-management&quot;&gt;logging feature&lt;/a&gt; as part of its integration for Ruby. Applications can log messages and structured event data from their applications which are stored on managed AppSignal servers. Once you&amp;#39;ve signed up, you can use AppSignal dashboards to search, filter, and view your logs.&lt;/p&gt;
&lt;h3&gt;PaperTrail&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/paper-trail-gem/paper_trail&quot;&gt;PaperTrail&lt;/a&gt; gem is a popular choice for audit logging in Ruby. It is focused on tracking changes to ActiveRecord model classes using a versions table. It allows you to see how a model looks at any point in its lifecycle. Several configuration options determine what type of model changes result in creating an audit event, or version. For example, you can choose to audit all changes or only create audit events where designated attributes are modified.&lt;/p&gt;
&lt;p&gt;If you have an application that requires maintaining a history of changes over time, you will find this gem particularly useful. There is a good deal of overlap between auditing and versioning capabilities, and PaperTrail provides a nice solution for both. A &lt;code&gt;versions&lt;/code&gt; method is added to each model class.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;You can easily see the changes between versions as well, as shown in &lt;a href=&quot;https://github.com/paper-trail-gem/paper_trail#3c-diffing-versions&quot;&gt;this code snippet from the PaperTrail documentation&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;widget = Widget.create name: &amp;#39;Bob&amp;#39;
widget.versions.last.changeset # reads object_changes column
# {
#   &amp;quot;name&amp;quot;=&amp;gt;[nil, &amp;quot;Bob&amp;quot;],
#   &amp;quot;created_at&amp;quot;=&amp;gt;[nil, 2015-08-10 04:10:40 UTC],
#   &amp;quot;updated_at&amp;quot;=&amp;gt;[nil, 2015-08-10 04:10:40 UTC],
#   &amp;quot;id&amp;quot;=&amp;gt;[nil, 1]
# }
widget.update name: &amp;#39;Robert&amp;#39;
widget.versions.last.changeset
# {
#   &amp;quot;name&amp;quot;=&amp;gt;[&amp;quot;Bob&amp;quot;, &amp;quot;Robert&amp;quot;],
#   &amp;quot;updated_at&amp;quot;=&amp;gt;[2015-08-10 04:13:19 UTC, 2015-08-10 04:13:19 UTC]
# }
widget.destroy
widget.versions.last.changeset
# {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As long as your controller has a &lt;code&gt;current_user&lt;/code&gt; method, it will track who is responsible for changes using the following callback:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ApplicationController
  before_action :set_paper_trail_whodunnit
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One drawback is the lack of a well-defined pattern for custom audit events. A workaround is to create a database entity that captures relevant audit information, although that somewhat defeats the purpose of this gem’s paradigm.&lt;/p&gt;
&lt;h3&gt;Audited&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/collectiveidea/audited&quot;&gt;Audited&lt;/a&gt; gem is another implementation similar in scope to PaperTrail. It now only focuses on ActiveRecord support. An opt-in approach is used to denote which model classes should be audited, as shown below.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ActiveRecord::Base
  audited
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Model classes get a &lt;code&gt;revisions&lt;/code&gt; method similar to PaperTrail’s &lt;code&gt;versions&lt;/code&gt;. Audited models also get an &lt;code&gt;audits&lt;/code&gt; method that provides change-tracking information.&lt;/p&gt;
&lt;p&gt;However, this choice suffers from the same drawback as PaperTrail: a lack of support for custom audit events.&lt;/p&gt;
&lt;h3&gt;AuditLog&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rails-engine/audit-log&quot;&gt;AuditLog&lt;/a&gt; takes a different approach. Rather than centering the design around model classes, it uses explicit calls to create audit events, as shown in the code below.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;AuditLog.audit!(:update_password, @user, payload: { ip: request.remote_ip })
AuditLog.audit!(:sign_in, @user, payload: { ip: request.remote_ip })
AuditLog.audit!(:create_address, nil, payload: params)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to audit individual model entities, you can use code similar to the following to accomplish this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class TicketsController &amp;lt; ApplicationController
  def index
    audit! :list_tickets, nil
  end

  def create
    if @ticket.save
      audit! :create_ticket, @ticket, payload: ticket_params
    else
      render :new
    end
  end

  def update
    if @ticket.save
      audit! :update_ticket, @ticket, payload: ticket_params
    else
      render :edit
    end
  end
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PaperTrail and Audited can do this automatically for you. However, in this code sample, you can also see the use of action-based auditing in the index method. This equates to an audit event capturing a view tickets operation.&lt;/p&gt;
&lt;p&gt;AuditLog also provides a simple web interface to view audit information. While your operational requirements may dictate a more comprehensive reporting capability, this is a nice feature to have right out of the box.&lt;/p&gt;
&lt;h3&gt;Audit Logging with AppSignal&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.appsignal.com/tour/log-management&quot;&gt;AppSignal provides a managed logging mechanism&lt;/a&gt; that offloads the storage of log data and provides a comprehensive set of search and filter capabilities. Each application has a default log source, or you can create additional sources as shown below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-04/new-log-source.png&quot; alt=&quot;Image of web form to create a new logging source&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The following code shows an example of creating an audit log event from a sample blog application. The logger constructor takes a group name as a parameter that can be used to identify event sources. This code example comes from an articles controller class where new articles are created.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;logger = Appsignal::Logger.new(&amp;quot;articles&amp;quot;)
logger.info(&amp;quot;Created new article #{@article.id}&amp;quot;, { article_id: @article.id, date: DateTime.now })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AppSignal dashboards can be used to view log data, both at the aggregate level as well as detailed audit log information.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-04/view-audit-logs.png&quot; alt=&quot;Image of AppSignal logging dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;You can drill down into specific log events and view the structured data included with the event, as shown below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-04/audit-log-details.png&quot; alt=&quot;Image of AppSignal detailed log event&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Custom Audit Logging Implementation&lt;/h3&gt;
&lt;p&gt;You can also roll your own implementation if you have specific requirements that don’t fit one of these solutions. In most cases, you will need to implement additional reporting capabilities using the stored audit data.&lt;/p&gt;
&lt;p&gt;For this, you may choose to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use another Ruby gem.&lt;/li&gt;
&lt;li&gt;Push the data to a data warehouse or cloud service for analysis and long-term storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Choosing an Audit Logging Implementation&lt;/h2&gt;
&lt;p&gt;Here are a few key considerations to help you choose between audit solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What types of events do you need to audit?&lt;/strong&gt; Are they primarily database-driven events? If so, a solution like PaperTrail is a great choice. If you have higher-level business events, then you need support for custom audit events similar to the pattern implemented by AuditLog.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do gems with higher activity levels give you more confidence?&lt;/strong&gt; If so, PaperTrail and Audited are two great choices. AuditLog provides more flexibility, but it has less of an installed user base.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What volume of audit events do you need to capture?&lt;/strong&gt; Will the storage of audit events in the same application database add significant overhead to your application? If so, consider using AppSignal for minimal impact to your application&amp;#39;s performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remember that you also need query and reporting capabilities.&lt;/strong&gt; The out-of-the-box capability here varies widely across the different options. AppSignal provides a comprehensive search and filter capability over log data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Audit logging is a fundamental capability for almost all business applications. We&amp;#39;ve seen that it is beneficial to establish a design pattern early on for the observability of a system as development progresses.&lt;/p&gt;
&lt;p&gt;There are some great implementations available for Ruby and Rails developers: PaperTrail, Audited, AuditLog, and AppSignal. Evaluate your requirements using the criteria discussed above to determine the best choice for your needs.&lt;/p&gt;
&lt;p&gt;Happy audit logging!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Use the rodauth-omniauth Gem in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/04/05/how-to-use-the-rodauth-omniauth-gem-in-ruby.html"/>
    <id>https://blog.appsignal.com/2023/04/05/how-to-use-the-rodauth-omniauth-gem-in-ruby.html</id>
    <published>2023-04-05T00:00:00+00:00</published>
    <updated>2023-04-05T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Explore how to set up the rodauth-omniauth gem for your Rails application.</summary>
    <content type="html">&lt;p&gt;Setting up authentication for your Ruby app is important to ensure it is secure. In this post, we&amp;#39;ll explore the &lt;code&gt;rodauth-omniauth&lt;/code&gt; gem for Ruby to implement authentication in your app via third-party providers.&lt;/p&gt;
&lt;p&gt;First, let&amp;#39;s quickly define authentication before exploring the benefits of OmniAuth and setting up the rodauth-omniauth gem for a Rails app.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive straight in!&lt;/p&gt;
&lt;h2&gt;What Is Authentication?&lt;/h2&gt;
&lt;p&gt;Authentication is the act of verifying that someone or something is who or what they say they are to grant them access to requested resources. So if user &lt;code&gt;A&lt;/code&gt; wants access to an application, they have to provide identification that is acceptable and confirmable by the system to be granted access.&lt;/p&gt;
&lt;p&gt;Authentication can take many forms — for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Providing a password that matches an account&amp;#39;s password as stored in an app&amp;#39;s database.&lt;/li&gt;
&lt;li&gt;Using facial recognition or fingerprints.&lt;/li&gt;
&lt;li&gt;The use of a code sent to a registered channel, like an email or phone number, or an authenticator app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are various descriptions of authentication — we have &lt;code&gt;single-factor authentication&lt;/code&gt;, &lt;code&gt;two-factor authentication&lt;/code&gt;, and &lt;code&gt;multi-factor authentication&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;What Does OmniAuth Bring to the Table?&lt;/h2&gt;
&lt;p&gt;Authentication can be tiring for a user, especially when they have to constantly remember their various usernames and passwords for various applications. One of the solutions &lt;a href=&quot;https://oauth.net/&quot;&gt;OAuth&lt;/a&gt; (Open Authentication) provides is to allow one service provider to authenticate a user using their identity with another service provider. This means that as a user, you only need to remember one username and password. Then every other application you need access to can delegate your authentication to your preferred OAuth provider.&lt;/p&gt;
&lt;p&gt;Also, you do not need to give your password to the application that requires authentication. Instead, you are redirected to your provider, where you can be authenticated, and then redirected to the application you intend to access (thereby keeping your credentials safe and secure). Your provider, on the other hand, then shares the relevant information about you with the requesting application. Some common OAuth service providers are  Google, Facebook, Twitter, and GitHub.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/omniauth/omniauth&quot;&gt;OmniAuth&lt;/a&gt; is a library that standardizes multi-provider authentication for web applications. It uses a strategy pattern for the different providers, and these strategies are generally released individually as Ruby gems. The &lt;code&gt;rodauth-omniauth&lt;/code&gt; gem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Offers login and registration via multiple external providers using OmniAuth, together with the persistence of external identities.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://github.com/janko/rodauth-omniauth&quot;&gt;rodauth-omniauth GitHub repo&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Setting Up rodauth-omniauth for a Rails Application&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s start by creating a new Rails project titled &amp;quot;rodauth_test_app&amp;quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails new rodauth_test_app
$ cd rodauth_test_app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is important to note that the &lt;a href=&quot;https://github.com/janko/rodauth-omniauth&quot;&gt;rodauth-omniauth&lt;/a&gt; gem is an extension to the &lt;a href=&quot;https://github.com/jeremyevans/rodauth&quot;&gt;rodauth&lt;/a&gt; gem, and as such, we need to have already set up Rodauth to use it. For a Rails app, a quick way to do that is to run the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add rodauth-rails
$ rails generate rodauth:install
$ rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To have access to the views &lt;code&gt;rodauth&lt;/code&gt; provides, you can run either of the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails g rodauth:views # to have access to all the views that rodauth provides
$ rails g rodauth:views login # to access only the login page which is what we need
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;rails rodauth:routes&lt;/code&gt; command gives us a list of the routes handled by the RodauthApp, making it possible for us to get to the different pages we&amp;#39;re interested in.&lt;/p&gt;
&lt;p&gt;Among the tables created during our installation, the most important one to us is the &lt;code&gt;Accounts&lt;/code&gt; table. This is the table where all user accounts are stored and will be the reference for the &lt;code&gt;AccountIdentities&lt;/code&gt; table we will create.&lt;/p&gt;
&lt;h3&gt;Creating an Account Identities Table&lt;/h3&gt;
&lt;p&gt;A person can have a driver&amp;#39;s license, international passport, and voter&amp;#39;s card. All these means of identification point to the same person and can be used interchangeably.&lt;/p&gt;
&lt;p&gt;Similarly, a user can have several means of identification (account identities), and this user may choose to log in at any time using any of these identities. That is why we&amp;#39;ll create an &lt;code&gt;AccountIdentities&lt;/code&gt; table to store all the identities of a specific user account, identified by a unique email address.&lt;/p&gt;
&lt;p&gt;With a quick look in our &lt;code&gt;db&lt;/code&gt; folder at the &lt;code&gt;..._create_rodauth.rb&lt;/code&gt; file, we find the schema for the accounts table as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;create_table :accounts do |t|
  t.integer :status, null: false, default: 1
  t.string :email, null: false
  t.index :email, unique: true, where: &amp;quot;status IN (1, 2)&amp;quot;
  t.string :password_hash
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To create a table to store our account identities, we run the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails g migration CreateAccountIdentities
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We find the generated file in the &lt;code&gt;db/migrate&lt;/code&gt; folder. Let&amp;#39;s go ahead and define its schema.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CreateAccountIdentities &amp;lt; ActiveRecord::Migration[7.0]
  def change
    create_table :account_identities do |t|
      t.references :account, null: false, foreign_key: { on_delete: :cascade }
      t.string :provider, null: false
      t.string :uid, null: false
      t.index [:provider, :uid], unique: true
      # timestamps are not included -&amp;gt; to be explained later
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within this table, we ensure the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every identity must belong to an account.&lt;/li&gt;
&lt;li&gt;When an account is deleted, all associated identities also get deleted.&lt;/li&gt;
&lt;li&gt;The provider must be declared (e.g., Google, Twitter, Facebook, GitHub).&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;uid&lt;/code&gt; must be present.&lt;/li&gt;
&lt;li&gt;The combination of the provider and the &lt;code&gt;uid&lt;/code&gt; must be unique to ensure that all account identities are unique for each provider.&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;It is also very important to have a homepage — a root route — to redirect users to after they are successfully authenticated. To ensure that, create a home controller with an index method that serves as the homepage.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails g controller home index
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our &lt;code&gt;config/routes.rb&lt;/code&gt; file, we add the root route as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;root &amp;quot;home#index&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Setting the OAuth Provider&lt;/h3&gt;
&lt;p&gt;We&amp;#39;ll use GitHub as the OAuth provider for this app, so install the GitHub OAuth gem and the rodauth-omniauth gem.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add rodauth-omniauth omniauth-github
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Having installed these gems, enable the omniauth feature and register your OAuth provider in the Rodauth configuration. The config file can be found at &lt;code&gt;app/misc/rodauth_main.rb&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;configure do
# ...
  enable :omniauth
  omniauth_provider :github, ENV[&amp;quot;GITHUB_CLIENT_ID&amp;quot;], ENV[&amp;quot;GITHUB_CLIENT_SECRET&amp;quot;], name: :github
  # name is only important here if it&amp;#39;s different from the provider e.g. :google_oauth2 with name: :google
# ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get the client id and client secret for this app, we need to &lt;a href=&quot;https://github.com/settings/developers&quot;&gt;create the OAuth app&lt;/a&gt; and set the callback URL to &lt;code&gt;{root_url}/auth/{provider}/callback&lt;/code&gt;. In our case, this translates to &lt;code&gt;https://localhost:3000/auth/github/callback&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point, we&amp;#39;re all set to start our server and visit our homepage.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Visiting &lt;code&gt;/login&lt;/code&gt;, as confirmed from our rodauth routes, leads to the login page. It is on this page that we add our &lt;code&gt;Login via GitHub&lt;/code&gt; button.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/views/rodauth/_login_form_footer.html.erb
# ...
  &amp;lt;li&amp;gt;&amp;lt;%= button_to &amp;quot;Login via GitHub&amp;quot;, rodauth.omniauth_request_path(:github), method: :post, data: { turbo: false } %&amp;gt;&amp;lt;/li&amp;gt;
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Authentication and Redirection with Rodauth&lt;/h3&gt;
&lt;p&gt;On clicking the &lt;code&gt;Login via GitHub&lt;/code&gt; button, we are redirected to the GitHub authentication page, assuming that the providers are properly configured. On this page, we are required to provide some details for authentication and after a successful authentication, we&amp;#39;re redirected to the root URL of our application.&lt;/p&gt;
&lt;p&gt;Between authentication and redirection to the homepage, Rodauth does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creates an account, along with an account identity, if no account with that email previously existed.&lt;/li&gt;
&lt;li&gt;Assigns an identity to an account if an account with that email already exists.&lt;/li&gt;
&lt;li&gt;Sets the status of a newly created account to &amp;quot;verified&amp;quot;, because it assumes that the external provider verified the email address.&lt;/li&gt;
&lt;li&gt;Returns an error response during the callback phase if an account associated with the external identity already exists and is unverified (e.g., it is created through normal registration), as only verified accounts can be logged into.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Accessing Account Identities&lt;/h3&gt;
&lt;p&gt;With &lt;code&gt;rodauth-omniauth&lt;/code&gt; version 0.3.3 and above, you can access the identities related to an account without any further setup, as shown below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;gt; Account.first
=&amp;gt; # &amp;lt;Account:0x00007f236b455a70 id: 1, status: &amp;quot;verified&amp;quot;, email: &amp;quot;biodun9@gmail.com&amp;quot;, password_hash: nil&amp;gt;

&amp;gt; Account.first.identities
=&amp;gt; [#&amp;lt;Account::Identity:0x00007fe90a0612d8
  id: 1,
  account_id: 2,
  provider: &amp;quot;github&amp;quot;,
  uid: &amp;quot;52589264&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is possible because during the installation of &lt;code&gt;rodauth-rails&lt;/code&gt;, &lt;a href=&quot;https://github.com/janko/rodauth-model&quot;&gt;rodauth-model&lt;/a&gt; is automatically configured within the &lt;code&gt;account&lt;/code&gt; model, defining an &lt;code&gt;identities&lt;/code&gt; one-to-many association on the account model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Account &amp;lt; ApplicationRecord
  include Rodauth::Rails.model # the rodauth-model is included here
  enum :status, unverified: 1, verified: 2, closed: 3
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For versions below &lt;code&gt;0.3.3&lt;/code&gt;, this isn&amp;#39;t the case &lt;a href=&quot;https://github.com/janko/rodauth-omniauth/issues/10&quot;&gt;due to a bug&lt;/a&gt;. So to access &lt;code&gt;identities&lt;/code&gt;, you can either choose to upgrade your version of &lt;code&gt;rodauth-omniauth&lt;/code&gt;, or define the relationship manually like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# ... app/models/account_identity.rb
class AccountIdentity &amp;lt; ApplicationRecord
  belongs_to :account
end

# ... app/models/account.rb
class Account &amp;lt; ApplicationRecord
  has_many :identities, class_name: &amp;quot;AccountIdentity&amp;quot;
end
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Adding Extra Columns&lt;/h3&gt;
&lt;p&gt;During the &lt;code&gt;identities&lt;/code&gt; table migration, the &lt;code&gt;timestamps&lt;/code&gt; column was missing. This is because that column is not originally included in the implementation provided by the gem.&lt;/p&gt;
&lt;p&gt;However, we can manually add that column (as well as any other columns) and any logic required to configure the gem.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails g migration add_timestamps_to_account_identities
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AddTimestampsToAccountIdentities &amp;lt; ActiveRecord::Migration[7.0]
  def change
    add_timestamps :account_identities, null: true
    # allowing this field to be nullable due to already existing records
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the configuration, we have to implement the logic to add these columns when creating an account identity.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# ... app/misc/rodauth_main.rb
configure do
# ...
  # omniauth_identity_insert_hash handles account identity creation
  omniauth_identity_insert_hash do
    super().merge(created_at: Time.now)
  end

  # omniauth_identity_update_hash handles account identity updates
  omniauth_identity_update_hash do
    super().merge(updated_at: Time.now)
  end
# ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On deleting the previous account identity and creating a new one, we get this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;gt; AccountIdentity.first
=&amp;gt; #&amp;lt;AccountIdentity:0x00007f28533b83c8
 id: 1,
 account_id: 1,
 provider: &amp;quot;github&amp;quot;,
 uid: &amp;quot;52589264&amp;quot;,
 created_at: Sat, 18 Feb 2023 09:04:10.069443000 UTC +00:00,
 updated_at: Sat, 18 Feb 2023 09:04:10.069430000 UTC +00:00&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it becomes necessary to access the auth hash or any other additional information, this gem provides us with some helper methods, like &lt;code&gt;omniauth_provider&lt;/code&gt;, &lt;code&gt;omniauth_email&lt;/code&gt;, and many more. Hooks are also available to implement the logic required at certain phases like &lt;code&gt;omniauth_before_callback_phase&lt;/code&gt;, &lt;code&gt;omniauth_on_failure&lt;/code&gt;, etc. A comprehensive list of these helper methods and hooks can be found in the &lt;a href=&quot;https://github.com/janko/rodauth-omniauth&quot;&gt;rodauth-omniauth documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A Note on Compatibility and Versioning&lt;/h2&gt;
&lt;p&gt;When installing the &lt;code&gt;omniauth-google_oauth2&lt;/code&gt; gem, a versioning error is encountered.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;Bundler could not find compatible versions for gem &amp;quot;omniauth&amp;quot;:
  In Gemfile:
    omniauth-google_oauth2 was resolved to 0.1.5, which depends on
      omniauth (~&amp;gt; 1.0)

    rodauth-omniauth was resolved to 0.3.1, which depends on
      omniauth (~&amp;gt; 2.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because the &lt;code&gt;omniauth&lt;/code&gt; version dependencies for both gems are not compatible. &lt;a href=&quot;https://rubygems.org/gems/omniauth-google_oauth2&quot;&gt;&lt;code&gt;omniauth-google_oauth2&lt;/code&gt;&lt;/a&gt; depends on &lt;code&gt;omniauth ~&amp;gt; 1.0&lt;/code&gt;, while the &lt;a href=&quot;https://rubygems.org/gems/rodauth-omniauth&quot;&gt;&lt;code&gt;rodauth-omniauth&lt;/code&gt;&lt;/a&gt; gem depends on &lt;code&gt;omniauth ~&amp;gt; 2.0&lt;/code&gt;. A more compatible &lt;code&gt;omniauth-google&lt;/code&gt; gem would be &lt;a href=&quot;https://rubygems.org/gems/omniauth-google-oauth2&quot;&gt;&lt;code&gt;omniauth-google-oauth2&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Any &lt;code&gt;OAuth&lt;/code&gt; provider gem used alongside &lt;code&gt;rodauth-omniauth&lt;/code&gt; must be compatible with &lt;code&gt;OmniAuth 2.0&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;rodauth-omniauth: Now and Future Plans&lt;/h2&gt;
&lt;p&gt;In summary, the &lt;code&gt;rodauth-omniauth&lt;/code&gt; gem, though powerful, cannot be employed independently as it is a Rodauth extension. However, when employed with Rodauth, it saves you time that would have been spent manually implementing OmniAuth logic.&lt;/p&gt;
&lt;p&gt;The gem handles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Routing a request to a third-party provider.&lt;/li&gt;
&lt;li&gt;The response (by creating the required accounts/identities or validating existing ones).&lt;/li&gt;
&lt;li&gt;Outputting errors when necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the login is successful, it also redirects the request to the root URL.&lt;/p&gt;
&lt;p&gt;In the future, the plan is for the &lt;code&gt;rodauth-omniauth&lt;/code&gt; gem to offer services like allowing users to connect or remove external identities when signed in. So you will have the option to connect your Google account to an application (even if you&amp;#39;re signed in with GitHub) or disconnect an already existing identity from an application while signed in.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we looked briefly at the usefulness of OmniAuth before setting up rodauth-omniauth for a Rails application. We also defined authentication and explained why it is so important for your Ruby app.&lt;/p&gt;
&lt;p&gt;I hope you&amp;#39;ve found this introduction to the rodauth-omniauth gem useful.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Diving into Custom Exceptions in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/03/29/diving-into-custom-exceptions-in-ruby.html"/>
    <id>https://blog.appsignal.com/2023/03/29/diving-into-custom-exceptions-in-ruby.html</id>
    <published>2023-03-29T00:00:00+00:00</published>
    <updated>2023-03-29T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover how to customize exceptions in Ruby and highlight exceptions in your logs.</summary>
    <content type="html">&lt;p&gt;Customizing exceptions is usually not a common concern during software development. But if you catch an error in an observability tool and can&amp;#39;t correctly and quickly identify the problem, you may need more information and details about an exception.&lt;/p&gt;
&lt;p&gt;This article will show you how to customize exceptions in Ruby and mitigate potential future problems due to a lack of error information.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive straight in!&lt;/p&gt;
&lt;h2&gt;A Quick Side-Note&lt;/h2&gt;
&lt;p&gt;This post is a natural progression to &lt;a href=&quot;https://blog.appsignal.com/2022/09/21/debugging-in-ruby-with-appsignal.html&quot;&gt;Debugging in Ruby with AppSignal&lt;/a&gt;, so we recommend reading that first for an overview of debugging and an introduction to custom exceptions.&lt;/p&gt;
&lt;h2&gt;How to Rescue an Exception with Ruby&lt;/h2&gt;
&lt;p&gt;Ruby has a class called &lt;code&gt;Exception&lt;/code&gt;, from which error-handling classes inherit. In this section, we&amp;#39;ll better understand Ruby&amp;#39;s structural exception flow before we create our custom exception.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Exception&lt;/code&gt; is the main exception class in Ruby, and &lt;code&gt;rescue Exception&lt;/code&gt; will catch all exceptions inherited from &lt;code&gt;Exception&lt;/code&gt;. It isn&amp;#39;t best practice to use this in our app, as it will catch all exceptions used internally by Ruby.&lt;/p&gt;
&lt;p&gt;The easiest way to rescue an exception in Ruby is to create a &lt;code&gt;begin ... rescue&lt;/code&gt; block of code like the example below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  call_some_method()
rescue Exception =&amp;gt; e
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;Exception&lt;/code&gt; will catch all exceptions, we should try to use something more specific to filter and get only the error we want.
One example is the &lt;code&gt;StandardError&lt;/code&gt; class, a standard rescuer used to catch exceptions related to application code errors by ignoring Ruby&amp;#39;s internal exceptions.&lt;/p&gt;
&lt;p&gt;To catch a &lt;code&gt;StandardException&lt;/code&gt;, you can use the example below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  call_some_method()
rescue StandardException =&amp;gt; e
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or, if no exception class is stated, Ruby will return the exceptions handled by the &lt;code&gt;StandardException&lt;/code&gt; class.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  call_some_method()
rescue =&amp;gt; e
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To create a custom exception with Ruby, you&amp;#39;ll probably create a class that inherits from &lt;code&gt;StandardError&lt;/code&gt;. We&amp;#39;ll cover that in the next few sections.&lt;/p&gt;
&lt;p&gt;To learn more about exceptions and find the best one to inherit, check &lt;a href=&quot;https://ruby-doc.org/core-2.5.0/Exception.html&quot;&gt;Ruby&amp;#39;s exceptions list&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Adding Additional Information to the Ruby Exception&lt;/h2&gt;
&lt;p&gt;Creating a new exception without sending relevant information will not effectively help in debugging — identifying and fixing — the problem. You need to add the error details. Here, we will see how to pass information from objects to the custom exception.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Let&amp;#39;s create a new custom exception just to receive the data we want to log. As mentioned in the previous section, the custom exception needs to inherit from &lt;code&gt;StandardError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can use a simple Rails project to test this implementation. Create a new folder called &lt;code&gt;exceptions&lt;/code&gt; inside the &lt;code&gt;app&lt;/code&gt; folder and a class called &lt;code&gt;custom_exception.rb&lt;/code&gt; in your Rails project.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/exceptions/custom_exception.rb

class CustomException &amp;lt; StandardError
  def initialize(code, name, msg, details)
    @code = code
    @name = name
    @msg = msg
    @details = details
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this exception class, we can define all the information that needs to be shown in the logs and we use four attributes for our data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a code to enumerate the exception&lt;/li&gt;
&lt;li&gt;a name&lt;/li&gt;
&lt;li&gt;an informational message&lt;/li&gt;
&lt;li&gt;internal information details about the exception already provided by Ruby&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And now we create a &lt;code&gt;begin...rescue&lt;/code&gt; block to raise the &lt;code&gt;CustomException&lt;/code&gt; and pass the parameters to the exception attributes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/employee.rb

class Employee
  def calculate_fees
    begin
      nil.object # Any code that throws an exception.
    rescue =&amp;gt; exception
      raise CustomException.new(
        333,
        &amp;quot;My customized exception&amp;quot;,
        &amp;quot;A new exception occurred in the application&amp;quot;,
        exception)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks useful, so let&amp;#39;s continue the implementation and use this data to customize the exception log.&lt;/p&gt;
&lt;h2&gt;Extending an Exception in Ruby&lt;/h2&gt;
&lt;p&gt;Extending an exception allows us to elevate customization to a more specific and reusable level. Just as Ruby has several exceptions for different types of errors, we can also follow this line of programming in our systems.&lt;/p&gt;
&lt;p&gt;Next, let&amp;#39;s understand how to override our exception and use it in a context that demands an extension.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.appsignal.com/tour/log-management&quot;&gt;Finding information in logs&lt;/a&gt; is a painful activity. Developers often blame themselves for not including more information about errors or how to search and filter. If you are not using any &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;monitoring tools that provide this&lt;/a&gt;, including meaningful data could save you in the foreseeable future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro-tip&lt;/strong&gt;: &lt;em&gt;Check out the section on &amp;#39;Debugging Exceptions in Ruby with AppSignal&amp;#39; in our post &lt;a href=&quot;https://blog.appsignal.com/2022/09/21/debugging-in-ruby-with-appsignal.html&quot;&gt;Debugging in Ruby with AppSignal&lt;/a&gt; for information about how to set up and track custom exceptions in AppSignal.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the example below, we will customize the display of exceptions in the log. Initially, you might think that it&amp;#39;s just a light and pretty way to show data. However, the keywords used in the data are key to finding an error and helping whoever will be monitoring the application.&lt;/p&gt;
&lt;p&gt;Add the &lt;a href=&quot;https://github.com/fazibear/colorize&quot;&gt;colorize&lt;/a&gt; gem to your project by including the code below in your Gemfile.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

gem &amp;#39;colorize&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the bundle to install the colorize gem and launch your Rails application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ bundle install
$ rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, in your exception class, import the gem. Print the attributes in a formatted way, so it&amp;#39;s easy to see and quickly find an important error.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/exceptions/custom_exception.rb

require &amp;#39;colorize&amp;#39;

class CustomException &amp;lt; StandardError
  def initialize(code, name, msg, details)
    ...
    log_exception
  end

  def log_exception
    print_separator

    STDERR.puts &amp;quot;🚨🚨🚨 #{self.class} 🚨🚨🚨&amp;quot;.colorize(:red)

    self.instance_variables.each do |attr|
      # All attributes will be printed.
      STDERR.puts &amp;quot;#{attr}: #{self.instance_variable_get(attr)}&amp;quot;.colorize(:red)
    end

    print_separator
  end

  def print_separator
    85.times { print &amp;quot;-&amp;quot;.colorize(color: :black, background: :red) }
    STDERR.puts &amp;quot;\n&amp;quot;
  end

  def to_s
    @name.colorize(:red)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out the result of this visual customization and test some options to create your format and style.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-03/custom-exception.png&quot; alt=&quot;Custom Exception&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping Up and Next Steps&lt;/h2&gt;
&lt;p&gt;In this post, we covered how to customize and highlight a Ruby exception in your logs. Many other customizations are possible, but what&amp;#39;s most important is that you handle the exceptions relevant to your application. And remember, don&amp;#39;t apply the same formatting style to all exceptions, as Rails already does.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Authorization Gems in Ruby: Pundit and CanCanCan</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/03/22/authorization-gems-in-ruby-pundit-and-cancancan.html"/>
    <id>https://blog.appsignal.com/2023/03/22/authorization-gems-in-ruby-pundit-and-cancancan.html</id>
    <published>2023-03-22T00:00:00+00:00</published>
    <updated>2023-03-22T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s take a look at Pundit and CanCanCan, two of the most popular authorization gems in Ruby.</summary>
    <content type="html">&lt;p&gt;Today, many web applications will feature pages that are publicly available — like a homepage — and more secure ones where a user has to log in to get access. The process of user registration, logging in, and tracking user session state is called &amp;quot;authentication&amp;quot;.&lt;/p&gt;
&lt;p&gt;At the same time, when dealing with logged-in users, it&amp;#39;s necessary to separate actions and resources that are available to them depending on their user roles. For example, &amp;quot;admins&amp;quot; generally have more access than normal users. This process of separating authenticated user access is termed &amp;quot;authorization&amp;quot;.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll explore two of the most popular authorization libraries in Ruby so far: Pundit and CanCanCan.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive in!&lt;/p&gt;
&lt;h2&gt;Setup and Prerequisites&lt;/h2&gt;
&lt;p&gt;In this article, we&amp;#39;ll use a simple Rails 7 app featuring users and posts. Users will be assigned an &amp;quot;editor&amp;quot; or &amp;quot;writer&amp;quot; role. Such a scenario is perfect for showcasing how authorization works.&lt;/p&gt;
&lt;p&gt;Check out the code repos for our example app with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/iamaestimo/pundit_vs_cancancan_tutorial&quot;&gt;Pundit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/iamaestimo/cancancan_tutorial&quot;&gt;CanCanCan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even though this article focuses on authorization, the companion subject of authentication cannot be ignored.&lt;/p&gt;
&lt;p&gt;We won&amp;#39;t get into the details of setting up authentication as that&amp;#39;s outside the scope of this post. You can follow the installation instructions in the &lt;a href=&quot;https://github.com/heartcombo/devise&quot;&gt;Devise gem documentation&lt;/a&gt; (we&amp;#39;ll pair Devise with our authorization gems).&lt;/p&gt;
&lt;p&gt;One more thing — whether you deal with Pundit or CanCanCan, the actual work of defining your app&amp;#39;s user roles does not happen automatically when you install either of the authorization gems. You will need to set it up manually.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s do that.&lt;/p&gt;
&lt;h2&gt;Defining User Roles&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s assume you&amp;#39;ve already installed the Devise gem and set up a user model. The next step is to decide what roles the app&amp;#39;s users will have. In our case, we&amp;#39;ll set out the following roles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Writer&lt;/strong&gt; - This user role will be able to &lt;em&gt;create&lt;/em&gt;, &lt;em&gt;edit&lt;/em&gt;, &lt;em&gt;update&lt;/em&gt;, and &lt;em&gt;delete&lt;/em&gt; their own posts. At the same time, a writer can also &lt;em&gt;view&lt;/em&gt; other writers&amp;#39; posts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Editor&lt;/strong&gt; - A user with the editor role can &lt;em&gt;edit&lt;/em&gt;, &lt;em&gt;update&lt;/em&gt;, &lt;em&gt;view&lt;/em&gt;, and &lt;em&gt;delete&lt;/em&gt; any user&amp;#39;s posts, but they cannot &lt;em&gt;create&lt;/em&gt; their own posts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add a column to store a user&amp;#39;s role (using a migration to modify the user&amp;#39;s table):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails generate migration add_column_role_to_users role:integer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the migration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then modify the user model to include the roles we&amp;#39;ve just defined:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/user.rb

class User &amp;lt; ApplicationRecord
  # User role
  enum role: %i[writer editor]

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While we are on this, let&amp;#39;s go ahead and set up a &lt;code&gt;Post&lt;/code&gt; model with the attributes of &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt;, as well as a reference to the user who writes the post:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails g scaffold Post title body:text user:references
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We add the foreign key &lt;em&gt;user_id&lt;/em&gt; into the &lt;code&gt;Post&lt;/code&gt; model to associate every post that&amp;#39;s created with a particular user. Since we have already set up user authentication using Devise, we can simply modify the create method of the &lt;code&gt;Posts&lt;/code&gt; controller to automatically set the &lt;em&gt;user_id&lt;/em&gt; to the currently logged-in user:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
...

def create
    @post = current_user.posts.new(post_params) # automatically assign a post to the current user on creation

    respond_to do |format|
        if @post.save
        format.html { redirect_to post_url(@post), notice: &amp;#39;Post was successfully created.&amp;#39; }
        format.json { render :show, status: :created, location: @post }
        else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @post.errors, status: :unprocessable_entity }
        end
    end
end
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also modify the User model to make sure it&amp;#39;s associated with the &lt;code&gt;Post&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/user.rb

class User &amp;lt; ApplicationRecord
  has_many :posts

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  enum role: %i[writer editor]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let&amp;#39;s seed the database with some users, each with a different role:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# db/seeds.rb

User.create(email: &amp;#39;writer1@example.com&amp;#39;, password: &amp;#39;example&amp;#39;, password_confirmation: &amp;#39;example&amp;#39;, role: 0) # this creates a user with the writer role
User.create(email: &amp;#39;writer2@example.com&amp;#39;, password: &amp;#39;example&amp;#39;, password_confirmation: &amp;#39;example&amp;#39;, role: 0) # second writer
User.create(email: &amp;#39;editor@example.com&amp;#39;, password: &amp;#39;example&amp;#39;, password_confirmation: &amp;#39;example&amp;#39;, role: 1) # this creates a user with the editor role
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then seed the database:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails db:seed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far, our app now has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication set up using Devise.&lt;/li&gt;
&lt;li&gt;Two defined user roles of &amp;quot;writer&amp;quot; and &amp;quot;editor&amp;quot;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Post&lt;/code&gt; model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that, we now have everything we need to work properly with Pundit.&lt;/p&gt;
&lt;h2&gt;Authorization in Your Ruby App with Pundit&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/varvet/pundit&quot;&gt;Pundit&lt;/a&gt; is an authorization library built around object-oriented architecture and plain Ruby classes. It gives you tools to build a solid authorization layer that can scale with your app.&lt;/p&gt;
&lt;h2&gt;Installing Pundit in Your Ruby App&lt;/h2&gt;
&lt;p&gt;Add the gem to your app&amp;#39;s Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

gem &amp;#39;pundit&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in the terminal, run the command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, run the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle add pundit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since authorization mostly deals with granting or denying access to controller resources, the next step is to add Pundit&amp;#39;s authorization module to the application controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/application_controller.rb

class ApplicationController &amp;lt; ActionController::Base
  include Pundit::Authorization
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, generate a base policy class all other policies will inherit from:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails g pundit:install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which gives you the following base policy class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/application_policy.rb

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def show?
    false
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  class Scope
    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      raise NotImplementedError, &amp;quot;You must define #resolve in #{self.class}&amp;quot;
    end

    private

    attr_reader :user, :scope
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, Pundit is now properly set up and ready to go. Additionally, we have authentication and user roles set up.&lt;/p&gt;
&lt;p&gt;Next, let&amp;#39;s use policies to implement rules that define how each user role will access the &lt;code&gt;Post&lt;/code&gt; resource.&lt;/p&gt;
&lt;h2&gt;Configuring Pundit Policies&lt;/h2&gt;
&lt;p&gt;In Pundit lingo, a &amp;quot;policy&amp;quot; is a plain Ruby class where you define all the rules for how a user role interacts with different resources.&lt;/p&gt;
&lt;p&gt;These policies come with some notable features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each policy is named after an existing model, suffixed with the word &amp;quot;Policy&amp;quot;. For example, a policy defining how the &lt;code&gt;Post&lt;/code&gt; model is accessed is called &lt;code&gt;PostPolicy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;attr_reader&lt;/code&gt;: this takes two arguments - the first is a &lt;code&gt;user&lt;/code&gt;, specifically, the currently logged-in user — &lt;code&gt;current_user&lt;/code&gt; — and the second argument is the model that you&amp;#39;d like to define authorization rules for, in our case, &lt;code&gt;post&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Query methods that will map to the controller methods of the resource that has authorization rules set up. For organizational purposes, it&amp;#39;s best to have all policies under the &lt;code&gt;app/policies&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since we know what access rules we need to define for the different user roles in our app, let&amp;#39;s start with the writer role.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Defining a Pundit Policy for a Role&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s use the writer role to see how this can be done. To begin with, we can outline the writer role&amp;#39;s access to the &lt;code&gt;Post&lt;/code&gt; resource as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create&lt;/strong&gt; their own posts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Edit and update&lt;/strong&gt; their own posts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View (or read)&lt;/strong&gt; their own posts as well as other user&amp;#39;s posts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delete&lt;/strong&gt; their own posts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that in mind, go ahead and generate a new policy to control how posts are accessed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails g pundit:policy post
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives us the following generic policy class that inherits from the base policy we generated earlier:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/post_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
  class Scope &amp;lt; Scope
    # NOTE: Be explicit about which records you allow access to!
    # def resolve
    #   scope.all
    # end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s now add access rules for the writer role accordingly (these also override any rules that are inherited from the base policy class):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/post_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
  ...
  def create?
    @user.writer? # a writer is able to create a post
  end

  def edit?
    @user.writer? # a writer is able to edit a post
  end

  def update?
    @user.writer? # a writer can update a post
  end

  def delete?
    @user.writer? # a writer can delete a post
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we define what a writer can do when creating, editing, updating, and deleting posts which are corresponding actions on the posts&amp;#39; controller. If you have noticed, these rules apply to posts in general and not necessarily to a writer&amp;#39;s own posts (we&amp;#39;ll get to that in the section on scopes).&lt;/p&gt;
&lt;p&gt;For now, let&amp;#39;s see how we can use this policy.&lt;/p&gt;
&lt;h3&gt;Using a Policy in Pundit&lt;/h3&gt;
&lt;p&gt;To use a policy, call Pundit&amp;#39;s &lt;code&gt;authorize&lt;/code&gt; method on the controller&amp;#39;s method where you want to check access rules. You instantiate the relevant policy class and, more specifically, the action that should be called based on where the &lt;code&gt;authorize&lt;/code&gt; method has been called.&lt;/p&gt;
&lt;p&gt;For example, let&amp;#39;s call &lt;code&gt;authorize&lt;/code&gt; on the post controller&amp;#39;s &lt;strong&gt;create&lt;/strong&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/post_controller.rb
...

def create
    @post = current_user.posts.new(post_params)
    authorize @post

    respond_to do |format|
        if @post.save
        format.html { redirect_to post_url(@post), notice: &amp;#39;Post was successfully created.&amp;#39; }
        format.json { render :show, status: :created, location: @post }
        else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @post.errors, status: :unprocessable_entity }
        end
    end
end
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test this, log in as an editor and try to &lt;code&gt;create&lt;/code&gt; a post. Doing this will result in the following error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-03/pundit-not-authorized-error.png&quot; alt=&quot;Pundit&amp;#39;s not authorized error&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Though this does what we want, showing such an error page is not good for the user experience. In the next section, you will learn how to rescue the &lt;code&gt;NotAuthorizedError&lt;/code&gt; and serve up something more user-friendly.&lt;/p&gt;
&lt;h2&gt;Rescuing From Pundit&amp;#39;s &lt;code&gt;NotAuthorizedError&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;First, we&amp;#39;ll need to edit &lt;code&gt;ApplicationController&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/application_controller.rb
class ApplicationController &amp;lt; ActionController::Base
  include Pundit::Authorization

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private

  def user_not_authorized
    flash[:alert] = &amp;quot;You are not authorized to perform this action.&amp;quot;
    redirect_back(fallback_location: root_path)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we are basically telling Pundit to use the &lt;code&gt;user_not_authorized&lt;/code&gt; method whenever the &lt;code&gt;NotAuthorizedError&lt;/code&gt; is raised. It will simply redirect the unauthorized user to a specific page and also provide a relevant flash message explaining what has happened:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-03/rescue-from-not-authorized-error.png&quot; alt=&quot;Rescuing from a Pundit&amp;#39;s not authorized error&quot;/&gt;&lt;/p&gt;
&lt;p&gt;We now have a simple authorization system capable of handling a very generalized permissions case.&lt;/p&gt;
&lt;p&gt;But what if we want more fine-grained permissions? For this, we need to utilize Pundit&amp;#39;s scopes.&lt;/p&gt;
&lt;h2&gt;Pundit&amp;#39;s Scopes&lt;/h2&gt;
&lt;p&gt;Pundit scopes are similar to ActiveRecord scopes. In the latter, you can use scopes to fetch records according to specific criteria. However, with Pundit&amp;#39;s scopes, you manage access to specific resources according to certain rules you set.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say we want editors to be able to view and edit posts that are in &amp;quot;draft&amp;quot; status, and at the same time, allow writers to create, view, edit, update and delete posts that only belong to them.&lt;/p&gt;
&lt;p&gt;We can start by editing the post policy to look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/post_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
  class Scope &amp;lt; Scope
    def resolve
      if user.editor?
        # an editor can only access posts in &amp;quot;draft&amp;quot; status
        scope.where(published: false)
      else
        # can access a post if they are the author
        scope.where(user: user)
      end
    end
  end
  def show?
    @user.writer? || @user.editor?
  end
  def create?
    @user.writer?
  end
  def edit?
    @user.writer? || @user.editor?
  end
  def update?
    @user.writer? || @user.editor?
  end
  def delete?
    @user.writer?
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we&amp;#39;ll authorize access to the resource in the posts controller, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
class PostsController &amp;lt; ApplicationController
  ...
  def index
    @posts = policy_scope(Post)
  end
  # GET /posts/1 or /posts/1.json
  def show
    @post = policy_scope(Post).find(params[:id])
  end
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, scoping with Pundit can go way deeper than we&amp;#39;ve shown, but we&amp;#39;ll leave it at that in this post. If you want, check out the &lt;a href=&quot;https://github.com/varvet/pundit&quot;&gt;Pundit documentation&lt;/a&gt; to see how you can use scopes in a more advanced way.&lt;/p&gt;
&lt;p&gt;For now, let&amp;#39;s go through how you can use the library with Rails&amp;#39; strong parameters.&lt;/p&gt;
&lt;h2&gt;Using Pundit with Rails&amp;#39; Strong Parameters&lt;/h2&gt;
&lt;p&gt;By combining Pundit&amp;#39;s authorization rules with Rails&amp;#39; strong parameters, you can achieve lockdown access to a resource&amp;#39;s attributes. Let&amp;#39;s say you want &lt;em&gt;editors&lt;/em&gt; to be the only ones with access to an excerpt field of the &lt;code&gt;Post&lt;/code&gt; model. How would you go about it?&lt;/p&gt;
&lt;p&gt;First, add an aptly-named block to the relevant policy:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/policies/post_policy.rb

class PostPolicy &amp;lt; ApplicationPolicy
...
  def permitted_attributes
    if user.editor?
      [:title, :body, :excerpt]
    else
      [:title, :body]
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, modify the permitted params block in the controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
class PostsController &amp;lt; ApplicationController
  ...

  private

  def post_params
    params.require(:post).permit(policy(@post).permitted_attributes)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, you have made the &lt;em&gt;excerpt&lt;/em&gt; attribute available to editors only.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll now shift gears to look at the other authorization library, CanCanCan.&lt;/p&gt;
&lt;h2&gt;Introducing CanCanCan for Your Ruby App&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/CanCanCommunity/cancancan&quot;&gt;CanCanCan&lt;/a&gt; is an authorization library that uses an &amp;quot;ability&amp;quot; class to define who has access to what in a Rails app. Actual access control is achieved using an authorization module and various view helpers.&lt;/p&gt;
&lt;h2&gt;Installing CanCanCan&lt;/h2&gt;
&lt;p&gt;Installation is as easy as running the command below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle add cancancan
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like Pundit, with CanCanCan, you can define all access rules within a plain Ruby class object called an &amp;quot;ability&amp;quot; class. Let&amp;#39;s do that next with the following generator command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails g cancan:ability
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which generates the class object below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/abilty.rb

class Ability
  include CanCan::Ability

  def initialize(user)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s learn more about how the ability class can define access rules for our example Rails app next.&lt;/p&gt;
&lt;h2&gt;Defining and Checking CanCanCan Abilities&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ll use the same user roles as in the Pundit example: writers and editors. A writer can create, edit, update, destroy their own posts, and view other writers&amp;#39; posts; an editor can do everything except create a post of their own.&lt;/p&gt;
&lt;p&gt;To use CanCanCan, first define what each user or role can access in the ability class, following this format:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/ability.rb

can actions, subjects, conditions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/abilty.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    can :update, Post, user: user # With CanCanCan, the update action covers both the edit and update actions
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in the controller, check if an access rule exists for a particular action — using our example, the &lt;code&gt;edit&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb

...
def edit
    authorize! :edit, @post
end
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, if we visit the edit post view as another writer, we get the error below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-03/cancancan-access-denied-error.png&quot; alt=&quot;CanCanCan access denied error&quot;/&gt;&lt;/p&gt;
&lt;p&gt;And just like we did with Pundit, let&amp;#39;s rescue this error and show the user a better error page.&lt;/p&gt;
&lt;h2&gt;Handling CanCanCan’s “Access Denied” Errors&lt;/h2&gt;
&lt;p&gt;Whenever a resource is not authorized, CanCanCan will raise a &lt;code&gt;CanCan::AccessDenied&lt;/code&gt; error. The easiest way to catch this exception is by modifying the &lt;code&gt;ApplicationController&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/application_controller.rb

class ApplicationController &amp;lt; ActionController::Base
  rescue_from CanCan::AccessDenied do |exception|
    respond_to do |format|
      format.json { head :forbidden }
      format.html { redirect_to root_path, alert: exception.message }
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doing this makes for a good user experience. In the screenshot below, the unauthorized user is redirected to the home page and shown a relevant flash message:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-03/catching-cancancan-access-denied-error.png&quot; alt=&quot;Handling CanCanCan&amp;#39;s access denied exception&quot;/&gt;&lt;/p&gt;
&lt;p&gt;You can even customize the error message shown to the user:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;#config/locales/en.yml
en:
  unauthorized:
    update:
      all: &amp;quot;You&amp;#39;re not authorized to %{action} %{subject}.&amp;quot;
      writer: &amp;quot;You&amp;#39;re not authorized to %{action} other writer&amp;#39;s posts.&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your app serves XML as a response, or you just want to dive deeper into handling the &lt;code&gt;CanCanCan::AccessDenied&lt;/code&gt; exception, check out &lt;a href=&quot;https://github.com/CanCanCommunity/cancancan/blob/develop/docs/handling_access_denied.md&quot;&gt;CanCanCan&amp;#39;s documentation&lt;/a&gt;. For now, let&amp;#39;s see how we can combine CanCanCan&amp;#39;s various abilities to create a more robust authorization layer.&lt;/p&gt;
&lt;h2&gt;Combining Multiple CanCanCan Abilities&lt;/h2&gt;
&lt;p&gt;You can define multiple access rules for a resource in the ability class. Taking the writer and editor roles, for example, we can do this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    can :update, Post, user: user # only a post&amp;#39;s author/owner can update or edit a post
    can :read, Post # any user can read a post or list of posts (access both show and index actions)
    can :destroy, Post, user: user # only a post&amp;#39;s author/owner can delete it

    return unless user.editor?
    cannot :create, Post # an editor role cannot create a post
    can :update, Post # an editor can update any post
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The question is, why would you want to do this?&lt;/p&gt;
&lt;p&gt;With CanCanCan, you can define all access rules in one ability file. This has both advantages and disadvantages.&lt;/p&gt;
&lt;p&gt;Having all your rules in one place is very convenient for handling access rules since all rules are defined in one place.&lt;/p&gt;
&lt;p&gt;However, if your app deals with many user roles or you have several resources that need authorization, the ability class can easily become too big and complex to handle. One way to handle this is by reorganizing the ability class to use method definitions, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    anyone_abilities

    if user.writer?
      writer_abilities
    elsif user.editor?
      editor_abilities
    end

  end

  private

  def anyone_abilities
    can :read, Post
  end

  def writer_abilities
    can :update, Post, user: Current.user # we define Current.user in the application controller so that the ability class (which is a model) is able to pick up the currently logged in user
  end

  def editor_abilities
    cannot :create, Post # an editor role cannot create a post
    can :update, Post # an editor can update any post
  end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&amp;#39;s a lot more to CanCanCan than can be effectively covered in this article. Do check out the detailed &lt;a href=&quot;https://github.com/CanCanCommunity/cancancan/blob/develop/docs/README.md&quot;&gt;CanCanCan documentation&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;p&gt;To wrap up, let&amp;#39;s briefly touch on the features of each library and give reasons why you might choose one over the other.&lt;/p&gt;
&lt;h2&gt;Feature Comparison: Pundit vs. CanCanCan for Your Ruby App&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File organization&lt;/strong&gt; - With Pundit, you can easily organize your app&amp;#39;s authorization across multiple policy files. But with CanCanCan, authorization rules will live in one ability file. Working with multiple ability files is still possible, but this is not the default implementation style.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests&lt;/strong&gt; - Because permissions code will mostly reside within a single class compared to Pundit&amp;#39;s multiple ability classes, writing tests for CanCanCan could be easier than for Pundit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Helpers&lt;/strong&gt; - Both libraries provide a number of view helpers to check for permissions in the view layer. CanCanCan gives you the &lt;code&gt;can?&lt;/code&gt; method to use in your views, while Pundit gives you relatively similar functionality with the &lt;code&gt;policy&lt;/code&gt; helper.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Devise integration&lt;/strong&gt; - As you can see from the examples we have used in the article, both libraries integrate with Devise very well.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, we looked at two of the most popular authorization gems in the Ruby and Rails ecosystem: Pundit and CanCanCan.&lt;/p&gt;
&lt;p&gt;Both libraries offer a rich set of features for managing permissions in your Rails app. Because of this, it is nearly impossible to tell you which gem to choose — both can manage even the most complex permissions setups.&lt;/p&gt;
&lt;p&gt;We encourage you to try out Pundit and CanCanCan in your app and see which one fits your needs best.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Generalized User-local Container for UI State in Kredis</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/03/15/a-generalized-user-local-container-for-ui-state-in-kredis.html"/>
    <id>https://blog.appsignal.com/2023/03/15/a-generalized-user-local-container-for-ui-state-in-kredis.html</id>
    <published>2023-03-15T00:00:00+00:00</published>
    <updated>2023-03-15T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the second and final part of this series, we&#039;ll develop a generalized user-local container for UI state in Kredis.</summary>
    <content type="html">&lt;p&gt;In our last post, we persisted and restored a collapsed/expanded UI state with Kredis. However, the great pain point with this is that we have to invent a lot of Kredis keys.&lt;/p&gt;
&lt;p&gt;This time, we&amp;#39;ll see how we can avoid this by developing a generalized solution for any DOM node whose attribute states we want to track on the server side.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive straight in!&lt;/p&gt;
&lt;h2&gt;The Challenge&lt;/h2&gt;
&lt;p&gt;Remember that we had to concoct a Kredis set key (&lt;code&gt;open_department_ids&lt;/code&gt;) on the user model in the previous post. Now, following our example, imagine a user might be associated to the products they bought via &lt;code&gt;has_many&lt;/code&gt;. Assume that each of these products has a details page which also has some ephemeral state attached.&lt;/p&gt;
&lt;p&gt;All of a sudden, the complexity increases tremendously: we now have N &lt;code&gt;kredis_sets&lt;/code&gt; to cater for, prefixed with the product ID. Or we could handle it on the &lt;code&gt;Product&lt;/code&gt; model, storing the state for each user ID. Either way, it is going to get cumbersome, and we&amp;#39;ll likely have to resort to metaprogramming to smooth it out a bit.&lt;/p&gt;
&lt;p&gt;This, you&amp;#39;ll agree with me, is not a delightful challenge. It would be very beneficial for the app&amp;#39;s architecture if we could abstract that away.&lt;/p&gt;
&lt;p&gt;And this is precisely what we are going to attempt. We&amp;#39;ll use an &lt;em&gt;accordion&lt;/em&gt; on the product details page to illustrate this case and also its resolution.&lt;/p&gt;
&lt;h3&gt;Preparation&lt;/h3&gt;
&lt;p&gt;To start, we&amp;#39;ll add two more text columns, &lt;code&gt;description&lt;/code&gt; and &lt;code&gt;specs&lt;/code&gt;, to our &lt;code&gt;Product&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g migration AddDescriptionAndSpecsToProducts description:text specs:text
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, in the Rails console, we&amp;#39;ll provide some seed data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;Product.find_each do |product|
  product.update(description: Faker::Lorem.paragraph, specs: Faker::Markdown.ordered_list)
    description: Faker::Lorem.paragraph,
    specs: Faker::Markdown.ordered_list
  )
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let&amp;#39;s amend the &lt;code&gt;_product&lt;/code&gt; partial to include those two new properties in an &amp;quot;accordion&amp;quot;, again making use of the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/products/_product.html.erb --&amp;gt;

  &amp;lt;div id=&amp;quot;&amp;lt;%= dom_id product %&amp;gt;&amp;quot;&amp;gt;
    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Name:&amp;lt;/strong&amp;gt;
      &amp;lt;%= product.name %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Department:&amp;lt;/strong&amp;gt;
      &amp;lt;%= product.department_id %&amp;gt;
    &amp;lt;/p&amp;gt;

+   &amp;lt;p&amp;gt;
+     &amp;lt;details&amp;gt;
+       &amp;lt;summary&amp;gt;Description&amp;lt;/summary&amp;gt;
+
+       &amp;lt;%= product.description %&amp;gt;
+     &amp;lt;/details&amp;gt;
+   &amp;lt;/p&amp;gt;
+
+   &amp;lt;p&amp;gt;
+     &amp;lt;details&amp;gt;
+       &amp;lt;summary&amp;gt;Specs&amp;lt;/summary&amp;gt;
+
+       &amp;lt;%= simple_format product.specs %&amp;gt;
+     &amp;lt;/details&amp;gt;
+   &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our home index view, let&amp;#39;s link to the products:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/home/index.html.erb --&amp;gt;

  &amp;lt;!-- ... --&amp;gt;

        &amp;lt;ul&amp;gt;
          &amp;lt;% child_dep.products.each do |prod| %&amp;gt;
-           &amp;lt;li&amp;gt;&amp;lt;%= prod.name %&amp;gt;&amp;lt;/li&amp;gt;
+           &amp;lt;li&amp;gt;&amp;lt;%= link_to prod.name, prod %&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;% end %&amp;gt;
        &amp;lt;/ul&amp;gt;

  &amp;lt;!-- ... --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&amp;#39;s the detail of the view this produces:&lt;/p&gt;
&lt;Video fileName=&quot;/images/blog/2023-03/ui-state-product-detail&quot; /&gt;

&lt;h3&gt;Observing UI Changes with JavaScript&lt;/h3&gt;
&lt;p&gt;Now we have to rewrite our &lt;code&gt;UIState&lt;/code&gt; Stimulus controllers to listen for arbitrary DOM changes. We do this by placing a &lt;code&gt;MutationObserver&lt;/code&gt; on its element, which looks for attribute changes after it connects. In its callback (&lt;code&gt;mutateState&lt;/code&gt;), the following happens:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All attributes on the element, except for the &lt;code&gt;data-controller&lt;/code&gt; attribute itself, are collected.&lt;/li&gt;
&lt;li&gt;Their respective values are stored on an &lt;code&gt;attributes&lt;/code&gt; object with their &lt;code&gt;name&lt;/code&gt; as the key.&lt;/li&gt;
&lt;li&gt;This object is appended to a new &lt;code&gt;FormData&lt;/code&gt; object, along with a unique &lt;code&gt;key&lt;/code&gt; that identifies the exact instance of the UI state (we&amp;#39;ll get to that in a moment).&lt;/li&gt;
&lt;li&gt;This is then sent over to the server as before, in the form of a &lt;code&gt;PATCH&lt;/code&gt; request.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  // app/javascript/controllers/ui_state_controller.js

  import { Controller } from &amp;quot;@hotwired/stimulus&amp;quot;;
- import { patch } from &amp;quot;@rails/request.js&amp;quot;;
+ import { get, patch } from &amp;quot;@rails/request.js&amp;quot;;

  export default class extends Controller {
    static values = {
-     departmentId: String,
+     key: String,
    };

-   async toggle() {
-     const body = new FormData();
-     body.append(&amp;quot;open&amp;quot;, this.element.open);
-     body.append(&amp;quot;department_id&amp;quot;, this.departmentIdValue);
-
-     await patch(&amp;quot;/ui_state/update&amp;quot;, {
-       body,
-     });
-   }

+   async connect() {
+     this.mutationObserver = new MutationObserver(this.mutateState.bind(this));
+
+     this.mutationObserver.observe(this.element, { attributes: true });
+   }
+
+   disconnect() {
+     this.mutationObserver?.disconnect();
+   }
+
+   async mutateState() {
+     const body = new FormData();
+
+     const attributes = {};
+     this.element
+       .getAttributeNames()
+       .filter((name) =&amp;gt; name !== &amp;quot;data-controller&amp;quot;)
+       .map((name) =&amp;gt; {
+         attributes[name] = this.element.getAttribute(name);
+       });
+
+     body.append(&amp;quot;key&amp;quot;, this.keyValue);
+     body.append(&amp;quot;attributes&amp;quot;, JSON.stringify(attributes));
+
+     await patch(&amp;quot;/ui_state/update&amp;quot;, {
+       body,
+     });
+   }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Persisting UI State with Unique Kredis Keys&lt;/h2&gt;
&lt;p&gt;To generalize persisting UI state, we have to accomplish two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generate unique Kredis keys regarding the &lt;strong&gt;user&lt;/strong&gt;, &lt;strong&gt;resource&lt;/strong&gt; (in our case, the product), and the &lt;strong&gt;location in the view&lt;/strong&gt;, respectively.&lt;/li&gt;
&lt;li&gt;Use these keys to persist the attributes object received from the client.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Using Rails Helpers&lt;/h2&gt;
&lt;p&gt;So first, we have to solve the problem of coming up with unique keys. If you look at the requirements carefully, they read a lot like the built-in Rails &lt;a href=&quot;https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache&quot;&gt;fragment &lt;code&gt;cache&lt;/code&gt; helper&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In short, this helper will build a cache key based on the template it&amp;#39;s called from, and any cacheable Ruby objects passed to it. To quote the documentation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;views/template/action:7a1156131a6928cb0026877f8b749ac9/projects/123
      ^template path   ^template tree digest             ^class   ^id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don&amp;#39;t need the &lt;code&gt;cache&lt;/code&gt; helper though, because we are not actually storing a fragment, we just need a key. Thankfully, the method it calls internally to generate one, &lt;code&gt;cache_fragment_name&lt;/code&gt;, &lt;a href=&quot;https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache_fragment_name&quot;&gt;is also exposed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s check out how we can make use of this. If we add the following line to our &lt;code&gt;products/_product.html.erb&lt;/code&gt; partial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= cache_fragment_name([current_user, product]) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Something like this is rendered:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;[&amp;quot;products/_product:0e55db8bd12c9b268798d6550447b303&amp;quot;,
  [#&amp;lt;User id: 1, email: &amp;quot;julian@example.com&amp;quot;, ...&amp;gt;,
  #&amp;lt;Product id: 68, name: &amp;quot;Durable Wool Wallet&amp;quot;, ...&amp;gt;]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clearly, this nested array needs some massaging, but we are already very close. What&amp;#39;s still missing is a reference to the specific line in the template, but we can obtain this from the &lt;a href=&quot;https://ruby-doc.org/core-3.1.2/Kernel.html#method-i-caller&quot;&gt;call stack&amp;#39;s&lt;/a&gt; first entry representing a template. Let&amp;#39;s extract this to a helper and convert it to a string:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/helpers/application_helper.rb

module ApplicationHelper
  def ui_state_key(name)
    key = cache_fragment_name(name, skip_digest: true)
      .flatten
      .compact
      .map(&amp;amp;:cache_key)
      .join(&amp;quot;:&amp;quot;)

    key += &amp;quot;:#{caller.find { _1 =~ /html/ }}&amp;quot;

    key
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: We are skipping the view digesting as including the caller location makes it redundant.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Called as &lt;code&gt;ui_state_key([current_user, product])&lt;/code&gt;, this yields a string of the following format:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;users/1:products/68:/usr/app/ui-state-kredis/app/views/products/_product.html.erb:23:in `_app_views_products__product_html_erb___2602651701187259549_284440&amp;#39;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now there&amp;#39;s one final piece to solve: because we will include this key in our markup (to be picked up by the Stimulus controller above), we&amp;#39;ll want to &lt;strong&gt;obfuscate&lt;/strong&gt; it. Otherwise, it would be easy for a bad actor to exchange the user ID and/or the product global ID using the browser&amp;#39;s developer tools. A simple &lt;strong&gt;digest&lt;/strong&gt; will do in our case, because we do not intend to decrypt it back.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/helpers/application_helper.rb

  module ApplicationHelper
    def ui_state_key(name)
      key = cache_fragment_name(name, skip_digest: true)
        .flatten
        .compact
        .map(&amp;amp;:cache_key)
        .join(&amp;quot;:&amp;quot;)

      key += &amp;quot;:#{caller.find { _1 =~ /html/ }}&amp;quot;

-     key
+     ActiveSupport::Digest.hexdigest(key)
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s it, now we can obtain a digest of our UI fragment that will be unique to the location in the template and its MTIME, as well as user and resource:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;d45028686c0171e1e6e8a8ab78aae835&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Attach Stimulus Controllers to Your Rails App&lt;/h2&gt;
&lt;p&gt;Equipped with this, we can now attach the Stimulus controllers along with the respective keys to our &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; elements:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/products/_product.html.erb --&amp;gt;

  &amp;lt;div id=&amp;quot;&amp;lt;%= dom_id product %&amp;gt;&amp;quot;&amp;gt;
    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Name:&amp;lt;/strong&amp;gt;
      &amp;lt;%= product.name %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Department:&amp;lt;/strong&amp;gt;
      &amp;lt;%= product.department_id %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;details
+       data-controller=&amp;quot;ui-state&amp;quot;
+       data-ui-state-key-value=&amp;quot;&amp;lt;%= ui_state_key([current_user, product]) %&amp;gt;&amp;quot;
      &amp;gt;
        &amp;lt;summary&amp;gt;Description&amp;lt;/summary&amp;gt;

        &amp;lt;%= product.description %&amp;gt;
      &amp;lt;/details&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;details
+       data-controller=&amp;quot;ui-state&amp;quot;
+       data-ui-state-key-value=&amp;quot;&amp;lt;%= ui_state_key([current_user, product]) %&amp;gt;&amp;quot;
      &amp;gt;
        &amp;lt;summary&amp;gt;Specs&amp;lt;/summary&amp;gt;

        &amp;lt;%= simple_format product.specs %&amp;gt;
      &amp;lt;/details&amp;gt;
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now set out to generalize the &lt;code&gt;UIStateController&lt;/code&gt; as well. For this, we obtain a new &lt;code&gt;Kredis.json&lt;/code&gt; key using the transmitted &lt;code&gt;key&lt;/code&gt; param. Whenever a UI state &lt;code&gt;update&lt;/code&gt; occurs, all the &lt;code&gt;attributes&lt;/code&gt; sent over as form data will be parsed and stored therein.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/controllers/ui_state_controller.rb

  class UiStateController &amp;lt; ApplicationController
+   include ActionView::Helpers::SanitizeHelper

+   before_action :set_ui_state

    def update
-     if ui_state_params[:open] == &amp;quot;true&amp;quot;
-       current_user.open_department_ids &amp;lt;&amp;lt; params[:department_id]
-     else
-       current_user.open_department_ids.remove(params[:department_id])
-     end
+     @ui_state.value = JSON.parse(params[:attributes]).deep_transform_values { sanitize(_1) }

      head :ok
    end

    private

    def ui_state_params
-     params.permit(:department_id, :open)
+     params.permit(:attributes, :key)
    end

+   def set_ui_state
+     @ui_state = Kredis.json params[:key]
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Rehydrating the UI&lt;/h3&gt;
&lt;p&gt;Since our UI state is now safely stored in an ephemeral Kredis key, the only piece of the puzzle that&amp;#39;s missing is how to put the UI back together. This process is called &lt;em&gt;rehydration&lt;/em&gt;. In this example, we&amp;#39;ll use &lt;em&gt;server-side rehydration&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For this, we will revisit the &lt;code&gt;ui_state_key&lt;/code&gt; helper from above, and extend it with the rehydration logic.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;   # app/helpers/application_helper.rb
   module ApplicationHelper
+    def remember_ui_state_for(name, &amp;amp;block)
+      included_html = capture(&amp;amp;block).to_s.strip
+      fragment = Nokogiri::HTML.fragment(included_html)
+
+      first_fragment_child = fragment.first_element_child
+
+      # rehydrate
+      ui_state = Kredis.json ui_state_key(name)
+
+      ui_state.value&amp;amp;.each do |attribute_name, value|
+        first_fragment_child[attribute_name] = sanitize(value, tags: [])
+      end
+
+      # add stimulus controller and create unique key
+      first_fragment_child[&amp;quot;data-controller&amp;quot;] = &amp;quot;#{first_fragment_child[&amp;quot;data-controller&amp;quot;]} ui-state&amp;quot;.strip
+      first_fragment_child[&amp;quot;data-ui-state-key-value&amp;quot;] = ui_state_key(name)
+
+      first_fragment_child.to_html.html_safe
+    end

     def ui_state_key(name)
       key = cache_fragment_name(name, skip_digest: true)
         .flatten
         .compact
         .map(&amp;amp;:cache_key)
         .join(&amp;quot;:&amp;quot;)

       key += &amp;quot;:#{caller.find { _1 =~ /html/ }}&amp;quot;

       key
     end
   end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;remember_ui_state_for&lt;/code&gt; helper takes a &lt;code&gt;name&lt;/code&gt; argument that it will pass on to the &lt;code&gt;ui_state_key&lt;/code&gt; helper from above. Before it does that, though, it will &lt;code&gt;capture&lt;/code&gt; the HTML from the block passed to it and extract its first fragment child with Nokogiri.&lt;/p&gt;
&lt;p&gt;Afterward, the actual rehydration logic starts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the &lt;code&gt;ui_state&lt;/code&gt; is read back from &lt;code&gt;Kredis&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;in case such a key already exists, each &lt;code&gt;attribute&lt;/code&gt; is put back on the HTML element obtained in the previous step. To prevent any XSS vulnerabilities, the attributes&amp;#39; values are also &lt;code&gt;sanitized&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;finally, the Stimulus controller is attached to the element, along with the &lt;code&gt;ui_state_key&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Any change to the attributes would now again be transmitted by the &lt;code&gt;MutationObserver&lt;/code&gt;, and any page reload would invoke this helper again, rehydrating them upon the element.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; An additional security mechanism would be &lt;em&gt;safelisting&lt;/em&gt; the allowed attributes, which I have excluded from this example for simplicity.&lt;/p&gt;
&lt;p&gt;In our product partial, instead of wiring up the Stimulus controller manually, we must now use the new view helper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/products/_product.html.erb --&amp;gt;

  &amp;lt;div id=&amp;quot;&amp;lt;%= dom_id product %&amp;gt;&amp;quot;&amp;gt;
    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Name:&amp;lt;/strong&amp;gt;
      &amp;lt;%= product.name %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      &amp;lt;strong&amp;gt;Department:&amp;lt;/strong&amp;gt;
      &amp;lt;%= product.department_id %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
-     &amp;lt;details
-       data-controller=&amp;quot;ui-state&amp;quot;
-       data-ui-state-key-value=&amp;quot;&amp;lt;%= ui_state_key([current_user, product]) %&amp;gt;&amp;quot;
-     &amp;gt;
-       &amp;lt;summary&amp;gt;Description&amp;lt;/summary&amp;gt;
-
-       &amp;lt;%= product.description %&amp;gt;
-     &amp;lt;/details&amp;gt;
+     &amp;lt;%= remember_ui_state_for([current_user, product]) do %&amp;gt;
+       &amp;lt;details&amp;gt;
+         &amp;lt;summary&amp;gt;Description&amp;lt;/summary&amp;gt;
+
+         &amp;lt;%= product.description %&amp;gt;
+       &amp;lt;/details&amp;gt;
+     &amp;lt;% end %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
-     &amp;lt;details
-       data-controller=&amp;quot;ui-state&amp;quot;
-       data-ui-state-key-value=&amp;quot;&amp;lt;%= ui_state_key([current_user, product]) %&amp;gt;&amp;quot;
-     &amp;gt;
-       &amp;lt;summary&amp;gt;Specs&amp;lt;/summary&amp;gt;
-
-       &amp;lt;%= simple_format product.specs %&amp;gt;
-     &amp;lt;/details&amp;gt;
+     &amp;lt;%= remember_ui_state_for([current_user, product]) do %&amp;gt;
+       &amp;lt;details &amp;gt;
+         &amp;lt;summary&amp;gt;Specs&amp;lt;/summary&amp;gt;
+
+         &amp;lt;%= simple_format product.specs %&amp;gt;
+       &amp;lt;/details&amp;gt;
+     &amp;lt;% end %&amp;gt;
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what the end result looks like:&lt;/p&gt;
&lt;Video fileName=&quot;/images/blog/2023-03/ui-state-generalized-rehydration&quot; /&gt;

&lt;p&gt;It turns out that this approach is perfect for HTML elements or custom web components that reflect their state in one or more attributes (e.g. &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;, &lt;a href=&quot;https://shoelace.style/&quot;&gt;Shoelace&lt;/a&gt;, &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt;, etc.).&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In part one of this series, we described what Kredis can and cannot do, and developed an example use case for storing ephemeral UI state using a bespoke Redis key.&lt;/p&gt;
&lt;p&gt;In this second and final part, we identified the drawbacks of this approach. We developed a generalized solution to apply to any DOM node whose state of attributes we want to track on the server side.&lt;/p&gt;
&lt;p&gt;Finally, let me point out that these considerations inspired me to develop a library called &lt;a href=&quot;https://github.com/julianrubisch/solder&quot;&gt;solder&lt;/a&gt;, which I would be happy to receive feedback on!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Making the Most of Your Logs in Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/03/01/making-the-most-of-your-logs-in-rails.html"/>
    <id>https://blog.appsignal.com/2023/03/01/making-the-most-of-your-logs-in-rails.html</id>
    <published>2023-03-01T00:00:00+00:00</published>
    <updated>2023-03-01T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">It&#039;s easy to get going with logging with Rails, but not so easy to master it. Discover how you can get the most from your logs.</summary>
    <content type="html">&lt;p&gt;Most people only realize the necessity of logs when they need them the most. But when your application breaks, user
complaints start flooding in, and you have no clue how to fix it, it&amp;#39;s too late to add some log messages that might have
helped.&lt;/p&gt;
&lt;p&gt;Good logs pay for themselves tenfold. They make it a breeze to diagnose those tricky bugs, and if you do logs right,
they can alert you of issues even before your users notice. But what does it mean to &amp;#39;do logging right&amp;#39;?&lt;/p&gt;
&lt;p&gt;Logging is easy to start with but hard to master. In this post, we&amp;#39;ll dive into how you
can use your Rails application&amp;#39;s logs to their full potential.&lt;/p&gt;
&lt;h2&gt;Logging in Rails&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s talk about the basics first. When you start a new Rails application, logging is already set up for you. Rails will
initialize a new instance of &lt;code&gt;ActiveSupport::Logger&lt;/code&gt; that you can use anywhere within your application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;Rails.logger.debug(&amp;#39;Hello logs!&amp;#39;)

I, [2019-01-04 05:14:33 #123] INFO -- Main: Hello Logs!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Rails logger writes to the standard output or &lt;code&gt;log/&amp;lt;environment&amp;gt;.log&lt;/code&gt; and will automatically log incoming requests
or executed queries in addition to any log messages you write explicitly.&lt;/p&gt;
&lt;p&gt;There are numerous ways in which you can configure Rails logs, as outlined excellently in the &lt;a href=&quot;https://guides.rubyonrails.org/debugging_rails_applications.html#the-logger&quot;&gt;documentation for Rails&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To make the most of our logs, we are especially interested in
setting log levels as well as log formatting.&lt;/p&gt;
&lt;p&gt;But before tackling that, let&amp;#39;s talk about why we should write logs.&lt;/p&gt;
&lt;h2&gt;Good Logging, Bad Logging&lt;/h2&gt;
&lt;p&gt;The purpose of logs is to inform you about system events so you can react to them. For example, when
an error happens, a log message should tell you about it in a way you can understand.&lt;/p&gt;
&lt;p&gt;How well you understand a log
message depends on how descriptive and contextual it is. A descriptive log message provides relevant information about
what happened. A contextual log message includes information about a system&amp;#39;s state when the system wrote it.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s consider a simple example to see why we need both. Here is some code that calls an external API and returns its
response when a user performs a request.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def call_external_api(user_id, payload)
  Rails.logger.info(&amp;#39;Method entered&amp;#39;)
  client = Client.new(user_id)
  response = client.request(payload)
  if response.ok?
    Rails.logger.info(&amp;#39;Response success&amp;#39;)
    return response
  else
    Rails.logger.info(&amp;#39;Response failure&amp;#39;)
    return response
  end
  rescue ClientError =&amp;gt; e
    logger.debug(&amp;#39;An error occurred)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&amp;#39;s imagine a situation where a customer reports an issue, and you look into the logs for help. This is what you
might see:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;I, [2022-07-10T10:40:34.827604 #100038]  INFO -- : [42f2e074-d53f-41e4-adca-13f59c6383ec] Processing by UserController#action as HTML
I, [2022-07-10T10:40:34.828084 #100038]  INFO -- : [42f2e074-d53f-41e4-adca-13f59c6383ec] Method entered
I, [2022-07-10T10:40:34.933263 #100038]  INFO -- : [42f2e074-d53f-41e4-adca-13f59c6383ec] Response success
I, [2022-07-10T10:40:34.933803 #100038]  INFO -- : [42f2e074-d53f-41e4-adca-13f59c6383ec] Completed 200 OK in 106ms (Views: 0.2ms | Allocations: 2135)
I, [2022-07-10T10:40:35.959454 #100038]  INFO -- : [458a209f-6b0e-4d29-a9b0-449d6ff3d29a] Started GET &amp;quot;/action&amp;quot; for 127.0.0.1 at 2022-07-10 10:40:35 +0200
I, [2022-07-10T10:40:35.959975 #100038]  INFO -- : [458a209f-6b0e-4d29-a9b0-449d6ff3d29a] Processing by UserController#action as HTML
I, [2022-07-10T10:40:35.960081 #100038]  INFO -- : [458a209f-6b0e-4d29-a9b0-449d6ff3d29a] Method entered
I, [2022-07-10T10:40:36.043971 #100038]  INFO -- : [458a209f-6b0e-4d29-a9b0-449d6ff3d29a] Response failure
I, [2022-07-10T10:40:36.044316 #100038]  INFO -- : [458a209f-6b0e-4d29-a9b0-449d6ff3d29a] Completed 200 OK in 84ms (Views: 0.1ms | Allocations: 1297)
I, [2022-07-10T10:42:49.701879 #103892]  INFO -- : [e02c3a8f-c081-40aa-90fb-9d1474126403] Started GET &amp;quot;/action&amp;quot; for 127.0.0.1 at 2022-07-10 10:42:49 +0200
I, [2022-07-10T10:42:49.703400 #103892]  INFO -- : [e02c3a8f-c081-40aa-90fb-9d1474126403] Processing by UserController#action as HTML
I, [2022-07-10T10:42:49.703745 #103892]  INFO -- : [e02c3a8f-c081-40aa-90fb-9d1474126403] Method entered
I, [2022-07-10T10:42:49.775846 #103892]  INFO -- : [e02c3a8f-c081-40aa-90fb-9d1474126403] An error occurred
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These log messages provide some information, but not nearly enough. We are no closer to finding out what&amp;#39;s
responsible for the error the customer has reported. These log messages lack descriptiveness and context. They are
noise. How can we improve them?&lt;/p&gt;
&lt;h3&gt;Log Levels in Rails&lt;/h3&gt;
&lt;p&gt;The default Rails logger provides the log levels &lt;code&gt;DEBUG&lt;/code&gt;, &lt;code&gt;INFO&lt;/code&gt;, &lt;code&gt;WARN&lt;/code&gt;, and &lt;code&gt;ERROR&lt;/code&gt;. Using them, you can group log
messages into different categories of relevance. This not only helps you filter log messages, but also provides some context.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;It can be challenging to decide when to use which log level. Here are some rules of thumb:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DEBUG&lt;/code&gt;: Use this log level for detailed information about actions happening within the system. You may use debug statements when methods are entered or exited, or anywhere you think it may add value during debugging.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INFO&lt;/code&gt;: Whenever your system changes its state, or some relevant event occurs, reach for the &lt;code&gt;INFO&lt;/code&gt; level. Examples of common info messages in the context of Rails applications are received requests, requests sent to external APIs, or jobs starting and finishing.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WARN&lt;/code&gt;: Use this log to signify that something unexpected has happened. It&amp;#39;s not a problem yet (because your application can handle it), but if it keeps happening, it might warrant your attention.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ERROR&lt;/code&gt;: Use this log level when an error happens. Errors are invalid application states that you must resolve as soon as possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can change the kinds of log messages your application writes by modifying the respective environment file.
Normally, Rails will discard &lt;code&gt;DEBUG&lt;/code&gt; messages in production, but you can change that.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/production.rb
Rails.logger.level = :debug
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s apply the log-level suggestions above to our code sample.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def call_external_api(user_id, payload)
  Rails.logger.debug(&amp;#39;Method entered&amp;#39;)
  client = Client.new(user_id)
  response = client.request(payload)
  if response.ok?
    Rails.logger.info(&amp;#39;Response success&amp;#39;)
    return response
  else
    Rails.logger.warn(&amp;#39;Response failure&amp;#39;)
    return response
  end
  rescue ClientError =&amp;gt; e
    logger.error(&amp;#39;An error occurred&amp;#39;)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When debugging, we can now identify root causes for issues by looking into &lt;code&gt;WARN&lt;/code&gt; and &lt;code&gt;ERROR&lt;/code&gt; messages. Sadly, our log
messages haven&amp;#39;t gotten any more descriptive.&lt;/p&gt;
&lt;h3&gt;Descriptive Log Messages&lt;/h3&gt;
&lt;p&gt;Descriptive log messages leave no room for interpretation. They provide the details necessary to give the
reader instant knowledge about what happened.&lt;/p&gt;
&lt;p&gt;When you read messages such as &lt;code&gt;&amp;#39;An error occurred&amp;#39;&lt;/code&gt;, you are left wondering what the error is. You want to avoid such
confusion when writing your log messages. The most important thing when writing descriptive log messages is to
put yourself in the shoes of the log reader. Will they get all the information they need when reading your logs?&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s change the message &lt;code&gt;&amp;#39;An error occurred&amp;#39;&lt;/code&gt; to the error itself, including its message.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;logger.error { &amp;quot;#{e}: #{e.message}&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s an improvement. Let&amp;#39;s check the other log messages in our example. &lt;code&gt;&amp;#39;Method entered&amp;#39;&lt;/code&gt; doesn&amp;#39;t tell us which method
was called, &lt;code&gt;&amp;#39;Response success&amp;#39;&lt;/code&gt; leaves out the actual response, and &lt;code&gt;&amp;#39;Response failure&amp;#39;&lt;/code&gt; provides no information about the
nature of the failure. Let&amp;#39;s change that.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def call_external_api(user_id, payload)
  Rails.logger.debug(&amp;#39;Calling external api&amp;#39;)
  client = Client.new(user_id)
  response = client.request(payload)
  if response.ok?
    Rails.logger.info { &amp;quot;Request success, received #{response.body}&amp;quot; }
    return response
  else
    Rails.logger.warn { &amp;quot;Request returned #{response.code}, #{response.body}&amp;quot; }
    return response
  end
  rescue ClientError =&amp;gt; e
    logger.error { &amp;quot;#{e}: #{e.message}&amp;quot; }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: You might have noticed we use the block syntax when performing string interpolation with our logs. Using it
avoids unnecessary computation when the application&amp;#39;s log level is higher than the log messages&amp;#39; level. For example,
&lt;code&gt;log.debug(&amp;quot;Some #{concatenation}&amp;quot;)&lt;/code&gt; will always perform the string concatenation, but &lt;code&gt;log.debug { &amp;quot;Some #{concatenation}&amp;quot; }&lt;/code&gt;
will only do so when the log level is set to debug.&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;I, [2022-07-30T11:19:13.742108 #122944]  INFO -- : [29d1ced1-3d05-4fbf-8b75-2a50e87ad749] Started GET &amp;quot;/users&amp;quot; for 127.0.0.1 at 2022-07-30 11:19:13 +0200
I, [2022-07-30T11:19:13.743058 #122944]  INFO -- : [29d1ced1-3d05-4fbf-8b75-2a50e87ad749] Processing by UsersController#index as HTML
D, [2022-07-30T11:19:13.743248 #122944] DEBUG -- : [29d1ced1-3d05-4fbf-8b75-2a50e87ad749] Calling external api, user 1, payload {:search=&amp;gt;&amp;quot;name&amp;quot;}
I, [2022-07-30T11:19:13.844355 #122944]  INFO -- : [29d1ced1-3d05-4fbf-8b75-2a50e87ad749] Request completed. Received {&amp;quot;userId&amp;quot;=&amp;gt;1, &amp;quot;id&amp;quot;=&amp;gt;1, &amp;quot;title&amp;quot;=&amp;gt;&amp;quot;title&amp;quot;, &amp;quot;completed&amp;quot;=&amp;gt;false}
I, [2022-07-30T11:19:13.844836 #122944]  INFO -- : [29d1ced1-3d05-4fbf-8b75-2a50e87ad749] Completed 200 OK in 102ms (Views: 0.2ms | Allocations: 2195)
I, [2022-07-30T11:20:09.501016 #123422]  INFO -- : [598eb218-d3c9-4e92-9236-13281cc660af] Started GET &amp;quot;/users&amp;quot; for 127.0.0.1 at 2022-07-30 11:20:09 +0200
I, [2022-07-30T11:20:09.502518 #123422]  INFO -- : [598eb218-d3c9-4e92-9236-13281cc660af] Processing by UsersController#index as HTML
D, [2022-07-30T11:20:09.502848 #123422] DEBUG -- : [598eb218-d3c9-4e92-9236-13281cc660af] Calling external api, user 1, payload {:search=&amp;gt;&amp;quot;&amp;quot;}
W, [2022-07-30T11:20:09.574884 #123422]  WARN -- : [598eb218-d3c9-4e92-9236-13281cc660af] Request failed with 400: Empty search
I, [2022-07-30T11:20:09.575415 #123422]  INFO -- : [598eb218-d3c9-4e92-9236-13281cc660af] Completed 200 OK in 73ms (Views: 0.2ms | Allocations: 2106)
I, [2022-07-30T11:20:41.229494 #125518]  INFO -- : [77b70a25-268c-43ea-a3da-8788086165f2] Started GET &amp;quot;/users&amp;quot; for 127.0.0.1 at 2022-07-30 11:20:41 +0200
I, [2022-07-30T11:20:41.230735 #125518]  INFO -- : [77b70a25-268c-43ea-a3da-8788086165f2] Processing by UsersController#index as HTML
D, [2022-07-30T11:20:41.231039 #125518] DEBUG -- : [77b70a25-268c-43ea-a3da-8788086165f2] Calling external api, user 1, payload {:search=&amp;gt;&amp;quot;long name&amp;quot;}
E, [2022-07-30T11:20:41.301237 #125518] ERROR -- : [77b70a25-268c-43ea-a3da-8788086165f2] Request timeout exceeded
I, [2022-07-30T11:20:41.301878 #125518]  INFO -- : [77b70a25-268c-43ea-a3da-8788086165f2] Completed 200 OK in 71ms (Views: 0.3ms | Allocations: 2085)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our logs read much better now. There is little doubt about what each log message signifies.&lt;/p&gt;
&lt;h3&gt;Further Context for Log Messages&lt;/h3&gt;
&lt;p&gt;Our new log messages provide a clear picture of what happened. Because this is a simple example, we also have a good
understanding of why certain things happened. In reality, it&amp;#39;s usually not that easy.&lt;/p&gt;
&lt;p&gt;Providing additional information about the context in which the system wrote each message can significantly help with
debugging.&lt;/p&gt;
&lt;p&gt;For example, when logging requests or responses, it may be helpful to know who performed the request. Attaching a stack trace for errors might be useful so we can gather additional information about why the error occurred.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def call_external_api(user_id, payload)
  Rails.logger.debug { &amp;quot;Calling external API. user_id: #{user_id}, payload: #{payload}&amp;quot; }
  client = Client.new(user_id)
  response = client.request(payload)
  if response.ok?
    Rails.logger.info { &amp;quot;Request completed. Received #{response.parsed_response}. user_id: #{user_id}, payload: #{payload}&amp;quot; }
    return response
  else
    Rails.logger.warn { &amp;quot;Request failed with #{response.code}: #{response.parsed_response}. user_id: #{user_id}, payload: #{payload}&amp;quot; }
    return response
  end
  rescue ClientError =&amp;gt; e
  logger.error(&amp;quot;#{e.message} (#{e.class}), #{ e.backtrace.drop(1).map { |s| &amp;quot;\t#{s}&amp;quot; } }&amp;quot;)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: You have likely noticed that creating log messages with contextual information can be cumbersome when you use
Rails&amp;#39; default logger. Luckily, custom loggers such as &lt;a href=&quot;https://github.com/tilfin/ougai&quot;&gt;Ougai&lt;/a&gt; or
&lt;a href=&quot;https://github.com/hschne/mr-loga-loga&quot;&gt;MrLogaLoga&lt;/a&gt; make this a lot easier.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Structured Logging&lt;/h2&gt;
&lt;p&gt;Once you&amp;#39;ve made your logs readable for humans, you are ready to take things to the next level. You now need to make your logs readable
by machines so you can use them for monitoring and data analysis.&lt;/p&gt;
&lt;p&gt;My go-to format is JSON, but depending on which tools
you use, you may prefer other log formats such as &lt;a href=&quot;https://www.elastic.co/logstash/&quot;&gt;Logstash&lt;/a&gt;. You can change the
format of your log messages by creating a custom log formatter.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/json_log_formatter.rb
class JsonLogFormatter &amp;lt; ::Logger::Formatter
  def call(severity, time, progname, msg)
    json = { time: time, progname: progname, severity: severity, message: msg2str(msg) }
      .compact_blank
      .to_json
    &amp;quot;#{json}\n&amp;quot;
  end
end

# application.rb
config.log_formatter = JsonLogFormatter.new
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;{&amp;quot;time&amp;quot;:&amp;quot;2022-08-07T18:57:20.261+02:00&amp;quot;,&amp;quot;severity&amp;quot;:&amp;quot;INFO&amp;quot;,&amp;quot;message&amp;quot;:&amp;quot;Request completed&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rather than writing custom formatters yourself, you can use one of the many gems that provide custom log formatters. I
recommend &lt;a href=&quot;https://github.com/roidrage/lograge&quot;&gt;Lograge&lt;/a&gt;, which not only offers out-of-the-box formatting but also
cleans up Rails&amp;#39; somewhat verbose request formatting so it&amp;#39;s much easier to read.&lt;/p&gt;
&lt;h2&gt;Rails Logging with AppSignal&lt;/h2&gt;
&lt;p&gt;Now that your logs are nice and readable, it would be nice to make them more accessible. After all, you don&amp;#39;t want to
SSH into some machine and tail or grep the logs there, right?&lt;/p&gt;
&lt;p&gt;Luckily, AppSignal has recently launched a logging feature in beta!
This allows you to inspect and analyze Rails logs directly in the AppSignal web interface.&lt;/p&gt;
&lt;p&gt;You can access our logging beta through the &amp;#39;Logging&amp;#39; tab in the left-hand side menu:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-03/logging-menu.png&quot; alt=&quot;logs menu&quot;/&gt;&lt;/p&gt;
&lt;p&gt;If you follow the steps in our &lt;a href=&quot;https://docs.appsignal.com/logging/platforms/integrations/ruby.html&quot;&gt;Ruby logging docs&lt;/a&gt;, you can configure the Rails Logger to use the &lt;code&gt;AppSignal::Logger&lt;/code&gt; class by placing the code below in the &lt;code&gt;config/environment.rb&lt;/code&gt; file before &lt;code&gt;initialization&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Use AppSignal&amp;#39;s logger
Rails.logger = Appsignal::Logger.new(&amp;quot;rails&amp;quot;)

# Initialize the Rails application.
Rails.application.initialize!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also ingest structured logs in JSON format or with Lograge.&lt;/p&gt;
&lt;p&gt;Once you&amp;#39;ve set everything up, you should see your Rails logs show up on AppSignal!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-03/logging-rails.png&quot; alt=&quot;logs&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Logging and Error Reporting&lt;/h2&gt;
&lt;p&gt;You&amp;#39;re reading this on the AppSignal blog, so you might wonder: why should I care about logging if I&amp;#39;m already using a nice error reporting platform?&lt;/p&gt;
&lt;p&gt;It&amp;#39;s a good question. Error tracking tools provide detailed information about application errors. They capture the full
stack trace of any application error and provide lots of contextual information out of the box.&lt;/p&gt;
&lt;p&gt;However, while most
tools allow you to capture events other than errors, sending these tools an arbitrary amount of
messages is generally infeasible. You won&amp;#39;t be able to replace the detailed, historical information that well-written logs provide.&lt;/p&gt;
&lt;p&gt;In reality, well-written logs augment and support &lt;a href=&quot;https://www.appsignal.com/tour/errors&quot;&gt;error tracking tools&lt;/a&gt;. You should likely use both.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we looked into how you can get the most out of your logs. We saw that getting started with logging in Rails is easy, but writing useful
logs can be challenging.&lt;/p&gt;
&lt;p&gt;Logs that lack descriptiveness or context will end up just filling up your disk while providing little value. However, logs that use the correct log levels and provide readers with the information they need are a great asset.&lt;/p&gt;
&lt;p&gt;Once you&amp;#39;ve mastered writing great log messages, you can take things further and format them in a way that allows for
log analysis and easy filtering.&lt;/p&gt;
&lt;p&gt;We also explored how you can use AppSignal&amp;#39;s new logging feature to access your logs easily. Finally, we touched on how logs should augment error tracking tools rather than replace them.&lt;/p&gt;
&lt;p&gt;Happy logging!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Storing Ephemeral UI State with Kredis for Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/02/22/storing-ephemeral-ui-state-with-kredis-for-rails.html"/>
    <id>https://blog.appsignal.com/2023/02/22/storing-ephemeral-ui-state-with-kredis-for-rails.html</id>
    <published>2023-02-22T00:00:00+00:00</published>
    <updated>2023-02-22T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s dive into how Kredis works and how you can use it to store short-term UI state.</summary>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://github.com/rails/kredis&quot;&gt;Kredis (Keyed Redis)&lt;/a&gt; is a recent addition to the Rails developer&amp;#39;s toolkit. It strives to simplify storing and accessing structured data on Redis.&lt;/p&gt;
&lt;p&gt;In this first part of a two-part series, we&amp;#39;ll start by going into how Kredis works. We&amp;#39;ll then run through an example use case for storing ephemeral UI state using a bespoke Redis key.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;An Introduction to Kredis for Rails&lt;/h2&gt;
&lt;p&gt;Kredis is a Railtie that provides convenient wrappers to streamline its use in three ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ruby-esque API&lt;/strong&gt;: For example, collection types like &lt;code&gt;Kredis.list&lt;/code&gt; or &lt;code&gt;Kredis.set&lt;/code&gt; emulate native Ruby types (and their respective API) as much as possible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typings&lt;/strong&gt;: Especially handy for collections, Kredis can handle &lt;a href=&quot;https://github.com/rails/kredis/blob/main/lib/kredis/type_casting.rb&quot;&gt;type casting&lt;/a&gt; the elements from/to standard data types (e.g., &lt;code&gt;datetime&lt;/code&gt;, &lt;code&gt;json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ActiveRecord DSL&lt;/strong&gt;: Probably the library&amp;#39;s biggest asset, it allows you to easily connect any Redis data structure with a &lt;em&gt;specific model instance&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;#39;s an example from the &lt;a href=&quot;https://github.com/rails/kredis&quot;&gt;README&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Person &amp;lt; ApplicationRecord
  kredis_list :names
  kredis_unique_list :skills, limit: 2
  kredis_enum :morning, values: %w[ bright blue black ], default: &amp;quot;bright&amp;quot;
  kredis_counter :steps, expires_in: 1.hour
end

person = Person.find(5)
person.names.append &amp;quot;David&amp;quot;, &amp;quot;Heinemeier&amp;quot;, &amp;quot;Hansson&amp;quot; # =&amp;gt; RPUSH people:5:names &amp;quot;David&amp;quot; &amp;quot;Heinemeier&amp;quot; &amp;quot;Hansson&amp;quot;
true == person.morning.bright?                       # =&amp;gt; GET people:5:morning
person.morning.value = &amp;quot;blue&amp;quot;                        # =&amp;gt; SET people:5:morning
true == person.morning.blue?                         # =&amp;gt; GET people:5:morning
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kredis&amp;#39; major benefit is the ease it provides to store ephemeral information associated with a certain record, but &lt;em&gt;independent of the session&lt;/em&gt;. Typically, when you need to persist data in Rails, you have a few options — of which the two most common ones are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ActiveRecord&lt;/strong&gt;: In most cases, this requires adding a column or otherwise patching your data model. A migration is needed, plus the optional backfilling of old records.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session&lt;/strong&gt;: The default key/value store of every Rails app and requires no or little setup. The downside is that data stored in it doesn&amp;#39;t survive a login/logout cycle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kredis brings a third option to the table. Little setup is required, apart from invoking the DSL in the model. But unless your Redis instance goes down, your data is stored across sessions, and even devices. So a good use case for Kredis is uncritical information that you want to share across device borders, e.g., in a web app and a companion mobile app.&lt;/p&gt;
&lt;h2&gt;Case Study: Persist and Restore a Collapsed/Expanded UI State Using Kredis&lt;/h2&gt;
&lt;p&gt;A typical instance of a good use case for Kredis is when persisting UI state, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sidebar open/closed state&lt;/li&gt;
&lt;li&gt;Tree view open/closed state&lt;/li&gt;
&lt;li&gt;Accordion collapsed/expanded state&lt;/li&gt;
&lt;li&gt;Custom dashboard layout&lt;/li&gt;
&lt;li&gt;How many lines of a data table to display&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Exemplarily, we will take a look at how to manage the collapsed/expanded state of a &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start out with a fresh Rails app, add &lt;code&gt;kredis&lt;/code&gt; to the bundle, and run its installer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails new ui-state-kredis
$ cd ui-state-kredis

$ bundle add kredis
$ bin/rails kredis:install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This will create a Redis configuration file in &lt;code&gt;config/redis/shared.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the rest of this article, I will assume that you have a local running Redis instance. On macOS with Homebrew, this is as easy as running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ brew install redis
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please consult the &lt;a href=&quot;https://redis.io/docs/getting-started/installation/&quot;&gt;official &amp;quot;Getting Started&amp;quot;&lt;/a&gt; guide for information on how to install Redis on your operating system.&lt;/p&gt;
&lt;h3&gt;User Authentication&lt;/h3&gt;
&lt;p&gt;We are going to use a &lt;code&gt;User&lt;/code&gt; model as the entity to store UI state information. To avoid bikeshedding here, let&amp;#39;s just use what &lt;a href=&quot;https://github.com/heartcombo/devise&quot;&gt;Devise&lt;/a&gt; provides out of the box:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add devise
$ bin/rails generate devise:install
$ bin/rails generate devise User
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then create an example user in the Rails console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails c

User.create(
  email: &amp;quot;julian@example.com&amp;quot;,
  password: &amp;quot;mypassword&amp;quot;,
  password_confirmation: &amp;quot;mypassword&amp;quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Our Example App: An Online Store&lt;/h3&gt;
&lt;p&gt;To illustrate how Kredis can help persist the state of a complex tree structure, let&amp;#39;s pretend we are running an online department store. To this end, we will scaffold &lt;code&gt;Department&lt;/code&gt; and &lt;code&gt;Product&lt;/code&gt; models. We include a self join from department to department, to create a two-level nested structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g scaffold Department name:string department:references
$ bin/rails g scaffold Product name:string department:references
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have to permit null parents, of course, to allow for our tree structure roots:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  class CreateDepartments &amp;lt; ActiveRecord::Migration[7.0]
    def change
      create_table :departments do |t|
        t.string :name
-       t.references :department, null: false, foreign_key: true
+       t.references :department, foreign_key: true

        t.timestamps
      end
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &lt;code&gt;Department&lt;/code&gt; and &lt;code&gt;Product&lt;/code&gt; models are defined as such:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Department &amp;lt; ApplicationRecord
  belongs_to :parent, class_name: &amp;quot;Department&amp;quot;, optional: true
  has_many :children, class_name: &amp;quot;Department&amp;quot;, foreign_key: &amp;quot;department_id&amp;quot;
  has_many :products
end

class Product &amp;lt; ApplicationRecord
  belongs_to :department
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Finally, we use &lt;a href=&quot;https://github.com/faker-ruby/faker&quot;&gt;faker&lt;/a&gt; to generate some seed data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add faker
$ bin/rails c

5.times do
  Department.create(
    name: Faker::Commerce.unique.department(max: 1),
    children: (0..2).map do
      Department.new(
        name: Faker::Commerce.unique.department(max: 1),
        products: (0..4).map do
          Product.new(name: Faker::Commerce.unique.product_name)
        end
      )
    end
  )
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Scaffolding a Storefront&lt;/h3&gt;
&lt;p&gt;We&amp;#39;ll create a very simple &lt;code&gt;HomeController&lt;/code&gt; that will act as our shop&amp;#39;s storefront.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g controller Home index --no-helper
      create  app/controllers/home_controller.rb
       route  get &amp;#39;home/index&amp;#39;
      invoke  erb
      create    app/views/home
      create    app/views/home/index.html.erb
      invoke  test_unit
      create    test/controllers/home_controller_test.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We perform a self join on the departments&amp;#39; children to retrieve only those which actually have subdepartments (or, in other words, are our tree&amp;#39;s roots):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/home_controller.rb

class HomeController &amp;lt; ApplicationController
  def index
    @departments = Department.joins(:children).distinct
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the index view, we set up a nested tree view using two levels of &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; elements for our departments:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/home/index.html.erb --&amp;gt;

&amp;lt;% @departments.each do |dep| %&amp;gt;
  &amp;lt;details&amp;gt;
    &amp;lt;summary&amp;gt;&amp;lt;%= dep.name %&amp;gt;&amp;lt;/summary&amp;gt;

    &amp;lt;% dep.children.each do |child_dep| %&amp;gt;
      &amp;lt;details style=&amp;quot;margin-left: 1rem&amp;quot;&amp;gt;
        &amp;lt;summary&amp;gt;&amp;lt;%= child_dep.name %&amp;gt;&amp;lt;/summary&amp;gt;

        &amp;lt;ul&amp;gt;
          &amp;lt;% child_dep.products.each do |prod| %&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;%= prod.name %&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;% end %&amp;gt;
        &amp;lt;/ul&amp;gt;
      &amp;lt;/details&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/details&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right now we have a tree view of departments with intentionally silly product names that we can explore by opening and closing:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-02/ui-state-home-controller.png&quot; alt=&quot;&amp;quot;Storefront&amp;quot; Catalog&quot;/&gt;&lt;/p&gt;
&lt;p&gt;We&amp;#39;d like to persist the disclosure state of the individual categories, which we will tend to next.&lt;/p&gt;
&lt;h3&gt;Persisting UI State of Categories in Kredis&lt;/h3&gt;
&lt;p&gt;Here is what we are going to do, step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add a &lt;code&gt;kredis_set&lt;/code&gt; called &lt;code&gt;open_department_ids&lt;/code&gt; to the &lt;code&gt;User&lt;/code&gt; model. The reason we are using a set here is that it doesn&amp;#39;t allow duplicates, so we can safely add and remove our departments.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;UIStateController&lt;/code&gt; that will receive the following params:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;department_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;open&lt;/code&gt; state of that department&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It will then add or remove this department to the &lt;code&gt;kredis_set&lt;/code&gt; for the currently logged-in user.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;strong&gt;Stimulus controller&lt;/strong&gt; which will listen for the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details#events&quot;&gt;toggle&lt;/a&gt; event on the details element and send over the respective payload.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;#39;s get into it!&lt;/p&gt;
&lt;p&gt;Adding said Kredis data structure to the &lt;code&gt;User&lt;/code&gt; model is as easy as calling &lt;code&gt;kredis_set&lt;/code&gt; and passing an identifier:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/user.rb

  class User &amp;lt; ApplicationRecord
    # Include default devise modules. Others available are:
    # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
    devise :database_authenticatable, :registerable,
           :recoverable, :rememberable, :validatable

+   kredis_set :open_department_ids
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we generate a &lt;code&gt;UIStateController&lt;/code&gt; to receive the UI state updates. Note that we have to configure the generated route to be a &lt;code&gt;patch&lt;/code&gt; endpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g controller UIState update --no-helper --skip-template-engine
      create  app/controllers/ui_state_controller.rb
       route  get &amp;#39;ui_state/update&amp;#39;
      invoke  test_unit
      create    test/controllers/ui_state_controller_test.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  Rails.application.routes.draw do
-   get &amp;#39;ui_state/update&amp;#39;
+   patch &amp;#39;ui_state/update&amp;#39;
    get &amp;#39;home/index&amp;#39;
    resources :products
    resources :departments
    devise_for :users
    # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

    # Defines the root path route (&amp;quot;/&amp;quot;)
    root &amp;quot;home#index&amp;quot;
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our first encounter with Kredis&amp;#39; API is in the controller. We can see that it tries to conform to Ruby developers&amp;#39; expectations as closely as possible, so you can add to the set using &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, and delete using &lt;code&gt;remove&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/controllers/ui_state_controller.rb

class UiStateController &amp;lt; ApplicationController
  def update
    if ui_state_params[:open] == &amp;quot;true&amp;quot;
      current_user.open_department_ids &amp;lt;&amp;lt; params[:department_id]
    else
      current_user.open_department_ids.remove(params[:department_id])
    end

    head :ok
  end

  private

  def ui_state_params
    params.permit(:department_id, :open)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&amp;#39;s happening here is that we toggle the presence of a specific &lt;code&gt;department_id&lt;/code&gt; in the set based on the &lt;code&gt;open&lt;/code&gt; param being handed over from the client. To complete the picture, we must write some client-side code to transmit these UI state changes.&lt;/p&gt;
&lt;p&gt;We are going to use &lt;code&gt;@rails/request.js&lt;/code&gt; to perform the actions, so we have to pin it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/importmap pin @rails/request.js
Pinning &amp;quot;@rails/request.js&amp;quot; to https://ga.jspm.io/npm:@rails/request.js@0.0.8/src/index.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a new Stimulus controller that we&amp;#39;ll attach to a specific &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element, we append the department ID and its &lt;code&gt;open&lt;/code&gt; state to a &lt;code&gt;FormData&lt;/code&gt; object, and submit it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/ui_state_controller.js

import { Controller } from &amp;quot;@hotwired/stimulus&amp;quot;;
import { patch } from &amp;quot;@rails/request.js&amp;quot;;

export default class extends Controller {
  static values = {
    departmentId: Number,
  };

  async toggle() {
    const body = new FormData();
    body.append(&amp;quot;open&amp;quot;, this.element.open);
    body.append(&amp;quot;department_id&amp;quot;, this.departmentIdValue);

    await patch(&amp;quot;/ui_state/update&amp;quot;, {
      body,
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We edit our view code as proposed, and listen for the &lt;code&gt;toggle&lt;/code&gt; event of each &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element to trigger the UI state updates:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/home/index.html.erb --&amp;gt;

  &amp;lt;% @departments.each do |dep| %&amp;gt;
-   &amp;lt;details&amp;gt;
+   &amp;lt;details
+     data-controller=&amp;quot;ui-state&amp;quot;
+     data-action=&amp;quot;toggle-&amp;gt;ui-state#toggle&amp;quot;
+     data-ui-state-department-id-value=&amp;quot;&amp;lt;%= dep.id %&amp;gt;&amp;quot;
+   &amp;gt;
      &amp;lt;summary&amp;gt;&amp;lt;%= dep.name %&amp;gt;&amp;lt;/summary&amp;gt;
      &amp;lt;% dep.children.each do |child_dep| %&amp;gt;
-       &amp;lt;details style=&amp;quot;margin-left: 1rem&amp;quot;&amp;gt;
+       &amp;lt;details style=&amp;quot;margin-left: 1rem&amp;quot;
+         data-controller=&amp;quot;ui-state&amp;quot;
+         data-action=&amp;quot;toggle-&amp;gt;ui-state#toggle&amp;quot;
+         data-ui-state-department-id-value=&amp;quot;&amp;lt;%= child_dep.id %&amp;gt;&amp;quot;
+       &amp;gt;
          &amp;lt;summary&amp;gt;&amp;lt;%= child_dep.name %&amp;gt;&amp;lt;/summary&amp;gt;

          &amp;lt;ul&amp;gt;
            &amp;lt;% child_dep.products.each do |prod| %&amp;gt;
              &amp;lt;li&amp;gt;&amp;lt;%= prod.name %&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;% end %&amp;gt;
          &amp;lt;/ul&amp;gt;
        &amp;lt;/details&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;/details&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Rehydrate the DOM Manually&lt;/h3&gt;
&lt;p&gt;The only component missing to go full circle is &lt;em&gt;rehydrating&lt;/em&gt; our DOM to the desired state once the user refreshes the page. We do this manually by adding the &lt;code&gt;open&lt;/code&gt; attribute to the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; node (if its department ID is present in the Kredis set):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/home/index.html.erb --&amp;gt;

  &amp;lt;% @departments.each do |dep| %&amp;gt;
    &amp;lt;details
      data-controller=&amp;quot;ui-state&amp;quot;
      data-action=&amp;quot;toggle-&amp;gt;ui-state#toggle&amp;quot;
      data-ui-state-department-id-value=&amp;quot;&amp;lt;%= dep.id %&amp;gt;&amp;quot;
+     &amp;lt;%= &amp;quot;open&amp;quot; if current_user.open_department_ids.include?(dep.id) %&amp;gt;
    &amp;gt;
      &amp;lt;summary&amp;gt;&amp;lt;%= dep.name %&amp;gt;&amp;lt;/summary&amp;gt;

      &amp;lt;% dep.children.each do |child_dep| %&amp;gt;
        &amp;lt;details style=&amp;quot;margin-left: 1rem&amp;quot;
          data-controller=&amp;quot;ui-state&amp;quot;
          data-action=&amp;quot;toggle-&amp;gt;ui-state#toggle&amp;quot;
          data-ui-state-department-id-value=&amp;quot;&amp;lt;%= child_dep.id %&amp;gt;&amp;quot;
+         &amp;lt;%= &amp;quot;open&amp;quot; if current_user.open_department_ids.include?(child_dep.id) %&amp;gt;
        &amp;gt;
          &amp;lt;summary&amp;gt;&amp;lt;%= child_dep.name %&amp;gt;&amp;lt;/summary&amp;gt;

          &amp;lt;ul&amp;gt;
            &amp;lt;% child_dep.products.each do |prod| %&amp;gt;
              &amp;lt;li&amp;gt;&amp;lt;%= prod.name %&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;% end %&amp;gt;
          &amp;lt;/ul&amp;gt;
        &amp;lt;/details&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;/details&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, here&amp;#39;s the result. Note that the open/closed state of individual tree nodes is preserved over 2 levels:&lt;/p&gt;
&lt;Video fileName=&quot;/images/blog/2023-02/manual-rehydration&quot; /&gt;

&lt;h2&gt;Up Next: A Generalized User-local Container for UI State&lt;/h2&gt;
&lt;p&gt;In the first part of this two-part series, we introduced Kredis and explored how to persist and restore a collapsed/expanded UI state with Kredis.&lt;/p&gt;
&lt;p&gt;We used the example of an online department store to highlight how Kredis can persist a complex tree structure&amp;#39;s state, before finally manually rehydrating the DOM.&lt;/p&gt;
&lt;p&gt;However, this does mean that we have to invent a lot of Kredis keys. Next time, we&amp;#39;ll dive into how we can address this with a generalized user-local container for UI state.&lt;/p&gt;
&lt;p&gt;Until then, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>What&#039;s New in Rails 7.1</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/02/15/whats-new-in-rails-7-1.html"/>
    <id>https://blog.appsignal.com/2023/02/15/whats-new-in-rails-7-1.html</id>
    <published>2023-02-15T00:00:00+00:00</published>
    <updated>2023-02-15T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s take a look at what we can expect from the upcoming Rails 7.1 release.</summary>
    <content type="html">&lt;p&gt;Rails 7 was a welcome release that brought a lot of significant features and changes. On the backend, Rails 7 introduced asynchronous query loading and Zeitwerk for autoloading. The frontend saw Hotwire becoming the default solution for new Rails apps.&lt;/p&gt;
&lt;p&gt;Rails 7.1 will add to these notable features. In this post, we&amp;#39;ll discuss some noteworthy additions that are likely to be shipped.&lt;/p&gt;
&lt;h2&gt;A New API for Async Queries in Rails&lt;/h2&gt;
&lt;p&gt;Building on an earlier feature from Rails 7, Rails 7.1 will make it possible to run some queries asynchronously. Rails 7 introduced &lt;code&gt;ActiveRecord::Relation#load_async&lt;/code&gt;, which schedules a query in a background thread pool, allowing us to do stuff like &lt;code&gt;Post.where(published: true).load_async&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In Rails 7.1, we&amp;#39;ll be able to run a lot more queries in background threads. Aggregate methods will run concurrently. Assuming you run two or more independent queries on a job or controller, query results may return quicker if your application is set up accordingly.&lt;/p&gt;
&lt;p&gt;For this to work as intended, there are two configuration options worth paying attention to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;config.active_record.async_query_executor
config.active_record.global_executor_concurrency
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Among the methods you can run asynchrously in Rails 7.1 are &lt;code&gt;async_sum&lt;/code&gt;, &lt;code&gt;async_pluck&lt;/code&gt;, and &lt;code&gt;async_count_by_sql&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Resetting Singular Associations&lt;/h2&gt;
&lt;p&gt;Rails 7.1 allows resetting the cache on singular associations. Currently, you can only clear the cache of &lt;code&gt;has_many&lt;/code&gt; associations with a class like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Teacher &amp;lt; ActiveRecord::Base
  has_many :students
  has_one :classroom
end

teacher = Teacher.first
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can only do &lt;code&gt;teacher.students.reset&lt;/code&gt; to clear the caches of the results returned by &lt;code&gt;teacher.students&lt;/code&gt;. Subsequent requests need to hit the database again for a fresh results set in case some data goes stale.&lt;/p&gt;
&lt;p&gt;With Rails 7.1, we&amp;#39;ll get the &lt;code&gt;reset&lt;/code&gt; method on a &lt;code&gt;has_one&lt;/code&gt; association. Using our example class above, Rails 7.1 will allow us to do &lt;code&gt;teacher.classroom.reset_teacher&lt;/code&gt; to clear the cache for the associations between &lt;code&gt;teacher&lt;/code&gt; and &lt;code&gt;classroom&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Disabling Methods Generated By &lt;code&gt;ActiveRecord#enum&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ActiveRecord#enum&lt;/code&gt; generates a bunch of methods if you create an enum Rails. Rails 7.1 will provide an option to opt out of these generated methods. Here&amp;#39;s a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Payment &amp;lt; ActiveRecord::Base
  enum :status, %i[succeeded failed], instance_methods: false
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails won&amp;#39;t generate auxiliary methods with &lt;code&gt;instance_methods: false&lt;/code&gt;. Currently, we expect to have methods like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;payment = Payment.first

payment.succeeded?
payment.failed?

payment.succeeded!
payment.failed!
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Support for Common Table Expressions&lt;/h2&gt;
&lt;p&gt;Rails 7.1 will have in-built support for Common Table Expressions (CTEs). This ensures that code will be more succinct but, more importantly, that we won&amp;#39;t have to use &lt;code&gt;Arel::Nodes&lt;/code&gt; for complex queries.&lt;/p&gt;
&lt;p&gt;With Rails 7.1, we&amp;#39;ll have a new &lt;code&gt;.with&lt;/code&gt; method to write queries similar to the one below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Post.with(
  posts_with_comments: Post.where(&amp;quot;comments_count &amp;gt; ?&amp;quot;, 0),
  posts_with_tags: Post.where(&amp;quot;tags_count &amp;gt; ?&amp;quot;, 0)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Support for Async Bulk Record Destruction&lt;/h2&gt;
&lt;p&gt;As mentioned, Rails 7.1 will introduce several ways to run code asynchronously. One such new addition to async code executions is the &lt;code&gt;destroy_association_async_batch_size&lt;/code&gt; configuration.&lt;/p&gt;
&lt;p&gt;With this new configuration, Rails applications can set a maximum number of records to be destroyed in a single background job by the &lt;code&gt;dependent: :destroy_async&lt;/code&gt; association.&lt;/p&gt;
&lt;p&gt;The default behavior, where all dependent records are destroyed in a single background job when the parent record is destroyed, will remain unchanged. However, if the number of dependent records exceeds the new configuration, they will be destroyed in multiple background jobs.&lt;/p&gt;
&lt;h2&gt;Other Rails 7.1 Updates&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;ActiveRecord::Relation#explain&lt;/code&gt; Accepts Options&lt;/h3&gt;
&lt;p&gt;Rails 7.1 will allow you to pass database systems that support &lt;code&gt;EXPLAIN&lt;/code&gt; options to &lt;code&gt;ActiveRecord::Relation#explain&lt;/code&gt;. An example query might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Customer.where(id: 1).joins(:orders).explain(:analyze, :verbose)
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Active Record &lt;code&gt;regroup&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Active Record will allow for &amp;quot;regrouping&amp;quot; queries with a new &lt;code&gt;regroup&lt;/code&gt; method that can be used like so: &lt;code&gt;Post.group(:title).regroup(:author)&lt;/code&gt;. This generates SQL equivalent to &lt;code&gt;SELECT posts.* FROM posts GROUP BY posts.author&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same can be achieved in current versions of Rails with more verbose code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt; Post.group(:title)..unscope(:group).group(:author)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;New &lt;code&gt;stub_const method&lt;/code&gt; for Testing&lt;/h3&gt;
&lt;p&gt;A new &lt;code&gt;stub_const&lt;/code&gt; method for &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; will be added that stubs a constant for a yield&amp;#39;s duration.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# World::List::Import::LARGE_IMPORT_THRESHOLD = 5000

stub_const(World::List::Import, :LARGE_IMPORT_THRESHOLD, 1) do
  assert_equal 1, World::List::Import::LARGE_IMPORT_THRESHOLD
end

assert_equal 5000, World::List::Import::LARGE_IMPORT_THRESHOLD = 5000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take note, however, that stubbing a constant will affect its value across all threads in a multi-threaded setup. This means that if multiple concurrent threads rely on the same constant, simultaneous and conflicting stubbing may occur.&lt;/p&gt;
&lt;h3&gt;Password Challenge via &lt;code&gt;has_secure_password&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Rails 7.1 has improved the functionality of &lt;code&gt;has_secure_password&lt;/code&gt; by adding a &lt;code&gt;password_challenge&lt;/code&gt; accessor and a corresponding validation. The validation will verify that a &lt;code&gt;password_challenge&lt;/code&gt; matches the stored &lt;code&gt;password_digest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With this, implementing a password challenge becomes as straightforward as a password confirmation. This also enables reusing the same error-handling logic in both the view and the controller.&lt;/p&gt;
&lt;p&gt;For instance, instead of writing separate code in the controller, you will simply use the existing logic for password confirmation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;password_params = params.require(:password).permit(
  :password_challenge,
  :password,
  :password_confirmation,
).with_defaults(password_challenge: &amp;quot;&amp;quot;)

if current_user.update(password_params)
  # perform some work
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Saving Attachments Returning the Blob&lt;/h3&gt;
&lt;p&gt;With Rails 7.1, when you save attachments to a record, the &lt;code&gt;attach&lt;/code&gt; method will return the attached blob or blobs. This enables the direct use of blob methods on the attachment. However, if the record fails to save, &lt;code&gt;attach&lt;/code&gt; will return &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s an example demonstrating its use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@user = User.create!(name: &amp;quot;Josh&amp;quot;)
avatar = @user.avatar.attach(params[:avatar])

# You can now directly call blob methods as follows:
avatar.download
avatar.url
avatar.variant(:thumb)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Storage of CSRF Tokens Outside of Sessions&lt;/h3&gt;
&lt;p&gt;Rails has introduced a new configuration option to address the excessive creation and eviction of millions of sessions for just storing a CSRF token when sessions are not stored in cookies.&lt;/p&gt;
&lt;p&gt;This option allows the use of a lambda function to store the CSRF token in a custom location, thus enabling the storage of CSRF tokens outside of sessions.&lt;/p&gt;
&lt;p&gt;You can also create custom strategy classes for storing CSRF tokens.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CustomStore
  def fetch(request)
    # Return the token from a custom location
  end

  def store(request, csrf_token)
    # Store the token in a custom location
  end

  def reset(request)
    # Delete the stored session token
  end
end

class ApplicationController &amp;lt; ActionController:x:Base
  protect_from_forgery store: CustomStore.new
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Validity Checking for PostgreSQL Indexes&lt;/h3&gt;
&lt;p&gt;Creating indexes as shown below may lead to an invalid index:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;add_index :account, :active, algorithm: :concurrently
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With Rails 7.1, you can verify an index&amp;#39;s validity as shown here:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;connection.index_exists?(:users, :email, valid: true)
connection.indexes(:users).select(&amp;amp;:valid?)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;ActiveRecord::QueryMethods#select&lt;/code&gt; Accepts a Hash&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ActiveRecord::QueryMethods#select&lt;/code&gt; in Rails 7.1 now accepts a hash of options. This is best demonstrated with an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# You can now write selects like this:
Post.joins(:comments).select(
  posts: { id: :post_id, title: :post_title },
  comments: { id: :comment_id, body: :comment_body}
)

# In place of this:
Post.joins(:comments).select(
  &amp;quot;posts.id as post_id, posts.title as post_title,
  comments.id as comment_id, comments.body as comment_body&amp;quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Number of Processors Match the Puma Worker Count&lt;/h3&gt;
&lt;p&gt;By default, newly generated Rails applications will have Puma workers that are capped at the total number of physical processors on the host machine. This default setting can always be changed in the &lt;code&gt;puma.rb&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;puma.rb&lt;/code&gt; file for newly-generated Rails applications will now look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if ENV[&amp;quot;RAILS_ENV&amp;quot;] == &amp;quot;production&amp;quot;
  worker_count = ENV.fetch(&amp;quot;WEB_CONCURRENCY&amp;quot;) { Concurrent.physical_processor_count }
  workers worker_count if worker_count &amp;gt; 1
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;preload&lt;/code&gt; and &lt;code&gt;eager_load&lt;/code&gt; Associations to Be Unscoped&lt;/h3&gt;
&lt;p&gt;Rails 7.1 will add the ability to unscope preloaded and eager loaded associations in a manner similar to how Active Record&amp;#39;s &lt;code&gt;includes&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;, and &lt;code&gt;joins&lt;/code&gt; methods work.&lt;/p&gt;
&lt;p&gt;This feature allows for the use of aggregate functions on &lt;code&gt;has_many&lt;/code&gt; associations previously loaded through &lt;code&gt;eager_load&lt;/code&gt; or &lt;code&gt;preload&lt;/code&gt; in existing queries.&lt;/p&gt;
&lt;p&gt;An example usage could look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;query.unscope(:eager_load, :preload).group(:id).select(:id)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Default Dockerfiles for New Rails Applications&lt;/h3&gt;
&lt;p&gt;Docker files are to be added as a default option for new Rails applications. The files include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.dockerignore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bin/docker-entrypoint&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These files serve as a starting point for deploying an application in a production environment and are not intended for use during development. However, if desired, you can skip these files by using the &lt;code&gt;--skip-docker&lt;/code&gt; option.&lt;/p&gt;
&lt;h3&gt;Default Health Controller&lt;/h3&gt;
&lt;p&gt;Rails 7.1 introduces a new endpoint for load balancers and uptime monitors. The endpoint, named &lt;code&gt;Rails::HealthController#show&lt;/code&gt;, is mapped to the &amp;quot;/up&amp;quot; path in newly generated Rails applications. This allows load balancers and uptime monitors to easily track an app&amp;#39;s availability.&lt;/p&gt;
&lt;p&gt;Note that monitoring the database, Redis, and internal network connections to microservices that an application relies on must be managed separately.&lt;/p&gt;
&lt;h3&gt;New &lt;code&gt;Rails.env.local?&lt;/code&gt; for Environment Checks&lt;/h3&gt;
&lt;p&gt;In Rails 7.1, a new &lt;code&gt;local?&lt;/code&gt; method can be used to simplify environment checks.&lt;/p&gt;
&lt;p&gt;You&amp;#39;ll be able to replace code like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if Rails.env.development? || Rails.env.test?
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if Rails.env.local?
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;New &lt;code&gt;ActiveRecord::Persistence#update_attribute!&lt;/code&gt; Method&lt;/h3&gt;
&lt;p&gt;Rails has added a new method, &lt;code&gt;ActiveRecord::Persistence#update_attribute!&lt;/code&gt;, which functions similarly to &lt;code&gt;update_attribute&lt;/code&gt; but uses &lt;code&gt;save!&lt;/code&gt; instead of &lt;code&gt;save&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s how you could use this new method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Apm &amp;lt; ActiveRecord::Base
  before_save :check_name

  def check_name = throw(:abort) if name == &amp;quot;abort&amp;quot;
end

monitor = Apm.create(name: &amp;quot;App Signal&amp;quot;)
# =&amp;gt; #&amp;lt;Apm name: &amp;quot;App Signal&amp;quot;&amp;gt;

monitor.update_attribute!(:name, &amp;quot;AppSignal&amp;quot;)
# =&amp;gt; #&amp;lt;Apm name: &amp;quot;AppSignal&amp;quot;&amp;gt;

monitor.update_attribute!(:name, &amp;quot;abort&amp;quot;)
# raises ActiveRecord::RecordNotSaved
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Templates Capable of Defining Accepted Locals&lt;/h3&gt;
&lt;p&gt;Templates will be enhanced with the option of required arguments that have default values.&lt;/p&gt;
&lt;p&gt;Currently, templates accept any locals as keyword arguments. With 7.1, Rails templates will define specific accepted locals through a magic comment.&lt;/p&gt;
&lt;p&gt;This improvement provides greater control and customization options for template behavior and functionality.&lt;/p&gt;
&lt;p&gt;A partial in Rails could now look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%# locals: (title: &amp;quot;Default title&amp;quot;, comment_count: 0) %&amp;gt;

&amp;lt;h2&amp;gt;&amp;lt;%= title %&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;span class=&amp;quot;comment-count&amp;quot;&amp;gt;&amp;lt;%= comment_count %&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;% title = local_assigns[:title] || &amp;quot;Default title&amp;quot; %&amp;gt;
&amp;lt;% comment_count = local_assigns[:comment_count] || 0 %&amp;gt;

&amp;lt;h2&amp;gt;&amp;lt;%= title %&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;span class=&amp;quot;comment-count&amp;quot;&amp;gt;&amp;lt;%= comment_count %&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;As you can see, Rails 7.1 promises a lot of further improvements on Rails 7.&lt;/p&gt;
&lt;p&gt;For more information on features, updates, and bug fixes, check out the &lt;a href=&quot;https://edgeguides.rubyonrails.org/7_1_release_notes.html&quot;&gt;Rails 7.1 release notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Guide to Rails View Helpers</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/02/01/a-guide-to-rails-view-helpers.html"/>
    <id>https://blog.appsignal.com/2023/02/01/a-guide-to-rails-view-helpers.html</id>
    <published>2023-02-01T00:00:00+00:00</published>
    <updated>2023-02-01T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out how you can keep your Rails views readable using helpers.</summary>
    <content type="html">&lt;p&gt;Views in Rails don&amp;#39;t do much besides showcasing what we want. Sure, they might render slightly different results depending on who&amp;#39;s watching (an admin or a logged-in user has a different experience than a guest user, for example), but they don&amp;#39;t do much processing on what they&amp;#39;re given.&lt;/p&gt;
&lt;p&gt;Or at least they shouldn&amp;#39;t. Often though, Rails projects wind up with a lot of logic in their views.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll explore how to use Rails helpers to keep our views clean and readable.&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s see why too much logic in our views can become problematic.&lt;/p&gt;
&lt;h2&gt;The Problem with Logic-heavy Rails Views&lt;/h2&gt;
&lt;p&gt;Logic-heavy views are bad for a &lt;em&gt;lot&lt;/em&gt; of reasons. We might end up with logic-heavy views because of the pressure of a crushing deadline, or they might result from inevitable legacy code buildup. New features get introduced along with new edge cases, and it seems harmless to update &lt;code&gt;&amp;lt;%= @post.name.titlecase %&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;%= @post.name.titlecase.gsub(&amp;quot;#&amp;quot;, &amp;quot;&amp;quot;) %&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But before you know it, this can snowball, even if you don&amp;#39;t keep tacking on little additions to this particular line of code. If you keep doing things like this elsewhere in the view, you end up with an ugly, difficult-to-read mess.&lt;/p&gt;
&lt;p&gt;So what do we do? Should we litter our controllers with dozens of instance variables like &lt;code&gt;@normalized_titlecased_post_name = @post.name.titlecase.gsub(&amp;quot;#&amp;quot;, &amp;quot;&amp;quot;)&lt;/code&gt;? Bloat our models with tons of methods intended only for use in our views? Probably not.&lt;/p&gt;
&lt;p&gt;Enter Rails helpers.&lt;/p&gt;
&lt;h2&gt;What Are Helpers in Rails?&lt;/h2&gt;
&lt;p&gt;A helper is a method intended to abstract logic out of our views, leaving them more readable, less cluttered, and not directly responsible for processing what they&amp;#39;ve been passed by our controllers.&lt;/p&gt;
&lt;p&gt;Rails already ships with a lot of &lt;a href=&quot;https://guides.rubyonrails.org/action_view_helpers.html&quot;&gt;useful helpers&lt;/a&gt; to begin with. Some you&amp;#39;re likely already familiar with, like &lt;code&gt;form_with&lt;/code&gt;, &lt;code&gt;link_to&lt;/code&gt;, and &lt;code&gt;image_tag&lt;/code&gt;, to name a few.&lt;/p&gt;
&lt;p&gt;Most of the helpers you write yourself will naturally be much more specific to your app than what Rails includes, but you will likely still find many Rails helpers very useful.&lt;/p&gt;
&lt;p&gt;If you haven&amp;#39;t already, familiarize yourself with the helpers Rails provides — otherwise, there&amp;#39;s a decent chance you&amp;#39;ll end up trying to solve a problem that&amp;#39;s already been solved.&lt;/p&gt;
&lt;p&gt;I once wrote a method early in my Rails career to convert a number to a dollar amount, only to discover later that Rails already had a solution — &lt;code&gt;number_to_currency&lt;/code&gt;!&lt;/p&gt;
&lt;h2&gt;Organizing Your Helpers in Rails&lt;/h2&gt;
&lt;p&gt;By default, a new Rails application has one helper — &lt;code&gt;ApplicationHelper&lt;/code&gt; — which all other helpers should inherit from. You&amp;#39;ve likely noticed that any time you create a controller via &lt;code&gt;rails g controller&lt;/code&gt;, Rails automatically creates a new (pluralized) helper module for you as well.&lt;/p&gt;
&lt;p&gt;While this form of organization (having a helper module for each controller) is both useful and important, it&amp;#39;s also a bit deceptive. The methods defined in &lt;em&gt;any&lt;/em&gt; of these helpers are available in &lt;em&gt;all&lt;/em&gt; your views. So if you define &lt;code&gt;my_generic_method()&lt;/code&gt; in, say, &lt;code&gt;UsersHelper&lt;/code&gt;, and an identically-named &lt;code&gt;my_generic_method()&lt;/code&gt; in &lt;code&gt;TransactionsHelper&lt;/code&gt;, they won&amp;#39;t only be available in their respective views — one will simply overwrite the other and almost certainly break something.&lt;/p&gt;
&lt;p&gt;So you should be fairly thoughtful when naming your helper methods. Don&amp;#39;t assume there&amp;#39;s an invisible &lt;code&gt;User::&lt;/code&gt; namespace or &lt;code&gt;user_&lt;/code&gt; prefix in front of the methods in your &lt;code&gt;UsersHelper&lt;/code&gt;, because there isn&amp;#39;t, real or implied.&lt;/p&gt;
&lt;p&gt;Any helper methods generic enough to be used all over (most of these probably won&amp;#39;t be model-specific) can go in your &lt;code&gt;ApplicationHelper&lt;/code&gt;. Methods specific to view folders (e.g., &lt;code&gt;/views/users&lt;/code&gt;) belong to helpers of the same name.&lt;/p&gt;
&lt;p&gt;Again, this is strictly for organizational purposes, given all helper methods are available to all views. But, as you might imagine, it will definitely confuse other developers (and likely your future self) if you end up finding &lt;code&gt;User&lt;/code&gt;-specific methods in your &lt;code&gt;ZooAnimalsHelper&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: Before Rails 4, this was not the case. You can &lt;a href=&quot;https://guides.rubyonrails.org/configuring.html#config-action-controller-include-all-helpers&quot;&gt;turn this behavior off&lt;/a&gt; by setting &lt;code&gt;config.action_controller.include_all_helpers = false&lt;/code&gt; in your &lt;code&gt;application.rb&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Do&amp;#39;s and Don&amp;#39;ts for Helpers&lt;/h2&gt;
&lt;h3&gt;Helpers in Controllers&lt;/h3&gt;
&lt;p&gt;You can include helpers in controllers. In modern Rails versions (5+), you can simply access them in your controllers via &lt;code&gt;helpers&lt;/code&gt; (e.g. &lt;code&gt;helpers.number_to_currency&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;It&amp;#39;s often tempting, and you can absolutely abuse this — I won&amp;#39;t pretend I never have. But generally, you &lt;em&gt;shouldn’t&lt;/em&gt;. It&amp;#39;s okay to do this occasionally, and if you must, do use the &lt;code&gt;helpers&lt;/code&gt; method rather than &lt;code&gt;include&lt;/code&gt; on the entire helper module in your controller. &lt;code&gt;include&lt;/code&gt; dumps every method defined in that helper into the controller, potentially conflicting with your controller methods.&lt;/p&gt;
&lt;p&gt;If you keep finding you want to share a helper across different layers of your app, consider whether it might be better off elsewhere, like in a model.&lt;/p&gt;
&lt;h3&gt;Helpers and Instance Variables&lt;/h3&gt;
&lt;p&gt;Helpers can access any instance variables available for the view they&amp;#39;re called in. The instance variables can be referenced inside your helpers without being passed explicitly as method arguments, but this is not advised.&lt;/p&gt;
&lt;p&gt;Instead, pass your instance variables to your helpers as regular arguments. Doing otherwise tightly couples your controllers and views (and helpers), making reuse, refactoring, testing, and debugging difficult.&lt;/p&gt;
&lt;p&gt;On &lt;em&gt;rare&lt;/em&gt; occasions, you might want to reference instance variables that aren&amp;#39;t passed as method arguments inside your helpers. You could have a helper method that&amp;#39;s so specific, it only belongs in one spot, or the opposite — it only leverages an instance variable you have everywhere.&lt;/p&gt;
&lt;p&gt;This is still somewhat iffy, but ultimately it&amp;#39;s up to you. I will say though that if you&amp;#39;re not very certain, err on the side of caution. As with anything else, you&amp;#39;ll get a feel for things as you see what does and doesn&amp;#39;t work over time.&lt;/p&gt;
&lt;h3&gt;Never Modify Objects with Helpers&lt;/h3&gt;
&lt;p&gt;This is probably obvious, but a helper should &lt;strong&gt;&lt;em&gt;never&lt;/em&gt;&lt;/strong&gt; modify the state of a passed object or (even worse) modify that object in the database. Remember, they&amp;#39;re responsible for helping the View display things cleanly, not changing anything.&lt;/p&gt;
&lt;h3&gt;Never Make Database Calls with Helpers&lt;/h3&gt;
&lt;p&gt;Helpers should also never be responsible for making calls to the database — no part of the view should. Not only does doing so badly violate the &lt;a href=&quot;https://en.wikipedia.org/wiki/Separation_of_concerns&quot;&gt;separation of concerns&lt;/a&gt;, it&amp;#39;s also incredibly confusing to other developers (&lt;em&gt;where&lt;/em&gt; is this query coming from?), and can make tracking down problems difficult. I once spent hours completely baffled by where an &lt;a href=&quot;https://blog.appsignal.com/2018/04/24/active-record-performance-the-n-1-queries-antipattern.html&quot;&gt;N+1&lt;/a&gt; was being introduced, only to discover some queries inside a loop embedded in the view.&lt;/p&gt;
&lt;h3&gt;Don&amp;#39;t Let Helpers Become a Dumping Ground&lt;/h3&gt;
&lt;p&gt;One final word of caution: don&amp;#39;t, as is so often the case, let your helpers become a dumping ground for one-off methods and little snippets of code that either serve little purpose, or might be better off as another kind of method. They should be relatively simple methods, and only concerned with the view; &lt;a href=&quot;https://en.wikipedia.org/wiki/Business_logic&quot;&gt;business logic&lt;/a&gt; really belongs in our models.&lt;/p&gt;
&lt;h2&gt;Generating HTML with Rails Helpers for Views: An Example&lt;/h2&gt;
&lt;p&gt;Rails helpers can work with and generate HTML for our views. Remember Rails&amp;#39; built-in helpers? A &lt;a href=&quot;https://apidock.com/rails/v6.1.3.1/ActionView/Helpers/TagHelper/content_tag&quot;&gt;content_tag&lt;/a&gt; will let us generate our own HTML.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say we have a zoo and want to display a list of exhibit times for a given group of animals a customer selects. We might have something like this in our view:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;div&amp;gt;
  &amp;lt;% @animals.map do |animal| %&amp;gt;
    &amp;lt;div class=&amp;quot;time-heading&amp;quot;&amp;gt;
      &amp;lt;%= animal.name %&amp;gt; (&amp;lt;i&amp;gt;&amp;lt;%= animal.species.name %&amp;gt;&amp;lt;/i&amp;gt;): &amp;lt;%= animal.exhibit_time.in_time_zone(animal.exhibit_time.time_zone).strftime(&amp;quot;%m/%d/%Y %Z&amp;quot;) %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a few issues with this. For one, it&amp;#39;s a bit hard to read. We&amp;#39;re also calling things like &lt;code&gt;in_time_zone&lt;/code&gt; and &lt;code&gt;strftime&lt;/code&gt; to format and display the exhibit time, along with a &lt;em&gt;lot&lt;/em&gt; of method chaining.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s also in no way reusable. Let&amp;#39;s say we need to display this (or something similar) in different places on our site. If we then later decide it needs a style or content update, we have to change it everywhere. This is annoying for obvious reasons (in addition to the fact that it can be tricky to find every example if they&amp;#39;re all slightly different).&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see what handling this in a helper might look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# helpers/animals_helper.rb
module AnimalsHelper
  def display_exhibit_times(animals, dom_class: nil, style: nil)
    exhibit_strings = animals.map { |animal|
      &amp;quot;#{animal.name} (&amp;lt;i&amp;gt;#{animal.species.name}&amp;lt;/i&amp;gt;): #{animal.exhibit_time.formatted_start_time}&amp;quot;.html_safe
    }

    exhibit_strings.map { |exhibit_string|
      content_tag(:div, &amp;quot;#{exhibit_string}&amp;quot;, class: dom_class, style: style)
    }.join
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Admittedly, this may not be the prettiest example, but it&amp;#39;s considerably easier to read and, more importantly, removes the logic from our view entirely. It&amp;#39;s also reusable and flexible; notice the two named arguments we&amp;#39;ve passed in — &lt;code&gt;dom_class&lt;/code&gt; and &lt;code&gt;style&lt;/code&gt;. Because &lt;code&gt;content_tag&lt;/code&gt; allows us to pass in HTML class and style information, we can pass this in any time we call this method to customize it in different places as needed.&lt;/p&gt;
&lt;p&gt;Another thing you may have noticed is the transformation of the time formatting into a call from &lt;code&gt;exhibit_time&lt;/code&gt;. This is just to point out that not all methods used in the view have to be helpers; this might be a model method, or a mixin shared across models with times and dates for easier formatting. (That isn&amp;#39;t to say it might not also make sense to use a helper here instead; it might.)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;.html_safe&lt;/code&gt; is needed to translate the &lt;code&gt;&amp;lt;/i&amp;gt;&lt;/code&gt; tag to HTML. &lt;code&gt;.join&lt;/code&gt; is required to return multiple content tags for use in the DOM.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Our view now becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= display_exhibit_times(@animals, dom_class: &amp;quot;time-heading&amp;quot;) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isn&amp;#39;t that better?&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;A Second Example&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s consider another example. We need to email our customers their tickets, and we&amp;#39;d like to provide them with an online menu for a dining area at our zoo.&lt;/p&gt;
&lt;p&gt;In both cases, we&amp;#39;ll probably want to render QR codes. Let&amp;#39;s use &lt;code&gt;RQRCode&lt;/code&gt;, a gem that can do just that for us:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;div&amp;gt;
  &amp;lt;%= RQRCode::QRCode.new(@ticket.qr_code).as_svg(color: &amp;quot;black&amp;quot;, shape_rendering: &amp;quot;crispEdges&amp;quot;, module_size: 5, standalone: true, use_path: true) %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This isn&amp;#39;t ideal for a number of reasons. For one thing, we&amp;#39;re instantiating objects in our view. For another, we&amp;#39;re passing every single required option, when most of them will probably be the same throughout our app.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s move this to a helper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# helpers/application_helper.rb
def render_qr_code(code, options = {})
  qr_code = RQRCode::QRCode.new(code)

  defaults = {
    color: &amp;quot;black&amp;quot;,
    shape_rendering: &amp;quot;crispEdges&amp;quot;,
    module_size: 3,
    standalone: true,
    use_path: true
  }

  qr_code.as_svg(defaults.merge(options)).html_safe
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our view becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;div&amp;gt;
  &amp;lt;%= render_qr_code(@ticket.qr_code, module_size: 5) %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much cleaner, and now we can reuse it for our restaurant!&lt;/p&gt;
&lt;h2&gt;One More Trick: The &lt;code&gt;tag&lt;/code&gt; Helper&lt;/h2&gt;
&lt;p&gt;Do you ever find yourself interpolating conditional class names in your elements?&lt;/p&gt;
&lt;p&gt;Say we&amp;#39;re listing our animal exhibits on our main page, and want to highlight certain exhibits if they&amp;#39;re currently featured:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;div class=&amp;quot;border-black margin-standard &amp;lt;%= &amp;#39;bg-featured text-bold&amp;#39; if @exhibit.featured? %&amp;gt;&amp;quot;&amp;gt;
  &amp;lt;%= @exhibit.name %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As of Rails 6.1, you can set conditional classes &lt;a href=&quot;https://api.rubyonrails.org/v6.1.4/classes/ActionView/Helpers/TagHelper.html#method-i-tag&quot;&gt;using the &lt;code&gt;tag&lt;/code&gt; helper&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= tag.div class: [&amp;quot;border-black margin-standard&amp;quot;, &amp;quot;bg-featured text-bold&amp;quot;: @exhibit.featured?] do %&amp;gt;
  &amp;lt;%= @exhibit.name %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The classes &amp;quot;bg-featured&amp;quot; and &amp;quot;text-bold&amp;quot; will only be included if it&amp;#39;s a featured exhibit. This can make things easier to read, especially if you&amp;#39;ve got multiple classes with multiple conditions!&lt;/p&gt;
&lt;h2&gt;There Isn&amp;#39;t Always a &amp;#39;Perfect&amp;#39; Solution&lt;/h2&gt;
&lt;p&gt;So you&amp;#39;ve been using helpers for a while now and are comfortable with them. You&amp;#39;ve refactored your views, pulled methods out of your models and your controllers, and everything&amp;#39;s cleaner, easier to read and test. You&amp;#39;ve noticed you&amp;#39;re introducing fewer bugs into your application.&lt;/p&gt;
&lt;p&gt;And yet — you still have a bit of logic in some of your views, a few calls to helpers in your controllers, and a few methods that don&amp;#39;t belong in the right place.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s completely fine. This is normal and, at least in my opinion, inevitable. Some devs claim there&amp;#39;s always a solution — be it a helper or model method, presenter/decorator class, etc., but personally, I&amp;#39;m not convinced.&lt;/p&gt;
&lt;p&gt;Suppose you continually find you want to use a particular method across multiple layers of your app. In that case, it might be a good candidate for a concern, model method, or part of a separate module. But sometimes, there isn&amp;#39;t an absolutely perfect solution for every use case or a perfect place to put code. There&amp;#39;s certainly no such thing as a &amp;#39;perfect&amp;#39; app. And that&amp;#39;s okay.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, we explored how to make your Rails views cleaner and more readable using helpers. We looked at how to organize your helpers, some do&amp;#39;s and don&amp;#39;ts, and how to generate HTML for your views using helpers.&lt;/p&gt;
&lt;p&gt;We finally argued that even if you master helpers, you&amp;#39;ll likely still never achieve the &amp;#39;perfect&amp;#39; Rails app. However, you&amp;#39;ll certainly have an app that&amp;#39;s easier to maintain.&lt;/p&gt;
&lt;p&gt;I hope you&amp;#39;ve found this post useful.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Calling Ruby Methods in C: Avoid Memory Leaks</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/01/25/calling-ruby-methods-in-c-avoid-memory-leaks.html"/>
    <id>https://blog.appsignal.com/2023/01/25/calling-ruby-methods-in-c-avoid-memory-leaks.html</id>
    <published>2023-01-25T00:00:00+00:00</published>
    <updated>2023-01-25T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover how you can avoid memory leaks in your C extension by using functions like `rb_protect`.</summary>
    <content type="html">&lt;p&gt;Memory leaks are a pain for gem users. They are hard to track and can lead to expensive infrastructure costs.&lt;/p&gt;
&lt;p&gt;Memory leaks within a C extension are even worse. You&amp;#39;ll see a lot of tools and
articles about finding leaks in Ruby. However, you don&amp;#39;t have the same access to internals in C.&lt;/p&gt;
&lt;p&gt;A naive usage of &lt;code&gt;rb_funcall&lt;/code&gt; can cause memory leaks: it&amp;#39;s much better to use &lt;code&gt;rb_protect&lt;/code&gt; instead. So, if you are a C extension
writer, please read on for the sake of developers who will use your gem.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;The Issue with &lt;code&gt;rb_funcall&lt;/code&gt; and C&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;rb_funcall&lt;/code&gt; can be a great tool when you need to interact between Ruby and the C parts
of your library but only need to write a little C.&lt;/p&gt;
&lt;p&gt;However, when you run &lt;code&gt;rb_funcall&lt;/code&gt;, you are no longer in C where everything is
straightforward. You can be left in muddy waters if the called function:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Completely changes its definition
during runtime&lt;/li&gt;
&lt;li&gt;Raises a call&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Number 1 is the easiest one to catch. You&amp;#39;ll likely end up with a segfault, and
if your test suite is complete enough, you should catch that before publishing.&lt;/p&gt;
&lt;p&gt;However, the latter can cause memory leaks and make your codebase way harder
to read. Let&amp;#39;s take a look at that now.&lt;/p&gt;
&lt;h2&gt;Raise in Ruby Causing C Memory Leaks&lt;/h2&gt;
&lt;p&gt;Ruby&amp;#39;s raising mechanism jumps between parts of the code from one scope to the
first parent that catches an error. This is implemented in the MRI using &lt;code&gt;longjmp&lt;/code&gt;
and &lt;code&gt;setjmp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you are interested in how this is built, read the
&lt;a href=&quot;https://rubyhackingguide.ulysse.md/evaluator#raise&quot;&gt;Evaluator chapter in the Ruby Hacking Guide&lt;/a&gt;. In a nutshell, when
you use a &lt;code&gt;begin..ensure&lt;/code&gt; block, you &lt;code&gt;setjmp()&lt;/code&gt;, and when you raise within
this block, you &lt;code&gt;longjmp()&lt;/code&gt; to the saved position.&lt;/p&gt;
&lt;p&gt;So if a function is raised with &lt;code&gt;rb_funcall&lt;/code&gt;, the C code called after it
never executes.&lt;/p&gt;
&lt;p&gt;The example below illustrates a potential leak. If &lt;code&gt;json_parse&lt;/code&gt; raises, it will
leak.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VALUE rb_create_geometry_hash(VALUE self, VALUE wkt) {
    // Alloc
    GEOSWKTReader* reader = GEOSWKTReader_create();
    GEOSGeoJSONWriter* writer = GEOSGeoJSONWriter_create();

    // C processing
    GEOSGeometry* geom = GEOSWKTReader_read(reader, StringValuePtr(wkt));
    char* geojson = GEOSGeoJSONWriter_writeGeometry(writer, geom, -1);

    // Ruby processing
    VALUE rb_geojson = rb_str_new_cstr(geojson);
    VALUE result = rb_funcall(self, rb_intern(&amp;quot;json_parse&amp;quot;), 1, rb_geojson);

    // Free
    GEOSWKTReader_destroy(reader);
    GEOSGeom_destroy(geom);
    GEOSGeoJSONWriter_destroy(writer);
    GEOSFree(geojson);

    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, the example above is a bit silly — you could invert the freeing
and Ruby processing parts. However, this is not always possible, and
longer function bodies can become more intertwined.&lt;/p&gt;
&lt;h2&gt;Using &lt;code&gt;begin..ensure&lt;/code&gt; in Ruby&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re using Ruby, you could instead write the above example using
&lt;code&gt;begin..ensure&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def create_geometry_hash(wkt)
    reader = GEOSWKTReader.new
    writer = GEOSGeoJSONWriter.new

    begin
        json_parse(writer.write(reader.read(wkt)))
    ensure
        reader.close
        writer.close
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;This API is also available in C with &lt;code&gt;rb_rescue&lt;/code&gt; and &lt;code&gt;rb_ensure&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;static VALUE try_ruby_processing(VALUE args) {
    char* geojson = (char*)args;
    // Ruby processing
    VALUE rb_geojson = rb_str_new_cstr(geojson);
    VALUE result = rb_funcall(self, rb_intern(&amp;quot;json_parse&amp;quot;), 1, rb_geojson);
}

struct to_free {
    GEOSWKTReader* reader;
    GEOSGeoJSONWriter* writer;
    GEOSGeometry* geom;
    char* geojson;
};

static VALUE ensure_free(VALUE args) {
    struct to_free data = (struct to_free)args
    GEOSWKTReader_destroy(data.reader);
    GEOSGeom_destroy(data.geom);
    GEOSGeoJSONWriter_destroy(data.writer);
    GEOSFree(data.geojson);

}

VALUE rb_create_geometry_hash(VALUE self, VALUE wkt) {
    // Alloc
    GEOSWKTReader* reader = GEOSWKTReader_create();
    GEOSGeoJSONWriter* writer = GEOSGeoJSONWriter_create();

    // C processing
    GEOSGeometry* geom = GEOSWKTReader_read(reader, StringValuePtr(wkt));
    char* geojson = GEOSGeoJSONWriter_writeGeometry(writer, geom, -1);

    return rb_ensure(
        try_ruby_processing, (VALUE)geojson
        ensure_free, (struct to_free){ reader, writer, geom, geojson }
    );

    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this is a bit cumbersome, and if you want to add a &lt;code&gt;rescue&lt;/code&gt; block to
the party, it gets way less readable. I suggest reading &lt;a href=&quot;https://blog.peterzhu.ca/ruby-c-ext-part-8/&quot;&gt;Peter Zhu&amp;#39;s &amp;#39;A Rubyist&amp;#39;s Walk Along the C-side (Part 8): Exceptions &amp;amp; Error Handling&amp;#39;&lt;/a&gt;
if you want to use the &lt;code&gt;begin..rescue..ensure..end&lt;/code&gt; API in C.&lt;/p&gt;
&lt;h2&gt;Using &lt;code&gt;rb_protect&lt;/code&gt; for C&lt;/h2&gt;
&lt;p&gt;There is another option. First, let&amp;#39;s see how it could look in Ruby:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def create_geometry_hash(wkt)
    reader = GEOSWKTReader.new
    writer = GEOSGeoJSONWriter.new

    err = nil
    result = nil
    begin
        result = json_parse(writer.write(reader.read(wkt)))
    rescue =&amp;gt; e
        err = e
    end

    reader.close
    writer.close

    raise err if err

    result
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks strange in Ruby, but is a workflow very well
suited to C. The MRI has an API for that, &lt;code&gt;rb_protect&lt;/code&gt;, and the C function looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VALUE ruby_call(VALUE rb_geojson) {
    return rb_funcall(self, rb_intern(&amp;quot;json_parse&amp;quot;), 1, rb_geojson);
}

VALUE rb_create_geometry_hash(VALUE self, VALUE wkt) {
    int state;

    // Alloc
    GEOSWKTReader* reader = GEOSWKTReader_create();
    GEOSGeoJSONWriter* writer = GEOSGeoJSONWriter_create();

    // C processing
    GEOSGeometry* geom = GEOSWKTReader_read(reader, StringValuePtr(wkt));
    char* geojson = GEOSGeoJSONWriter_writeGeometry(writer, geom, -1);

    // Ruby processing
    VALUE rb_geojson = rb_str_new_cstr(geojson);
    rb_protect(ruby_call, rb_geojson, &amp;amp;state);

    // Free
    GEOSWKTReader_destroy(reader);
    GEOSGeom_destroy(geom);
    GEOSGeoJSONWriter_destroy(writer);
    GEOSFree(geojson);

    if (state) rb_jump_tag(state);

    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above method will re-raise a Ruby error after having freed
everything.&lt;/p&gt;
&lt;p&gt;Note that we could also choose to ignore the error by using an
empty &lt;code&gt;rescue&lt;/code&gt; block in Ruby:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;    ...

    if (state) rb_set_errinfo(Qnil);

    return result; // =&amp;gt; nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Warning:&lt;/strong&gt; If you do not raise the error, the &lt;code&gt;rb_set_errinfo(Qnil)&lt;/code&gt; step is
important so you don&amp;#39;t keep information available about an error that users should not know
about.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Or, you can conditionally choose to raise an error, like &lt;code&gt;rescue My::Error&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;    ...

    if (state) {
        if (rb_obj_is_kind_of(rb_errinfo(), rb_define_class_under(rb_mMy, &amp;quot;Error&amp;quot;, rb_eStandardError))) {
            rb_jump_tag(state);
        } else {
            rb_set_errinfo(Qnil);
        }
    }

    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can actually consider &lt;code&gt;rb_errinfo()&lt;/code&gt; as the same as the &lt;code&gt;$!&lt;/code&gt; global
variable.&lt;/p&gt;
&lt;p&gt;This is all great, but when it boils down to one &lt;code&gt;rb_funcall&lt;/code&gt; only, we can simplify that API.&lt;/p&gt;
&lt;p&gt;The overall idea behind using the &lt;code&gt;rb_protect&lt;/code&gt; API when there is a function
to raise is to enhance readability. You don&amp;#39;t need to check if the
function can raise or not, you assume it can, and use the state to work with
that.&lt;/p&gt;
&lt;h2&gt;The &lt;code&gt;rb_protect_funcall&lt;/code&gt; Proposal&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s isolate &lt;code&gt;rb_funcall&lt;/code&gt;, as it&amp;#39;s the only &lt;em&gt;dangerous&lt;/em&gt; method to use. Here&amp;#39;s an API that will do that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VALUE rb_protect_funcall(VALUE recv, ID mid, int* state, int n, ...);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This API is the same as &lt;code&gt;rb_funcall&lt;/code&gt;, with a &lt;code&gt;state&lt;/code&gt; from &lt;code&gt;rb_protect&lt;/code&gt;. Hence the
usage is pretty straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VALUE rb_create_geometry_hash(VALUE self, VALUE wkt) {
    int state;

    // Alloc
    GEOSWKTReader* reader = GEOSWKTReader_create();
    GEOSGeoJSONWriter* writer = GEOSGeoJSONWriter_create();

    // C processing
    GEOSGeometry* geom = GEOSWKTReader_read(reader, StringValuePtr(wkt));
    char* geojson = GEOSGeoJSONWriter_writeGeometry(writer, geom, -1);

    // Ruby processing
    VALUE rb_geojson = rb_str_new_cstr(geojson);
    rb_protect_funcall(self, rb_intern(&amp;quot;json_parse&amp;quot;), &amp;amp;state, 1,  rb_geojson);

    // Free
    GEOSWKTReader_destroy(reader);
    GEOSGeom_destroy(geom);
    GEOSGeoJSONWriter_destroy(writer);
    GEOSFree(geojson);

    if (state) rb_jump_tag(state);

    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This API is not yet available in Ruby, and may never be. You can take it from
&lt;a href=&quot;https://github.com/rgeo/rgeo/blob/893371229adcaf0f84c59e433db494304823990a/ext/geos_c_impl/ruby_more.c#L31-L56&quot;&gt;RGeo&lt;/a&gt; (MIT LICENSE).&lt;/p&gt;
&lt;h2&gt;A Real-World Example&lt;/h2&gt;
&lt;p&gt;If you want to see a real-world example, I encourage you to read the &lt;a href=&quot;https://github.com/rgeo/rgeo&quot;&gt;RGeo&lt;/a&gt;
codebase as we recently switched to going full &lt;code&gt;rb_protect&lt;/code&gt;. We even
have some functions, such as &lt;code&gt;rgeo_convert_to_geos_geometry&lt;/code&gt;, that propagate
this state for simpler usage. This function is a good place to start digging
around.&lt;/p&gt;
&lt;p&gt;Feel free to &lt;a href=&quot;https://github.com/rgeo/rgeo&quot;&gt;open an issue on RGeo&lt;/a&gt; to
discuss the choices we made further.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we warned against using &lt;code&gt;rb_funcall&lt;/code&gt; with C as it can cause memory leaks. We explored using &lt;code&gt;begin..ensure&lt;/code&gt; or &lt;code&gt;rb_protect&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Parse Arguments in Your Ruby C Extension</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/01/18/how-to-parse-arguments-in-your-ruby-c-extension.html"/>
    <id>https://blog.appsignal.com/2023/01/18/how-to-parse-arguments-in-your-ruby-c-extension.html</id>
    <published>2023-01-18T00:00:00+00:00</published>
    <updated>2023-01-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore two ways to set up a complex Ruby API written in C.</summary>
    <content type="html">&lt;p&gt;Ruby is a wonderful language, made for humans first and machines
second. It is easy to read and write. There are plenty of ways to write anything,
and you can often guess its standard library by typing the name of the
method you would have chosen yourself.&lt;/p&gt;
&lt;p&gt;Because of this, Ruby&amp;#39;s arguments are very flexible, which lets us
express our APIs very clearly. But this comes with a drawback: Ruby is quite hard to parse for C
extension developers!&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll go through two ways to set up a complex Ruby API
that is written in C:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;with &lt;code&gt;rb_define_method&lt;/code&gt; and parsing it with &lt;code&gt;rb_scan_args&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;using a Ruby interface&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;C and Ruby: An Introduction&lt;/h2&gt;
&lt;p&gt;As mentioned, Ruby is hard to parse for C extension developers.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def this(is, a = &amp;quot;quite&amp;quot;, *convoluted, yet: 1, possible:, &amp;amp;example)
  # And we could have omitted the block, yet still passed it as an argument!
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The beauty of C, the language Ruby is written in, stems from its simplicity, including in its function parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;data-type&amp;gt; &amp;lt;variable-identifier&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt; for variadic arguments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These will help you maintain a
codebase that is not too hard to understand.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the most complex way to define a C function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int printf(const char*, ...);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you code a C extension for your Ruby codebase, you&amp;#39;ll start
to understand where the complexity begins. But don&amp;#39;t worry — Ruby MRI developers have us covered.&lt;/p&gt;
&lt;h2&gt;Simple Method Definition in a Ruby C Extension&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ll start with a method you&amp;#39;ll have to use at some point, &lt;code&gt;rb_define_method&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also &lt;a href=&quot;https://github.com/BuonOmo/c-arg-parsing-comparison&quot;&gt;follow along with the code examples in this repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is &lt;code&gt;rb_define_method&lt;/code&gt;&amp;#39;s signature:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void rb_define_method(VALUE klass, const char *name,
                      VALUE (*func)(ANYARGS), int argc);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And according to &lt;a href=&quot;https://ruby-doc.org/core-3.1.2/ruby-3_1_2/doc/extension_rdoc.html&quot;&gt;Ruby&amp;#39;s extension.rdoc&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;argc is the number of arguments. if argc is -1, the function will receive
3 arguments: argc, argv, and self. if argc is -2, the function will
receive 2 arguments, self and args, where args is a Ruby array of
the method arguments.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In a nutshell:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;// argc is -1.
VALUE func(int argc, VALUE* argv, VALUE self);
// argc is -2.
VALUE func(VALUE self, VALUE args);
// argc is N (here N=2).
VALUE func(VALUE self, VALUE arg1, VALUE arg2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So if your API only consists of methods with fixed parameter lengths, or only
one variadic parameter (&lt;code&gt;def foo(*bar)&lt;/code&gt;), read no further — you are done! If
you want a richer way to call your API, please, be my guest.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By the way, if you want more involved examples on &lt;code&gt;rb_define_method&lt;/code&gt;, I
suggest you read &lt;a href=&quot;https://blog.peterzhu.ca/ruby-c-ext-part-2/&quot;&gt;Peter Zhu&amp;#39;s article on Defining Methods&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Using Ruby C API Internals&lt;/h2&gt;
&lt;p&gt;So, let&amp;#39;s go back to our use case: parsing complex arguments. Fortunately, some tools can help us.&lt;/p&gt;
&lt;p&gt;But first,
let&amp;#39;s see the limitations of solely using &lt;code&gt;rb_define_method&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Drawbacks of &lt;code&gt;rb_define_method&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;No Mention of Block Arguments&lt;/h3&gt;
&lt;p&gt;One limitation is that &lt;code&gt;rb_define_method&lt;/code&gt; never mentions block arguments. Those
are not considered, as it doesn&amp;#39;t really matter to Ruby anyway if you pass
a block. You can still ensure that a block is passed by using
&lt;code&gt;rb_block_given_p&lt;/code&gt; or &lt;code&gt;rb_need_block&lt;/code&gt;. There&amp;#39;s more on that topic in
&lt;a href=&quot;https://blog.peterzhu.ca/ruby-c-ext-part-2/#blocks&quot;&gt;Peter Zhu&amp;#39;s article&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Args Can Vary&lt;/h3&gt;
&lt;p&gt;Another important limitation is that args can vary, but the method
call itself is not so constrained. Therefore, if you want your API to be
like &lt;code&gt;def foo(bar, *baz)&lt;/code&gt;, you&amp;#39;ll have to parse your arguments. There are
a few methods to help you down that path. &lt;code&gt;rb_check_arity&lt;/code&gt; is one you
can use, along with the -1 version of &lt;code&gt;rb_define_method&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the function signature:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;rb_check_arity(int argc, int min, int max)
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Keyword Arguments&lt;/h3&gt;
&lt;p&gt;One last limitation we&amp;#39;ll go through is the use of keyword arguments.
And I kept the best for last, as that will be the core of this article.&lt;/p&gt;
&lt;p&gt;We have to retrieve
keyword arguments before we can parse them correctly. Fortunately, the Ruby C API comes with a method for this,
&lt;code&gt;rb_scan_args&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the &lt;code&gt;rb_scan_args&lt;/code&gt; signature:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;rb_scan_args(int argc, VALUE *argv, const char *fmt, ...)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You pass &lt;code&gt;rb_scan_args&lt;/code&gt; the &lt;code&gt;argc&lt;/code&gt; and &lt;code&gt;argv&lt;/code&gt;
given by &lt;code&gt;rb_define_method&lt;/code&gt; — a string that says how the arguments should be
parsed (&lt;code&gt;fmt&lt;/code&gt;) and the receiver for those arguments. There you go, all of
Ruby&amp;#39;s args complexity parsed in one line! Well, almost.&lt;/p&gt;
&lt;p&gt;You can refer to &lt;a href=&quot;https://ruby-doc.org/core-3.1.2/ruby-3_1_2/doc/extension_rdoc.html&quot;&gt;extension.rdoc&lt;/a&gt; for a formal representation of
how &lt;code&gt;fmt&lt;/code&gt; should be written, although we&amp;#39;ll partially cover it in
the examples below.&lt;/p&gt;
&lt;h2&gt;Write and Parse a Function: An Example&lt;/h2&gt;
&lt;p&gt;For the rest of this article, let&amp;#39;s consider that we want to write this
function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def voronoi_diagram(envelope, *polygons, tolerance:, only_edges: false)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parsing this using &lt;code&gt;rb_scan_args&lt;/code&gt; will look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VALUE voronoi_diagram(int argc, VALUE *argv, VALUE self) {
  VALUE envelope;
  VALUE polygons;
  VALUE kwargs;
  rb_scan_args(argc, argv, &amp;quot;1*:&amp;quot;, &amp;amp;envelope, &amp;amp;polygons, &amp;amp;kwargs);

  // Actual method
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;&amp;quot;1*:&amp;quot;&lt;/code&gt; gibberish means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt;: one required positional argument&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*&lt;/code&gt;: any amount of positional arguments not required&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:&lt;/code&gt;: keyword arguments at the end&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Parse Keyword Arguments&lt;/h2&gt;
&lt;p&gt;Now we&amp;#39;ve constrained the method, unfortunately, we are not done yet. Our
current API is &lt;code&gt;def voronoi_diagram(envelope, *polygons, **kwargs)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we
need to parse those keyword arguments using &lt;a href=&quot;https://ruby-doc.org/core-3.1.2/ruby-3_1_2/doc/extension_rdoc.html#label-Method+Definition&quot;&gt;&lt;code&gt;rb_get_kwargs&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int rb_get_kwargs(VALUE keyword_hash, const ID *table,
                  int required, int optional, VALUE *values);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have
to choose some required and optional arguments. Once that&amp;#39;s done, you use &lt;code&gt;table&lt;/code&gt; to tell Ruby the name of
those arguments, and you store the result in an array (&lt;code&gt;values&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VALUE voronoi_diagram(int argc, VALUE *argv, VALUE self) {
  VALUE envelope;
  VALUE polygons;
  VALUE tolerance;
  VALUE only_edges;

  VALUE kwargs;
  rb_scan_args(argc, argv, &amp;quot;1*:&amp;quot;, &amp;amp;envelope, &amp;amp;polygons, &amp;amp;kwargs);

  ID table[2];
    table[0] = rb_intern(&amp;quot;tolerance&amp;quot;);
    table[1] = rb_intern(&amp;quot;only_edges&amp;quot;);
  VALUE *values;
  rb_get_kwargs(kwargs, table, 1, 1, values);

  tolerance = values[0];
  only_edges = values[1] == Qundef ? Qfalse : values[1];

  // Actual method
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you have it! A complex Ruby method, parsed using only C. However,
if this is too convoluted for you, there is another option.&lt;/p&gt;
&lt;h2&gt;Using a Ruby Interface&lt;/h2&gt;
&lt;p&gt;Another way to handle the problem is actually to use Ruby&amp;#39;s syntax directly
and do the parsing at the Ruby stage.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def voronoi_diagram(envelope, *polygons, tolerance:, only_edges: false)
  c_voronoi_diagram(envelope, polygons, tolerance, only_edges)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this, you can directly use the third form of &lt;code&gt;rb_define_method&lt;/code&gt;, for a C method that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VALUE c_voronoi_diagram(VALUE self, VALUE envelope,
                        VALUE polygons, VALUE tolerance,
                        VALUE only_edges) {
  // Actual method
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there you go — you completely avoid the problem with an elegant
solution that is actually used for some methods in a Ruby
implementation (with the &lt;code&gt;Primitive&lt;/code&gt; class).&lt;/p&gt;
&lt;p&gt;Although the class used by the MRI is quite complex and generates C itself, we can get inspired by it.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s create an object and plug our methods
to avoid having a visible &lt;code&gt;c_voronoi_diagram&lt;/code&gt; for users of our API:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void Init_ext() {
  VALUE primary_mod = rb_define_module(&amp;quot;Hidden&amp;quot;)
  rb_define_method(primary_mod, &amp;quot;voronoi_diagram&amp;quot;, func, 4)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def voronoi_diagram(envelope, *polygons, tolerance:, only_edges: false)
  Hidden.voronoi_diagram(envelope, polygons, tolerance, only_edges)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rgeo/rgeo&quot;&gt;Check out a real use case of this class in RGeo&amp;#39;s codebase&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Parsing Arguments: Which Method Should I Use?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/BuonOmo/c-arg-parsing-comparison&quot;&gt;This repo&lt;/a&gt; showcases the two ways to set up a complex Ruby API
that is written in C — to recap:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;with &lt;code&gt;rb_define_method&lt;/code&gt; and parsing it with &lt;code&gt;rb_scan_args&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;using a Ruby interface&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When we compare
both solutions in terms of performance, they are roughly equal (Ruby parsing is
1.02 times faster on average on my M1). That doesn&amp;#39;t make much of a difference.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://github.com/rgeo/rgeo&quot;&gt;RGeo&lt;/a&gt; lib, our first design choice was to have an API that
only uses variadic length arguments. No keywords, no blocks. This can be
very limiting, and we are now using the &lt;code&gt;Primitive&lt;/code&gt; way to allow more
convoluted arguments.&lt;/p&gt;
&lt;h3&gt;Benefits of Using a Ruby Interface&lt;/h3&gt;
&lt;p&gt;My advice is to use a Ruby interface for multiple
reasons, including that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the code size is smaller&lt;/li&gt;
&lt;li&gt;changes are easier to make&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall,
this makes the experience of reading your codebase simpler.&lt;/p&gt;
&lt;p&gt;Engaging Ruby users in reading the source code of the gems they use is
important to me. As &lt;em&gt;Ruby is easy to read&lt;/em&gt;, gems should be as well.&lt;/p&gt;
&lt;h3&gt;Benefits of Using &lt;code&gt;rb_define_method&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The C version, however, gives us a taste of some very useful internal methods.
For instance, &lt;code&gt;rb_check_arity&lt;/code&gt; is still really useful. Methods for handling
blocks are great as well, and you might not need to use the Ruby facade for
blocks.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s all about finding the best choice for your use case.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we explored two methods to parse arguments in your Ruby C extension — using &lt;code&gt;rb_define_method&lt;/code&gt; (also looking briefly at its limitations) and parsing it with &lt;code&gt;rb_scan_args&lt;/code&gt; and using a Ruby interface.&lt;/p&gt;
&lt;p&gt;If you want to read more about C extensions, I recommend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.appsignal.com/2018/10/30/ruby-magic-building-a-ruby-c-extension-from-scratch.html&quot;&gt;Building a Ruby C Extension From Scratch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.peterzhu.ca/ruby-c-ext/&quot;&gt;A Rubyist&amp;#39;s Walk Along the C-side&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/klaxit-techblog/working-with-ruby-c-extensions-on-a-macbook-639c068a9815&quot;&gt;Working with ruby C extensions on Mac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And my final word of advice for you: check out &lt;a href=&quot;https://github.com/rgeo/rgeo#readme&quot;&gt;RGeo&lt;/a&gt;. It is a widely used C extension codebase in active development. Most of my examples come from here.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Create a Business Language for a Rails Application</title>
    <link rel="alternate" href="https://blog.appsignal.com/2023/01/11/create-a-business-language-for-a-rails-application.html"/>
    <id>https://blog.appsignal.com/2023/01/11/create-a-business-language-for-a-rails-application.html</id>
    <published>2023-01-11T00:00:00+00:00</published>
    <updated>2023-01-11T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Build and parse a programming language to extend your Rails application&#039;s functionality.</summary>
    <content type="html">&lt;p&gt;As web developers, we tend to approach problems with traditional low-risk solutions. When all you
have is a hammer, everything looks like a nail. When you need complex input from the user, you use a form and JSON
representation (even if, in retrospect, it is not the most efficient solution).&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll take a different approach. We&amp;#39;ll leverage some tooling to create a business language that extends the functionality of a Rails
application.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;A few years ago, my team implemented a feature
that enabled users to input a set of complex conditions. Our system presented results in accordance with these conditions.&lt;/p&gt;
&lt;p&gt;We took the conservative road and implemented a state-of-the-art form with multiple input widgets, drag and drop, and
all the UI sugar. Then the form state was serialized into a JSON object and sent to the backend, where it was
unpacked, conditions applied, and results sent back.&lt;/p&gt;
&lt;p&gt;It worked, but not without multiple problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The form was difficult to maintain, and bugs kept creeping in.&lt;/li&gt;
&lt;li&gt;It was also
very complex; most users could only use 10% of its capabilities.&lt;/li&gt;
&lt;li&gt;Any change to JSON representation had
to be implemented in two places: in the form frontend and the backend.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will now discuss a possible alternative approach that can solve at least some of the problems outlined above and that we had not considered at the time.&lt;/p&gt;
&lt;h2&gt;A Problem to Solve in a Rails App&lt;/h2&gt;
&lt;p&gt;First, let me describe the problem we will solve in more detail. This is a very simplified version of the actual
requirement I talked about above.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll build a backend for a promo mobile app, with a &amp;#39;Coupons&amp;#39; section. I&amp;#39;m sure you are familiar with this
concept from some real-world mobile applications as well.&lt;/p&gt;
&lt;p&gt;At any given moment, you&amp;#39;ll usually have a small number of coupons
available — let&amp;#39;s say, 10 to 30. This number is too large to fit on the screen, so to increase the conversion (usage) rate,
it is important to personalize the order of the coupons. The ones most likely to pique a user&amp;#39;s interest
should go first, based on data about the available user.&lt;/p&gt;
&lt;p&gt;So, when adding a new coupon to the system, the operator fills in the &amp;#39;targeting info&amp;#39;,
i.e., the cohort this should interest. This might be based on science, intuition, or stereotypes. It can be
very simplistic (&amp;quot;women aged 18–35&amp;quot;) or quite complex (&amp;quot;women aged 50+ interested in health, or men interested in sports or the outdoors, or people with children aged 10+&amp;quot;). In fact, any combination of data assertions we have should be possible with any
logical operators.&lt;/p&gt;
&lt;p&gt;Using the aforementioned JSON representation, we could represent a complex condition with something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;operator&amp;quot;: &amp;quot;or&amp;quot;,
  &amp;quot;conditions&amp;quot;: [
    {
      &amp;quot;operator&amp;quot;: &amp;quot;and&amp;quot;,
      &amp;quot;conditions&amp;quot;: [
        { &amp;quot;property&amp;quot;: &amp;quot;gender&amp;quot;, &amp;quot;operator&amp;quot;: &amp;quot;in&amp;quot;, &amp;quot;value&amp;quot;: [&amp;quot;woman&amp;quot;] },
        { &amp;quot;property&amp;quot;: &amp;quot;age&amp;quot;, &amp;quot;operator&amp;quot;: &amp;quot;gt&amp;quot;, &amp;quot;value&amp;quot;: 18 },
        { &amp;quot;property&amp;quot;: &amp;quot;age&amp;quot;, &amp;quot;operator&amp;quot;: &amp;quot;lt&amp;quot;, &amp;quot;value&amp;quot;: 35 }
      ]
    },
    {
      &amp;quot;operator&amp;quot;: &amp;quot;and&amp;quot;,
      &amp;quot;conditions&amp;quot;: [
        { &amp;quot;property&amp;quot;: &amp;quot;gender&amp;quot;, &amp;quot;operator&amp;quot;: &amp;quot;in&amp;quot;, &amp;quot;value&amp;quot;: [&amp;quot;man&amp;quot;] },
        {
          &amp;quot;property&amp;quot;: &amp;quot;interests&amp;quot;,
          &amp;quot;operator&amp;quot;: &amp;quot;intersect&amp;quot;,
          &amp;quot;value&amp;quot;: [&amp;quot;sports&amp;quot;, &amp;quot;outdoors&amp;quot;]
        }
      ]
    },
    {
      &amp;quot;property&amp;quot;: &amp;quot;child_birth_year&amp;quot;,
      &amp;quot;operator&amp;quot;: &amp;quot;between&amp;quot;,
      &amp;quot;value&amp;quot;: [2004, 2012]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though this is legible, it is hard to follow. And the form supporting it is tricky to understand as
well (actually, we haven&amp;#39;t even come up with a satisfactory solution for a form that mixes &amp;quot;and&amp;quot; and &amp;quot;or&amp;quot; logical
operators).&lt;/p&gt;
&lt;h2&gt;A Solution: Design a New Business Language&lt;/h2&gt;
&lt;p&gt;An alternative solution to this problem is to get rid of the form and the JSON
representation. Instead, we&amp;#39;ll design a whole new, specific business language to apply here and then implement it in a Rails
application. As a result, our condition will look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(gender(woman) and age &amp;gt; 18 and age &amp;lt; 35) or (gender(man) and (interest(sports) or interest(outdoors))) or has_child_aged(10, 18)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As this is much terser, it is easier to understand and debug. It could be really hard
for a regular user picked from the depths of the Internet to use. However, in cases like this, the actual users
can be trained with access to fast customer support.&lt;/p&gt;
&lt;p&gt;We could call our business language a &lt;strong&gt;domain-specific language&lt;/strong&gt; (DSL) because this is more or less the term&amp;#39;s original meaning. However, in recent years, especially in the Ruby community, the meaning of DSL has somewhat changed. When we talk about
DSLs, we usually mean bending the Ruby language to look like something else while it is still actually Ruby. Think
about RSpec:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;quot;a subject&amp;quot; do
  let(:number) { 15 }
  it { expect(NumberDoubler.call(number)).to eq(30) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though we introduce specific words defined as Ruby methods (&lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;it&lt;/code&gt;), this is still Ruby.&lt;/p&gt;
&lt;p&gt;It is
important to understand that the business language example above is not Ruby. Users won&amp;#39;t be able to access the filesystem,
the network, or make infinite loops. All the language constructs only allow talking about users and their properties
or combining expressions with logical operators.&lt;/p&gt;
&lt;p&gt;It is a completely new language, which needs to have a grammar,
parser, etc. This is what we are going to do next.&lt;/p&gt;
&lt;h2&gt;Implement a Business Language in Your Rails App with Parslet&lt;/h2&gt;
&lt;p&gt;To achieve our goals, we will use a gem called &lt;a href=&quot;https://kschiess.github.io/parslet/&quot;&gt;Parslet&lt;/a&gt;, for:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Constructing parsers in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Parsing_expression_grammar&quot;&gt;PEG&lt;/a&gt; (Parsing Expression Grammar)
fashion.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://kschiess.github.io/parslet/&quot;&gt;Parslet website&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PEG is relatively simple (compared to other techniques) and quite fast, especially for small
input.&lt;/p&gt;
&lt;p&gt;This post is not going to be a step-by-step Parslet tutorial. The &lt;a href=&quot;https://kschiess.github.io/parslet/documentation.html&quot;&gt;official Parslet documentation&lt;/a&gt; does a great job of going
through the process. But let&amp;#39;s briefly go over the building blocks of language interpreting with Parslet, which consists of
three steps.&lt;/p&gt;
&lt;p&gt;In step one, we define a &lt;strong&gt;parser&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CouponLang::Parser &amp;lt; Parslet::Parser
  rule(:lparen)     { str(&amp;#39;(&amp;#39;) &amp;gt;&amp;gt; space? }
  rule(:rparen)     { str(&amp;#39;)&amp;#39;) &amp;gt;&amp;gt; space? }
  # [...]
end

tree = CouponLang::Parser.new.parse(&amp;quot;age &amp;gt; 18 or age &amp;lt; 30&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of the &lt;code&gt;parse&lt;/code&gt; method is an &lt;strong&gt;intermediary tree&lt;/strong&gt;, which is a hash-like structure representing language tokens.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;This tree is fed to a &lt;strong&gt;transform&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ConjunctionExpr = Struct.new(:left, :right) do
  def eval(user)
    left.eval(user) &amp;amp;&amp;amp; right.eval(user)
  end
end

# [...]

class CouponLang::Transform &amp;lt; Parlet::Transform
  rule(:and =&amp;gt; [subtree(:left), subtree(:right)]) { ConjunctionExpr.new(left, right) }
  # [...]
end

tree = CouponLang::Parser.new.parse(&amp;quot;age &amp;gt; 18 or age &amp;lt; 30&amp;quot;)
ast = CouponLang::Transform.new.apply(tree)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of the transform is an &lt;strong&gt;abstract syntax tree&lt;/strong&gt; (AST). The final step is to evaluate the AST, passing
a user as a context:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;user = User.find(params[:user_id])
coupons = Coupon.active(Time.now)
proritized, others = coupons.partition do |coupon|
  tree = CouponLang::Parser.new.parse(coupon.priority_condition)
  ast = CouponLang::Transform.new.apply(tree)
  ast.eval(user)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last code listing shows how this can be used in a wider context, relevant to our original requirements.&lt;/p&gt;
&lt;p&gt;However, there is
one important limitation to mention. As you can see, all active coupons are fetched upfront, and we apply the conditions on them one by one. It is safe,
because, as stated before, only a handful of coupons are active at the time. However, this makes it impossible to
answer the question: &amp;quot;What users would coupon X prioritize?&amp;quot;&lt;/p&gt;
&lt;p&gt;If you really need to answer this kind of question, you have to fetch all the users and apply the conditions to
them, user by user, in the application layer.&lt;/p&gt;
&lt;p&gt;Of course, there are more efficient solutions.&lt;/p&gt;
&lt;p&gt;For example, you can write another transform which, instead of evaluating the conditions, translates them into an
SQL query which you can then execute.&lt;/p&gt;
&lt;h2&gt;A Complete Example&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m not going to lie to you: getting the code parser for the language right might be a tedious task. I spent a few hours
getting the example for this article working. For brevity, I show an example that only implements the &lt;code&gt;age&lt;/code&gt; and
&lt;code&gt;has_children&lt;/code&gt; functions for the user params.&lt;/p&gt;
&lt;p&gt;This is the parser:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module CouponLang
  class Parser &amp;lt; Parslet::Parser
    rule(:lparen) { str(&amp;quot;(&amp;quot;) &amp;gt;&amp;gt; space? }
    rule(:rparen) { str(&amp;quot;)&amp;quot;) &amp;gt;&amp;gt; space? }

    rule(:space) { match(&amp;#39;\s&amp;#39;).repeat(1) }
    rule(:space?) { space.maybe }
    rule(:sep) { space | any.absent? }

    rule(:or_) { str(&amp;quot;or&amp;quot;) &amp;gt;&amp;gt; space }
    rule(:and_) { str(&amp;quot;and&amp;quot;) &amp;gt;&amp;gt; space }

    rule(:gt) { str(&amp;quot;&amp;gt;&amp;quot;).as(:gt) &amp;gt;&amp;gt; space? }
    rule(:lt) { str(&amp;quot;&amp;lt;&amp;quot;).as(:lt) &amp;gt;&amp;gt; space? }
    rule(:comparison_op) { gt | lt }
    rule(:comparison) { int.as(:left) &amp;gt;&amp;gt; comparison_op &amp;gt;&amp;gt; int.as(:right) }

    rule(:integer) { match(&amp;quot;[0-9]&amp;quot;).repeat(1).as(:int) &amp;gt;&amp;gt; space? }
    rule(:string) { match[&amp;quot;a-z&amp;quot;].repeat(1).as(:string) &amp;gt;&amp;gt; space? }

    rule(:age_fun) { str(&amp;quot;age&amp;quot;).as(:age_fun) &amp;gt;&amp;gt; space? }
    rule(:has_children_fun) { str(&amp;quot;has_children&amp;quot;).as(:has_children_fun) &amp;gt;&amp;gt; sep }

    rule(:int) { age_fun | integer }
    rule(:bool) { comparison | has_children_fun | bool_in_parens }
    rule(:bool_in_parens) { lparen &amp;gt;&amp;gt; expr &amp;gt;&amp;gt; rparen }

    rule(:expr) { infix_expression(bool, [or_, 1, :left], [and_, 1, :left]) { |left, op, right| {op.to_s.strip.to_sym =&amp;gt; [left, right]} } }
    root(:expr)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this is the transform:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module CouponLang
  class Transform &amp;lt; Parslet::Transform
    UserAgeFun = Class.new do
      def eval(user) = user.age
    end

    HasChildrenFun = Class.new do
      def eval(user) = user.has_children?
    end

    IntLit = Struct.new(:int) do
      def eval(user) = int.to_i
    end

    InfixOp = Struct.new(:left, :op, :right) do
      def eval(user) = left.eval(user).public_send(op, right.eval(user))
    end

    LogicalOr = Struct.new(:left, :right) do
      def eval(user) = left.eval(user) || right.eval(user)
    end

    LogicalAnd = Struct.new(:left, :right) do
      def eval(user) = left.eval(user) &amp;amp;&amp;amp; right.eval(user)
    end

    rule(:age_fun =&amp;gt; simple(:_)) { UserAgeFun.new }
    rule(:has_children_fun =&amp;gt; simple(:_)) { HasChildrenFun.new }
    rule(:or =&amp;gt; [subtree(:left), subtree(:right)]) { LogicalOr.new(left, right) }
    rule(:and =&amp;gt; [subtree(:left), subtree(:right)]) { LogicalAnd.new(left, right) }
    rule(:left =&amp;gt; subtree(:left), :gt =&amp;gt; simple(:_), :right =&amp;gt; subtree(:right)) { InfixOp.new(left, :&amp;gt;, right) }
    rule(:left =&amp;gt; subtree(:left), :lt =&amp;gt; simple(:_), :right =&amp;gt; subtree(:right)) { InfixOp.new(left, :&amp;lt;, right) }
    rule(:int =&amp;gt; simple(:int)) { IntLit.new(int) }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s important to thoroughly test the language you have created. Otherwise, some unpleasant
surprises will be waiting for you when you want to change it in the future. An example of a spec for the parser looks
like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;RSpec.describe CouponLang::Parser do
  it &amp;quot;parses expression with parentheses&amp;quot; do
    expect(described_class.new.parse_with_debug(&amp;quot;has_children and (age &amp;gt; 12)&amp;quot;)).to eq(and: [{has_children_fun: &amp;quot;has_children&amp;quot;}, {left: {age_fun: &amp;quot;age&amp;quot;}, gt: &amp;quot;&amp;gt;&amp;quot;, right: {int: &amp;quot;12&amp;quot;}}])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And for the transform:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse_and_eval(code, user)
  tree = CouponLang::Parser.new.parse(code)
  ast = CouponLang::Transform.new.apply(tree)
  ast.eval(user)
end

RSpec.describe CouponLang::Transform do
  it &amp;quot;evaluates conditions with parentheses&amp;quot; do
    user_with_children = User.new(birth_year: 1980, child_birth_years: [2008, 2012])
    user = User.new(birth_year: 1981)
    code = &amp;quot;age &amp;gt; 65 or (has_children and age &amp;gt; 40)&amp;quot;

    expect(parse_and_eval(code, user_with_children)).to eq(true)
    expect(parse_and_eval(code, user)).to eq(false)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, for a full Rails integration, you must also validate whether a user puts
a valid &lt;code&gt;CouponLang&lt;/code&gt; code in a new coupon form. Parslet returns &lt;code&gt;nil&lt;/code&gt; when it cannot parse the text, so it is as simple as this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Coupon &amp;lt; ApplicationRecord
  validate :correct_code

  def correct_code
    CouponLang::Parser.new.parse(code).present?
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Extra Parsing Tips&lt;/h2&gt;
&lt;p&gt;We are ready to ship the &lt;code&gt;CouponLang&lt;/code&gt; to our internal users, with great flexibility for defining conditions.
The road to arrive here has not exactly been without bumps, so here are a few additional tips:&lt;/p&gt;
&lt;h3&gt;Start Small&lt;/h3&gt;
&lt;p&gt;Writing a parser is difficult. Start with the simplest possible language, make a parser for it, write
tests, commit, and add another feature.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;CouponLang&lt;/code&gt;, I first started with a language that could only evaluate
logical conditions with &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; values (like &lt;code&gt;false and true&lt;/code&gt;). Then I added parentheses — &lt;code&gt;(true or false) and true&lt;/code&gt;. Only after this was I ready to replace &lt;code&gt;true/false&lt;/code&gt; literals with comparisons and
functions.&lt;/p&gt;
&lt;h3&gt;Test Subparsers&lt;/h3&gt;
&lt;p&gt;I haven&amp;#39;t shown it, but Parslet allows you to test only a subset of parsers. With our language above,
you could use &lt;code&gt;CouponLang::Parser.new.comparison.parse(&amp;quot;11 &amp;gt; 12&amp;quot;)&lt;/code&gt; to just check if the comparison part is fine. This
is very useful for testing and debugging.&lt;/p&gt;
&lt;h3&gt;Check the Tree is Transform-friendly&lt;/h3&gt;
&lt;p&gt;Unless you are quite experienced in writing PEGs with Parslet, you may end up with a parser that works, but a tree
that&amp;#39;s not transform-friendly. As a result, you will have to change it. Be prepared for that.&lt;/p&gt;
&lt;h3&gt;Be Careful with Backward Compatibility&lt;/h3&gt;
&lt;p&gt;When changing the parser breaks backward compatibility, chances are
that the coupons already saved in the database will stop working. You should consider running &lt;strong&gt;every&lt;/strong&gt; coupon from
the production database against the new parser to check whether they will work after the change. It&amp;#39;s a one-time
operation, but important to save a lot of headaches.&lt;/p&gt;
&lt;h2&gt;When You Should Consider Using a Parser&lt;/h2&gt;
&lt;p&gt;You may think that my coupon example is quite unusual, even though it comes from a real-world problem. It&amp;#39;s true that what I show here is not for everyday Rails development. You need a good reason to implement such a powerful
(and not very user-friendly) tool as Parslet.&lt;/p&gt;
&lt;p&gt;And the reason is simple — it is still easier than trying to do it any other way.&lt;/p&gt;
&lt;p&gt;A few examples of when a parser might be
worth considering include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;For very fine access control to some resources&lt;/strong&gt; - e.g., when you need to make a document accessible only for &amp;quot;all
managers, members of team alpha or beta that have worked here for at least 3 months, and Jason from HR&amp;quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When modeling game-like conditions&lt;/strong&gt; - Want to wield the Epic Battleaxe of Doom? Sure, you just need to be at least level 32,
have at least 180 strength, and have finished the quest &amp;quot;Waiting for Ragnarok&amp;quot; or &amp;quot;The Abyss of Destiny&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember that you are not limited to languages that return a boolean as a result. You can, for example, create a language
that evaluates numbers and thus gives multiple rankings for users. Or restaurants. Or dogs.&lt;/p&gt;
&lt;p&gt;If you are brave enough, you could also build a language that behaves much more like a &amp;quot;real&amp;quot; programming language and
executes something with variables and conditionals. For example, if you build a system that reacts to certain
events (like a notification that a database is down):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if(notification.severity &amp;lt;= 2) {
  user = sample_from_groups(&amp;#39;sre)
  if(user.has_phone_number and notification.severity == 1) {
    send_sms_to(user)
  } else {
    send_email_to(user)
  }

  send_slack_notification(&amp;#39;alerts, notification.payload)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This, however, is much more complicated than the example I showed in this article. You might want to
&lt;a href=&quot;https://github.com/parttimenerd/parser-experiments/tree/master/SimpleJavaParser&quot;&gt;check out this attempt to parse Java source code with Parslet&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we saw how you can leverage tooling to build a programming language that extends your Rails application&amp;#39;s functionality. Even if you don&amp;#39;t need it in your current
project, it&amp;#39;s worth knowing that it is possible without too much effort.&lt;/p&gt;
&lt;p&gt;We also explored when it&amp;#39;s worthwhile to consider using this approach.&lt;/p&gt;
&lt;p&gt;If this piqued your curiosity about creating languages, I hope you will have a lot of fun experimenting.&lt;/p&gt;
&lt;p&gt;Happy parsing (and interpreting)!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>AppSignal’s Top 5 Ruby posts in 2022</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/12/21/appsignal’s-top-5-ruby-posts-in-2022.html"/>
    <id>https://blog.appsignal.com/2022/12/21/appsignal’s-top-5-ruby-posts-in-2022.html</id>
    <published>2022-12-21T00:00:00+00:00</published>
    <updated>2022-12-21T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s look at the top 5 most liked Ruby blog posts from 2022!</summary>
    <content type="html">&lt;p&gt;With the holidays approaching, the AppSignal team is taking a mini winter break from publishing Ruby posts. We hope to bring you a lot more of them in 2023. 🍵 ❄️&lt;/p&gt;
&lt;p&gt;In the meantime, let&amp;#39;s look at the top 5 most liked Ruby blog posts from 2022!&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://blog.appsignal.com/2022/06/22/state-machines-in-ruby-an-introduction.html&quot;&gt;State Machines in Ruby: An Introduction&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A state machine can hold all possible states of something and the allowed transitions between these states. For example, the state machine for a door would have only two states (&lt;code&gt;open&lt;/code&gt; and &lt;code&gt;closed&lt;/code&gt;) and only two transitions (&lt;code&gt;opening&lt;/code&gt; and &lt;code&gt;closing&lt;/code&gt;). In this post, we&amp;#39;ll look at how to set up a state machine in Ruby and use the state machines gem.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://blog.appsignal.com/2022/08/24/an-introduction-to-ractors-in-ruby.html&quot;&gt;An Introduction to Ractors in Ruby&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ll dive into ractors in Ruby, exploring how to build a ractor. You&amp;#39;ll send and receive messages in ractors, and learn about shareable and unshareable objects.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://blog.appsignal.com/2022/05/25/an-introduction-to-polymorphism-in-ruby-on-rails.html&quot;&gt;An Introduction to Polymorphism in Ruby on Rails&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This article will give you a greater understanding of polymorphism, specifically in Ruby on Rails. To accomplish this, we’ll dive into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The use of polymorphism in the real world&lt;/li&gt;
&lt;li&gt;Polymorphism in programming by way of OOP&lt;/li&gt;
&lt;li&gt;How you can incorporate it into your Rails application to help maintain high-quality code&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;&lt;a href=&quot;https://blog.appsignal.com/2022/07/06/get-started-with-hotwire-in-your-ruby-on-rails-app.html&quot;&gt;Get Started with Hotwire in Your Ruby on Rails App&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hotwire is a completely new way of adding interactivity to your app with very few lines of code, and it works blazing fast by transmitting HTML over the wire. That means you can keep your hands clean from most Single Page Applications (SPA) frameworks. You can also keep your rendering logic centralized on the server, while still maintaining quick page load times and interactivity.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://blog.appsignal.com/2022/03/30/5-tips-to-design-ruby-on-rails-transactions-the-right-way.html&quot;&gt;5 Tips to Design Ruby on Rails Transactions the Right Way&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This article offers a set of good practices for working with transactions. The tips are pretty simple, but they will help make your transactions bulletproof, readable, and relatively safe.&lt;/p&gt;
&lt;h2&gt;Honorable Mention - &lt;a href=&quot;https://blog.appsignal.com/2022/01/26/test-and-optimize-your-ruby-on-rails-database-performance.html&quot;&gt;Test and Optimize Your Ruby on Rails Database Performance&lt;/a&gt;&lt;/h2&gt;
&lt;h2&gt;AFK Time!&lt;/h2&gt;
&lt;p&gt;We hope you&amp;#39;ve learned something new while reading our blog this year. We know we have! In 2023, our team will continue working hard to publish new blog posts to spread knowledge across the Ruby community and bring you the best content and resources to help you improve your skills and build performant Ruby applications.&lt;/p&gt;
&lt;p&gt;Wherever you are in the world, we hope you enjoy a happy and healthy end of year. We hope that 2023 will be an amazing year for you, with no bugs and packed with events and adventures that are almost as sweet as a stroopwafel.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Guide to Memoization in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/12/20/a-guide-to-memoization-in-ruby.html"/>
    <id>https://blog.appsignal.com/2022/12/20/a-guide-to-memoization-in-ruby.html</id>
    <published>2022-12-20T00:00:00+00:00</published>
    <updated>2022-12-20T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover the benefits of memoization for your Ruby application, common mistakes to avoid, and when not to memoize.</summary>
    <content type="html">&lt;p&gt;Memoization is a caching technique to make your Ruby application run more efficiently and faster.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll look at the benefits of memoization and when to use it in your Ruby application. We&amp;#39;ll also look at some memoization mistakes to avoid.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s first start by looking at code optimization — what it is and some of the different optimization techniques available.&lt;/p&gt;
&lt;h2&gt;What Is Code Optimization?&lt;/h2&gt;
&lt;p&gt;Code optimization is the process of improving code quality to make a piece of code or program more efficient and functional. Its advantages include — but are not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Less memory consumption during an expensive calculation&lt;/li&gt;
&lt;li&gt;Much faster execution&lt;/li&gt;
&lt;li&gt;Sometimes, less space in terms of codebase size&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The need to achieve some of the goals mentioned above arises at one time or another during an app&amp;#39;s life cycle. If you&amp;#39;re at a loss about where to begin with code optimization, try profiling!&lt;/p&gt;
&lt;h2&gt;What Is Profiling?&lt;/h2&gt;
&lt;p&gt;Profiling refers to analyzing a program to measure its space and time complexities. Via profiling, we can obtain information such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The frequency and duration of function calls&lt;/li&gt;
&lt;li&gt;The percentage of program execution time spent in a function in comparison with other functions&lt;/li&gt;
&lt;li&gt;The call stack of every function&lt;/li&gt;
&lt;li&gt;How many database calls are made for an HTML page to successfully load and how long it takes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This information can guide us to where code optimization is highly required.&lt;/p&gt;
&lt;h2&gt;Code Optimization Methods for Ruby and Rails&lt;/h2&gt;
&lt;p&gt;A few Ruby and Rails code optimization techniques include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Getting rid of N+1 queries&lt;/strong&gt; - This can help improve the speed of an app. The &lt;a href=&quot;https://rubygems.org/gems/bullet/versions/6.1.0&quot;&gt;Bullet&lt;/a&gt; or &lt;a href=&quot;https://rubygems.org/gems/prosopite/versions/1.0.8&quot;&gt;Prosopite&lt;/a&gt; gems can give a lending hand here. &lt;a href=&quot;https://labs.factorial.dev/posts/bullet-or-prosopite-for-nplus1&quot;&gt;The N+1 Dilemma — Bullet or Prosopite?&lt;/a&gt; entails a brief comparison of both.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using static code analyzers&lt;/strong&gt; - These reduce memory consumption and codebase size, as we&amp;#39;re alerted to code duplication, unused variables or method arguments, and the like. Examples include &lt;a href=&quot;https://rubocop.org/&quot;&gt;Rubocop&lt;/a&gt; and &lt;a href=&quot;https://blog.appsignal.com/2022/10/19/improve-code-in-your-ruby-application-with-rubycritic.html&quot;&gt;RubyCritic&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching&lt;/strong&gt; - Stores the content generated during the request-response cycle and reuses it when responding to similar requests, to improve the speed of an application. In Rails, we have page caching, fragment caching, action caching, low-level caching, and many more.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choosing appropriate data structures&lt;/strong&gt; - Some data structures perform better in certain situations than others. As a result, a good way to optimize code is to use the most appropriate data structures for each situation, considering their space and time complexities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memoization&lt;/strong&gt; - Improves speed by reducing the number of times certain computations are carried out.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s now turn our focus to memoization.&lt;/p&gt;
&lt;h2&gt;An Introduction to Memoization in Ruby&lt;/h2&gt;
&lt;p&gt;Memoization is the act of caching a method&amp;#39;s result so that the next time that method is called, the previous result is returned (as opposed to carrying out a recomputation). This helps save time when running a program.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at an example involving anagrams.&lt;/p&gt;
&lt;p&gt;In the class below, we create a dictionary to pass in any word as an argument, and find the anagram.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Dictionary
  def words
    puts &amp;quot;creating my dictionary&amp;quot;
    words = File.readlines(&amp;#39;/usr/share/dict/words&amp;#39;)
    dictionary = Hash.new {|h,k| h[k] = []}
    words.each do |word|
      word = word.chomp
      dictionary[word.chars.sort.join(&amp;quot;&amp;quot;)] = word
    end
    dictionary
  end

  def check(word)
    words[word.chars.sort.join(&amp;quot;&amp;quot;)]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Testing the class above, we obtain:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;dictionary = Dictionary.new
dictionary.check(&amp;#39;rasp&amp;#39;)
# creating my dictionary
=&amp;gt; &amp;quot;spar&amp;quot;
dictionary.check(&amp;#39;kame&amp;#39;)
# creating my dictionary
=&amp;gt; &amp;quot;make&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see that every time we call the check method, we create the dictionary again. This is definitely not optimal, as the dictionary does not change.&lt;/p&gt;
&lt;p&gt;What if we created the dictionary once and used it whenever it was needed? We can; using memoization. Memoization allows us to cache the dictionary if it has been previously created.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def words
  @words ||= begin
    puts &amp;quot;creating my dictionary&amp;quot;
    words = File.readlines(&amp;#39;/usr/share/dict/words&amp;#39;)
    dictionary = Hash.new {|h,k| h[k] = []}
    words.each do |word|
      word = word.chomp
      dictionary[word.chars.sort.join(&amp;quot;&amp;quot;)] = word
    end
    dictionary
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Testing the above, we obtain:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;dict.check(&amp;#39;eat&amp;#39;)
#creating my dictionary
=&amp;gt; &amp;quot;tea&amp;quot;
dict.check(&amp;#39;kame&amp;#39;)
=&amp;gt; &amp;quot;make&amp;quot;
dict.check(&amp;#39;live&amp;#39;)
=&amp;gt; &amp;quot;vile&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, the dictionary is created once and the cached version is used after that.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take a benchmark to see how much faster our program has become because of memoization. Name the memoized version &lt;code&gt;memoized_check&lt;/code&gt;, and the non-memoized version &lt;code&gt;check&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;quot;benchmark&amp;quot;

dictionary = Dictionary.new
puts Benchmark.measure { 10.times { dictionary.check(&amp;#39;rasp&amp;#39;) } }
puts Benchmark.measure { 10.times { dictionary.memoized_check(&amp;#39;rasp&amp;#39;) } }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We get the following result:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;5.771061   0.044656   5.815717 (  5.836218)
0.563966   0.000016   0.563982 (  0.564909)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This shows us that the unmemoized version takes &lt;code&gt;5.83&lt;/code&gt; seconds while the memoized one takes &lt;code&gt;0.56&lt;/code&gt; seconds, making it about ten times faster.&lt;/p&gt;
&lt;h2&gt;Memoization Mistakes to Avoid in Your Ruby Application&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s now look at some mistakes to avoid when using memoization.&lt;/p&gt;
&lt;h3&gt;Ignoring the False Or Nil Return&lt;/h3&gt;
&lt;p&gt;In cases where a method&amp;#39;s computation returns a false or nil, each call leads to a recomputation whenever the method is called (even though memoized). This is because the comparison is made using an &lt;code&gt;or&lt;/code&gt; — and in Ruby, both &lt;code&gt;nil&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; are &lt;code&gt;false&lt;/code&gt; values.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;2 || 4+5
=&amp;gt; 2
nil || 4+5
=&amp;gt; 9
false || 4+5
=&amp;gt; 9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Memoization using &lt;code&gt;||=&lt;/code&gt; doesn&amp;#39;t consider false/nil return values, so such cases should be handled.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def do_computation
  puts &amp;quot;I am computing&amp;quot;
  nil
end

def check
  @check ||= do_computation
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling the check method, we get the following results:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;check
# I am computing
=&amp;gt; nil
check
# I am computing
=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To deal with this, we can ascertain if the variable has been defined. If so, we return early before proceeding to the computation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def check
  return @check if defined?(@check)
  @check ||= do_computation
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This results in:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;check
# I am computing
=&amp;gt; nil
check
=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Passing Parameters to a Method&lt;/h3&gt;
&lt;p&gt;Another common mistake is assuming memoization will work differently when parameters are passed to a method.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say that a method&amp;#39;s result is memoized using &lt;code&gt;||=&lt;/code&gt;, but that result depends on parameters. If these parameters change, the result does not change.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def change_params(num)
  @params ||= num
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s see what happens here:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;change_params(4)
=&amp;gt; 4
change_params(8)
=&amp;gt; 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result does not change just because we changed the parameters, and frankly, this is what is expected, considering how memoization works in its basic form.&lt;/p&gt;
&lt;p&gt;To deal with cases like this, you have to be comfortable with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;storing the parameters as keys in a hash&lt;/li&gt;
&lt;li&gt;using a gem or module that takes all the different cases to be handled into consideration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using a hash:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def change_params(num)
  @params_hash ||= {}
  if (@params_hash.has_key?(num))
    @params_hash[num]
  else
    puts &amp;#39;creating a new key-value pair&amp;#39;
    @params_hash[num] = num
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Trying this out, we have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;change_params(4)
creating a new key-value pair
=&amp;gt; 4
change_params(8)
creating a new key-value pair
=&amp;gt; 8
change_params(4)
=&amp;gt; 4
change_params(8)
=&amp;gt; 8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another way to rewrite this would be as the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def change_params(num)
  @params_hash ||= {}
  @params_hash[num] ||= num
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or you can use the gem &lt;a href=&quot;https://github.com/matthewrudy/memoist&quot;&gt;Memoist&lt;/a&gt;. It handles caching the method&amp;#39;s results, considering the arguments passed. It also provides a way to flush the current value or the entire memoization cache for an object.&lt;/p&gt;
&lt;h2&gt;When To Memoize — and When Not To&lt;/h2&gt;
&lt;p&gt;To decide when to memoize, take note of the following:&lt;/p&gt;
&lt;h3&gt;Expensive Operations&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s say that an expensive operation will most definitely be called multiple times within a class and return the same result.&lt;/p&gt;
&lt;p&gt;We can move this into an instance variable initialized upon object instantiation (the expensive operation is also done at this time).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;attr_reader :result

def initialize
  @result = do_expensive_calculation
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;result&lt;/code&gt; is available throughout the lifecycle of the class instance, and the expensive calculation is only done once.&lt;/p&gt;
&lt;p&gt;In cases like this, we don&amp;#39;t need a separate method to memoize the &lt;code&gt;do_expensive_calculation&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;An expensive calculation that might not happen — but if it does, might happen more than once (and return the same value) — is a good candidate for memoization. This means we only &lt;code&gt;do_expensive_calculation&lt;/code&gt; when needed and then cache the result.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def expensive_calculation
  @expensive_calculation ||= do_expensive_calculation
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Analysing Potential Performance Improvements&lt;/h3&gt;
&lt;p&gt;Memoization may be seen as necessary only after we&amp;#39;ve carried out a performance analysis. We need to accurately ascertain that the implemented memoization actually improves performance (as we did within the &lt;code&gt;Dictionary&lt;/code&gt; class).&lt;/p&gt;
&lt;p&gt;Without this, we could add unnecessary complexities to the code base. Be sure that the space and code complexities incurred from memoization are low compared to the gain in run-time.&lt;/p&gt;
&lt;h3&gt;Changing Parameters&lt;/h3&gt;
&lt;p&gt;If the parameters used for computation are constantly changing, memoization is not a good option.&lt;/p&gt;
&lt;p&gt;Memoization is more suitable for pure functions whose return values are the same for the same set of arguments.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def calculation(a, b)
  a + b + Time.now.to_i
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, let&amp;#39;s say we do cache the method result. Every other time we call the method, our cached value is wrong because &lt;code&gt;Time.now&lt;/code&gt; changes.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we explored how memoization, like every caching technique, comes with its advantages and disadvantages. We first looked at a range of code optimization techniques before diving into an example involving memoization. Then we touched on some mistakes to look out for. Finally, we explored when memoization is beneficial and when to avoid it.&lt;/p&gt;
&lt;p&gt;When memoization is carried out on methods available to class instances, the memoized result is only available for the lifecycle of that object. Where this result is the same for multiple class instances (e.g., multiple web requests), class-level memoization is usually a go-to.&lt;/p&gt;
&lt;p&gt;However, this might add more complexities in terms of cache invalidation. Using a cache store might be a better alternative to caching and allow for better optimization.&lt;/p&gt;
&lt;p&gt;Before you decide to employ it, you must ascertain that the pros of memoization outweigh the cons for your specific use case.&lt;/p&gt;
&lt;p&gt;Happy memoizing!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Database Performance Optimization and Scaling in Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/12/07/database-performance-optimization-and-scaling-in-rails.html"/>
    <id>https://blog.appsignal.com/2022/12/07/database-performance-optimization-and-scaling-in-rails.html</id>
    <published>2022-12-07T00:00:00+00:00</published>
    <updated>2022-12-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Improve your Rails application&#039;s performance by fine-tuning and scaling your database.</summary>
    <content type="html">&lt;p&gt;Web applications usually rely heavily on databases, for the most part. And as applications grow, databases grow too. We keep scaling web servers and background workers to keep up with the heavy load. But eventually, the database needs to keep up with all the new connections from these processes.&lt;/p&gt;
&lt;p&gt;One way to tackle this is to grow a database with an app using vertical scaling. This means adding more CPU power and memory to the database server. But this is usually slow. You might have to copy all your data to the new server and then put the application down to change the database servers that it communicates with.&lt;/p&gt;
&lt;p&gt;This is also usually a one-way operation. You can&amp;#39;t keep adding/removing CPU power or memory from the database server based on your load.&lt;/p&gt;
&lt;p&gt;This post will cover some alternative methods to fine-tune and scale a database under heavy load and improve the performance of your Rails app. We will focus on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Splitting schema and data between multiple databases&lt;/li&gt;
&lt;li&gt;Using read-only replica databases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;Disclaimer: Scaling Isn&amp;#39;t Always Needed&lt;/h2&gt;
&lt;p&gt;As always, with any post about performance optimization and scaling, I would like to put up a standard disclaimer: &lt;strong&gt;make sure you have an issue before trying to solve it&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For example, on one of our apps in production, we tackled scaling and optimization only when we started processing more than 3 million background jobs a day.&lt;/p&gt;
&lt;p&gt;This will, of course, be different for different apps. But usually, a good indicator of whether you need to optimize is if your database is always running at high CPU or memory usage.&lt;/p&gt;
&lt;h2&gt;A Separate Database for Your Rails Application&lt;/h2&gt;
&lt;p&gt;Sometimes, simply using a separate database server for a part of your app is a good solution. For example, your app might do two different things that don’t have a big overlap. Alternatively, maybe there&amp;#39;s one very database-heavy feature, but it is only used rarely or by a small section of users. You should go for a separate database server if — and only if — there is a clear distinction between the parts of your app that will use different databases.&lt;/p&gt;
&lt;p&gt;For example, a separate database is useful when you&amp;#39;re audit logging in a high-frequency app (or have other very high-volume data that isn&amp;#39;t necessarily accessed frequently). Let&amp;#39;s see how we can set this up.&lt;/p&gt;
&lt;p&gt;First, set up the &lt;code&gt;database.yml&lt;/code&gt; to configure the second database. Let’s call the second database &lt;code&gt;log&lt;/code&gt;. We&amp;#39;ll add a &lt;code&gt;log_default&lt;/code&gt; entry for the secondary database with a common configuration across all environments:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;log_default: &amp;amp;log_default
  host: localhost
  port: &amp;lt;%= ENV[&amp;quot;DB_PORT&amp;quot;] || 5432 %&amp;gt;
  adapter: postgresql
  encoding: unicode
  user: &amp;lt;%= ENV[&amp;quot;DB_USER&amp;quot;] || &amp;quot;postgres&amp;quot; %&amp;gt;
  password: &amp;lt;%= ENV[&amp;quot;DB_PASSWORD&amp;quot;] || &amp;quot;postgres&amp;quot; %&amp;gt;
  migrations_paths: db/log_migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important bit here is the &lt;code&gt;migrations_paths&lt;/code&gt; option that tells Rails where to find migrations related to this database.&lt;/p&gt;
&lt;p&gt;Then, update &lt;code&gt;database.yml&lt;/code&gt; to configure access to the log database for &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, and &lt;code&gt;production&lt;/code&gt; environments:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;development:
  primary:
    &amp;lt;&amp;lt;: *default
    database: &amp;lt;%= ENV[&amp;quot;DB_NAME&amp;quot;] %&amp;gt;
  log:
    &amp;lt;&amp;lt;: *log_default
    database: &amp;lt;%= ENV[&amp;quot;LOG_DB_NAME&amp;quot;] %&amp;gt;

test:
  primary:
    &amp;lt;&amp;lt;: *default
    database: my_app_test
  log:
    &amp;lt;&amp;lt;: *log_default
    database: my_app_log_test

production:
  primary:
    adapter: postgresql
    encoding: unicode
    url: &amp;lt;%= ENV[&amp;quot;DATABASE_URL&amp;quot;] %&amp;gt;
  log:
    adapter: postgresql
    encoding: unicode
    migrations_paths: db/log_migrate
    url: &amp;lt;%= ENV[&amp;quot;DATABASE_LOG_URL&amp;quot;] %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we move the configuration for each environment one level deeper. The configuration for our primary database lives inside the &lt;code&gt;primary&lt;/code&gt; key for each environment. The second database&amp;#39;s configuration (that we call &lt;code&gt;log&lt;/code&gt;) lives inside the &lt;code&gt;log&lt;/code&gt; key. &lt;code&gt;primary&lt;/code&gt; is a special keyword that tells Rails to use this database as the default.&lt;/p&gt;
&lt;p&gt;The next step is to set up &lt;code&gt;ActiveRecord&lt;/code&gt; to use this new database. We first create a base class that establishes a connection to this database (instead of a primary one) using &lt;code&gt;establish_connection&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LogRecord &amp;lt; ActiveRecord::Base
  self.abstract_class = true
  establish_connection :log
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then, we inherit from &lt;code&gt;LogRecord&lt;/code&gt; instead of &lt;code&gt;ApplicationRecord&lt;/code&gt; for all the records that should access the &lt;code&gt;log&lt;/code&gt; DB:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AuditLog &amp;lt; LogRecord
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that you keep using the rails generators for generating models and migrations by appending the &lt;code&gt;--database log&lt;/code&gt; option to target the second database. The migration task now automatically migrates both databases, but if you need to only migrate one, you can use &lt;code&gt;db:migrate:primary&lt;/code&gt; or &lt;code&gt;db:migrate:log&lt;/code&gt; tasks.&lt;/p&gt;
&lt;p&gt;This works great if there is a clear distinction between two parts of the app. But what if you don’t have a clear idea of the database that&amp;#39;s creating issues? There are still a couple of options left. Let&amp;#39;s check them out.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Using Read-Only Replicas for Rails&lt;/h2&gt;
&lt;p&gt;Our second scaling option is to access data from read-only replicas while still writing to the primary database. This can improve performance for users who only browse parts of the app without performing any writing operations. Depending on your app&amp;#39;s use case, this can be 80% of your users or 10% of them. So, evaluate the behavior of your users or your application use case before going down this route.&lt;/p&gt;
&lt;p&gt;For an app with a majority of read-only users (like Twitter, for example), you can gain huge improvements in performance. New read-only replicas can be added/removed at will without affecting the primary database, which opens up options for auto-scalability.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see how to set this up.&lt;/p&gt;
&lt;h3&gt;Setting up a Read-Only Replica&lt;/h3&gt;
&lt;p&gt;As usual, we will start with modifications to the &lt;code&gt;database.yml&lt;/code&gt; config to include the replica:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;production:
  primary:
    adapter: postgresql
    encoding: unicode
    url: &amp;lt;%= ENV[&amp;quot;DATABASE_URL&amp;quot;] %&amp;gt;
  primary_replica:
    adapter: postgresql
    encoding: unicode
    url: &amp;lt;%= ENV[&amp;quot;DATABASE_REPLICA_URL&amp;quot;] %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, the &lt;code&gt;primary&lt;/code&gt; key is a special key indicating that this database is the default. We can use any other key for the replica database configuration. Let’s choose &lt;code&gt;primary_replica&lt;/code&gt; for sanity.&lt;/p&gt;
&lt;p&gt;Then update the &lt;code&gt;ApplicationRecord&lt;/code&gt; to configure a connection to multiple databases:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ApplicationRecord &amp;lt; ActiveRecord::Base
  self.abstract_class = true

  connects_to database: { writing: :primary, reading: :primary_replica } if Rails.env.production?
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we add configuration for a &lt;code&gt;production&lt;/code&gt; environment only in the above example. If you want to, you can set it up for all environments, after modifying your development setup to support replicas.&lt;/p&gt;
&lt;p&gt;Finally, to use the read-only replica, you need to tell Rails when to use the primary database and when to use the replica. Rails provides a basic implementation for automatic role-switching based on the HTTP verb of the request out of the box. To enable it, add the following to your &lt;code&gt;production.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Use Read-Only Databases on GET requests
config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells Rails to use the replica for all &lt;code&gt;GET&lt;/code&gt; or &lt;code&gt;HEAD&lt;/code&gt; requests and the primary one otherwise. Notice the option that sets the delay to two seconds? That tells Rails to keep using the primary database for &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;HEAD&lt;/code&gt; requests if it is within two seconds of a write request in the same session.&lt;/p&gt;
&lt;h3&gt;Gotchas&lt;/h3&gt;
&lt;p&gt;Does all of this sound too simple? Well, we aren’t done yet. Depending on how your application is structured, you need to keep a few points in mind.&lt;/p&gt;
&lt;p&gt;Firstly, ensure you do not write anything in a read-only action like &lt;code&gt;index&lt;/code&gt; or &lt;code&gt;show&lt;/code&gt;. If there are legitimate reasons for writing during those operations, you can manually switch the connection:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Base.connected_to(role: :writing) do
  # Your code here
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A good example where this can be useful is if you are managing sessions in a database. If you do that, make sure you use the &lt;code&gt;writing&lt;/code&gt; role. For example, you can override the &lt;code&gt;save_record&lt;/code&gt; method in your session with &lt;code&gt;authlogic&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UserSession &amp;lt; Authlogic::Session::Base
  # Your session configuration

  def save_record(alternate_record = nil)
    ActiveRecord::Base.connected_to(role: :writing) do
      super
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another use case for automatic role-switching is when an app exposes a GraphQL API. All operations on a GraphQL API are &lt;code&gt;POST&lt;/code&gt;, but conventionally, all &lt;code&gt;queries&lt;/code&gt; are read-only, and &lt;code&gt;mutations&lt;/code&gt; are read-write. In this case, to allow Rails to select the correct database, we can use a custom tracer in the schema.&lt;/p&gt;
&lt;p&gt;First, create a tracer that switches roles based on the GraphQL document:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Tracers
  class DatabaseRoleTracer
    EVENT_NAME = &amp;quot;execute_multiplex&amp;quot;

    # Execute on read only database if the operation has only queries.
    def trace(event, data)
      return yield unless Rails.env.production?
      return yield unless event == EVENT_NAME

      multiplex = data[:multiplex]
      ActiveRecord::Base.connected_to(role: role(multiplex)) do
        yield
      end
    end

    private

    def role(multiplex)
      if multiplex.queries.all?(&amp;amp;:query?)
        ActiveRecord::Base.reading_role
      else
        ActiveRecord::Base.writing_role
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then use the &lt;code&gt;tracer&lt;/code&gt; in your GraphQL Schema:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyAppSchema &amp;lt; GraphQL::Schema
  tracer Tracers::DatabaseRoleTracer.new

  # Your code here
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s get to the final alternative. What can you do if your application still has a huge load on your primary database? The answer: database sharding.&lt;/p&gt;
&lt;h2&gt;Database Sharding in Your Rails Application&lt;/h2&gt;
&lt;p&gt;This is the most complex of all the methods for scaling databases discussed in this post. So reach for it only if you need it. It will add considerable complexity to your application and needs to be done right to provide any real benefit.&lt;/p&gt;
&lt;p&gt;There are two strategies to shard your database:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Distribute different tables on different nodes (vertical sharding).&lt;/li&gt;
&lt;li&gt;Have the same schema across all nodes, but distribute data depending on certain parameters (horizontal sharding).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Vertical sharding is similar to our “multiple databases” strategy in terms of setup and pros and cons, so we will not discuss it again.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s discuss horizontal sharding and where it could be helpful.&lt;/p&gt;
&lt;p&gt;One example is when you have a SaaS platform where a lot of data is associated with each user, but there is no overlap between the data of two users. In this case, splitting the data across nodes based on the user&amp;#39;s id is the most logical way to go. Every signed-in user will have to access only a single database, so we won&amp;#39;t have to reach out to multiple databases at the same time.&lt;/p&gt;
&lt;h3&gt;Setting up Horizontal Sharding&lt;/h3&gt;
&lt;p&gt;Let’s start with the configuration inside &lt;code&gt;database.yml&lt;/code&gt; again:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;production:
  primary:
    adapter: postgresql
    encoding: unicode
    url: &amp;lt;%= ENV[&amp;quot;DATABASE_URL&amp;quot;] %&amp;gt;
  primary_replica:
    adapter: postgresql
    encoding: unicode
    url: &amp;lt;%= ENV[&amp;quot;DATABASE_REPLICA_URL&amp;quot;] %&amp;gt;
  primary_shard_one:
    adapter: postgresql
    encoding: unicode
    url: &amp;lt;%= ENV[&amp;quot;DATABASE_SHARD_ONE_URL&amp;quot;] %&amp;gt;
    primary_shard_one_replica:
    adapter: postgresql
    encoding: unicode
    url: &amp;lt;%= ENV[&amp;quot;DATABASE_SHARD_ONE_REPLICA_URL&amp;quot;] %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then modify the &lt;code&gt;ApplicationRecord&lt;/code&gt; to connect to different shards:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;class ApplicationRecord &amp;lt; ActiveRecord::Base
  self.abstract_class = true

  connects_to shards: {
    default: { writing: :primary, reading: :primary_replica },
    shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica }
  }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can use the &lt;code&gt;shard&lt;/code&gt; option with &lt;a href=&quot;https://edgeapi.rubyonrails.org/classes/ActiveRecord/ConnectionHandling.html#method-i-connected_to&quot;&gt;&lt;code&gt;ActiveRecord::Base.connected_to&lt;/code&gt;&lt;/a&gt; to switch shards. Like the automatic database role selector, Rails also provides an automatic shard switcher that can be activated inside &lt;code&gt;production.rb&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.configure do
  config.active_record.shard_selector = { lock: true }
  config.active_record.shard_resolver = -&amp;gt;(request) { Tenant.find_by!(host: request.host).shard }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;shard_resolver&lt;/code&gt; lambda is the most interesting part. The above implementation relies on the assumption that our application is accessed from different domains/subdomains and distinguishes between the shards. Modify it to fit your application needs (you might need to access cookies to identify a user before switching the shard).&lt;/p&gt;
&lt;p&gt;As with vertical scaling, this strategy is usually a one-way street. Once you shard a database, it is very hard to “un-shard” it (since several databases could have the same ids for different objects). But this lays down the foundation to really scale your app to millions or billions of users when fitting everything on the same server is not an option.&lt;/p&gt;
&lt;p&gt;If you want to read more about database sharding, see this &lt;a href=&quot;https://www.linode.com/docs/guides/sharded-database/&quot;&gt;write-up by Linode&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Measure Your Rails Database Performance with AppSignal&lt;/h2&gt;
&lt;p&gt;It can be tricky to keep an eye on the performance of your database without any other tools. Using AppSignal, you can easily track how your databases perform. See our &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;AppSignal for Ruby page&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;You can also check out our post &lt;a href=&quot;https://blog.appsignal.com/2022/01/26/test-and-optimize-your-ruby-on-rails-database-performance.html&quot;&gt;Test and Optimize Your Ruby on Rails Database Performance&lt;/a&gt; for more information on the metrics you can measure in AppSignal and how a dashboard might look.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we discussed three major strategies to scale your database servers horizontally:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Splitting schema between multiple databases&lt;/li&gt;
&lt;li&gt;Using read-only replica databases&lt;/li&gt;
&lt;li&gt;Splitting data between multiple databases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All strategies have their pros and cons depending on your application use case and scale.&lt;/p&gt;
&lt;p&gt;Rails has really upped its database game in its last few releases. This post has shown how easy it is to set up a Rails app to use multiple databases.&lt;/p&gt;
&lt;p&gt;As always, if your application allows it, I suggest starting small with optimizations. The easiest optimization to start with is a read-only replica, then only move on when you need more scale.&lt;/p&gt;
&lt;p&gt;Happy scaling!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A First Look at Hanami 2 for Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/12/06/a-first-look-at-hanami-2-for-ruby.html"/>
    <id>https://blog.appsignal.com/2022/12/06/a-first-look-at-hanami-2-for-ruby.html</id>
    <published>2022-12-06T00:00:00+00:00</published>
    <updated>2022-12-06T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s look at what Hanami 2.0 brings in terms of slices, dependency management and performance.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;As of today (06/12/2022), Hanami 2.0.1 has been released. &lt;a href=&quot;https://hanamirb.org/blog/2022/12/06/hanami-201/&quot;&gt;Read more about the enhancements, bug fixes and gems in release 2.0.1&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Hanami 2 was released on 22 November, concluding four years of work on this version. It brings a breath of fresh
air into Ruby&amp;#39;s web development community. Version 2.0 is not just an incremental upgrade. One could say it&amp;#39;s a project
written anew, with bright ideas from version one rebuilt on top of a solid dry-rb libraries ecosystem.&lt;/p&gt;
&lt;p&gt;Since there is no clear upgrade path from version 1.3, in this article, we will focus on some key aspects of the release rather than comparing the two versions.&lt;/p&gt;
&lt;p&gt;We will take a close look at the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Slices&lt;/li&gt;
&lt;li&gt;Dependency management&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Slices in Hanami 2&lt;/h2&gt;
&lt;p&gt;Hanami&amp;#39;s go-to solution for setting boundaries between different
contexts of a growing application is slices. By providing separate namespaces, slices clarify which code belongs to which
area and when the boundaries are crossed.&lt;/p&gt;
&lt;p&gt;Note that, as useful as they are, slices are also completely optional. If your app is simple or you don&amp;#39;t yet know what
slices will emerge, you can just put your code under a &amp;quot;main&amp;quot; slice in the &lt;code&gt;app&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;A new slice can be created within an existing Hanami application using a CLI generator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; hanami generate slice accounts
Updated config/routes.rb
Created slices/accounts/
Created slices/accounts/action.rb
Created slices/accounts/actions/.keep
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produces a very bare-bones structure under the &lt;code&gt;slices&lt;/code&gt; subdirectory, with an &lt;code&gt;Accounts::Action&lt;/code&gt; superclass and a
directory where account management actions will be created. These classes will, by convention, inherit from
&lt;code&gt;Accounts::Action&lt;/code&gt;. By doing so, they gain behaviors specific to the application&amp;#39;s user account area.&lt;/p&gt;
&lt;p&gt;You may wonder what &amp;quot;actions&amp;quot; are, exactly. They are Hanami&amp;#39;s take on organizing web endpoints. In Rails,
a controller class groups a few actions (represented by public methods). However, in Hanami, you create one class per
action. So an action is a standalone unit of code that can be tested in isolation and quite clearly states
what it does.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s have a look at an example action class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Accounts::Actions::UpdatePassword &amp;lt; Accounts::Action
  include Accounts::Deps[&amp;quot;commands.change_password&amp;quot;]
  before :require_authenticated

  params do
    required(:current_password).filled(:str?)
    required(:password).filled(:str?, min_size?: 8)
  end

  def handle(req, res)
    if !req.params.valid?
      res.status = 422
      res.body = req.params.errors.to_h.join(&amp;quot;, &amp;quot;)
    elsif change_password.call(
        req.session[:current_user],
        req.params[:current_password],
        req.params[:password])
      res.status = 201
      res.body = &amp;quot;password updated&amp;quot;
    else
      res.status = 422
      res.body = &amp;quot;cannot update password&amp;quot;
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Here we have all the information in one place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The action depends on a &lt;code&gt;change_password&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;It requires an authenticated user context&lt;/li&gt;
&lt;li&gt;It needs two parameters to be passed (and one of them must be at least 8 characters long)&lt;/li&gt;
&lt;li&gt;Finally, we have a &lt;code&gt;handle&lt;/code&gt; method that holds the core logic of the endpoint: checking the validity of the params, calling a
dependency, and setting an appropriate response&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might be wondering what this syntax means:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;include Accounts::Deps[&amp;quot;commands.change_password&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This brings us to our next section about dependencies.&lt;/p&gt;
&lt;h2&gt;Dependencies&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Accounts::Deps&lt;/code&gt; is a &lt;strong&gt;dependency mixin&lt;/strong&gt; for the &lt;code&gt;accounts&lt;/code&gt; slice. Hanami makes a dependency between different classes
a first-class citizen.&lt;/p&gt;
&lt;p&gt;Instead of having dependencies hidden in the code, you can (and should, if you want to follow Hanami
philosophy) define them on top of the class. Not only does it clarify what you rely on, but it also makes the class easier
to test.&lt;/p&gt;
&lt;p&gt;In the example above, the &amp;quot;change password&amp;quot; command is defined in &lt;code&gt;slices/accounts/commands/change_password.rb&lt;/code&gt;
as an &lt;code&gt;Accounts::Commands::ChangePassword&lt;/code&gt; class. The dependency mixin will make an instance of it available as
&lt;code&gt;change_password&lt;/code&gt; in the class where you use it. This is all based on
&lt;a href=&quot;https://dry-rb.org/gems/dry-system/1.0/dependency-auto-injection/&quot;&gt;dry-system&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In tests, you can stub the dependency easily using a built-in &lt;code&gt;stub&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Accounts::Container.stub(&amp;quot;commands.change_password&amp;quot;, FakePasswordChanger.new)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If, for example, your original command uses a slow-by-design hashing algorithm (such as bcrypt), you can swap it
with something faster in tests.&lt;/p&gt;
&lt;h2&gt;Great Performance in Hanami&lt;/h2&gt;
&lt;p&gt;Speaking of performance, Hanami is &lt;em&gt;fast&lt;/em&gt;. It boots really fast because providers are only booted
when needed, not eagerly at the application start. As a result, a &lt;a href=&quot;https://rubygems.org/gems/hanami-reloader/versions/0.2.1&quot;&gt;&lt;code&gt;hanami-reloader&lt;/code&gt; gem&lt;/a&gt; (used to
reload an application after changes are made to the code in development) doesn&amp;#39;t need a sophisticated and
fragile reloading mechanism. Instead, it just reboots the whole app in milliseconds. Simple yet effective.&lt;/p&gt;
&lt;p&gt;But there&amp;#39;s more on this front. Hanami 2.0 includes a brand-new router, which makes it
&lt;a href=&quot;https://web-frameworks-benchmark.netlify.app/result?asc=0&amp;l=ruby&amp;order_by=level64&quot;&gt;faster than Sinatra, Grape, and Rails in benchmarks&lt;/a&gt;.
That might not be the most important indicator for a database-heavy web application, but it&amp;#39;s worth noting.&lt;/p&gt;
&lt;p&gt;Hanami actions themselves can be very speedy as well. As an anecdote, a few days before the final release, Peter Solnica
from the core team &lt;a href=&quot;https://ruby.social/@solnic/109358993216946670&quot;&gt;tweaked a logger&lt;/a&gt; to display the time it takes
to process a request in microseconds because he was getting sub-millisecond times.&lt;/p&gt;
&lt;h2&gt;What Isn&amp;#39;t in Hanami 2 (Yet)?&lt;/h2&gt;
&lt;p&gt;Before you start to rewrite your main application in Hanami, you should keep a few things in mind.&lt;/p&gt;
&lt;p&gt;Version 2.0 is a bit stripped-down. As of today, the framework does not have a default view
and database layer. However, people report that adding the former is easy with the &lt;a href=&quot;https://rubygems.org/gems/hanami-view&quot;&gt;&lt;code&gt;hanami-view&lt;/code&gt; gem&lt;/a&gt;, which is almost ready.
The persistence layer can be &lt;a href=&quot;https://guides.hanamirb.org/v2.0/introduction/getting-started/#persisting-books&quot;&gt;implemented using ROM.rb&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Official support for the missing pieces is planned in the Hanami 2.1 release.&lt;/p&gt;
&lt;h2&gt;My Personal Experience with Hanami 2&lt;/h2&gt;
&lt;p&gt;I have had a test Hanami application since the beta 1 release. It is a plain old server-rendered discussion forum - think something like &lt;a href=&quot;https://www.phpbb.com/&quot;&gt;phpBB&lt;/a&gt;. The app checks how much effort it takes to add common features to a Hanami application, such as
user authentication, file uploads, etc.&lt;/p&gt;
&lt;p&gt;My experience so far has been positive. Pre-2.0, I reported some issues to the maintainers. They were super helpful and the issues were quickly fixed.&lt;/p&gt;
&lt;p&gt;In general, even though the Hanami ecosystem lacks any &amp;quot;plug-and-play&amp;quot; solutions such as &lt;a href=&quot;https://rubygems.org/gems/devise/versions/4.8.1&quot;&gt;Devise&lt;/a&gt;, you can use many
existing libraries not tightly coupled to Ruby on Rails. For authentication, you can use
&lt;a href=&quot;https://github.com/wardencommunity/warden&quot;&gt;Warden&lt;/a&gt;, &lt;a href=&quot;https://github.com/omniauth/omniauth&quot;&gt;OmniAuth&lt;/a&gt; or
&lt;a href=&quot;https://github.com/jeremyevans/rodauth&quot;&gt;Rodauth&lt;/a&gt;. For uploads there is &lt;a href=&quot;https://shrinerb.com/&quot;&gt;Shrine&lt;/a&gt;. The pagination
&lt;a href=&quot;https://hanamimastery.com/episodes/32-rom-pagination&quot;&gt;is built into ROM&lt;/a&gt;. Integration with &lt;a href=&quot;https://ruby.social/@solnic/109397645329387276&quot;&gt;exception catchers such as
Rollbar&lt;/a&gt; is easy.&lt;/p&gt;
&lt;p&gt;So far, I haven&amp;#39;t hit any obstacles that would block me from progressing.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;I am excited about the future and waiting to see what Hanami 2.1 will bring in terms of views and persistence.&lt;/p&gt;
&lt;p&gt;The Ruby
community hasn&amp;#39;t really had more than one feature-complete full-stack web framework since Merb, so it is really
refreshing to see a change in that area.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>System Notifications with Noticed and CableReady in Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/11/23/system-notifications-with-noticed-and-cableready-in-rails.html"/>
    <id>https://blog.appsignal.com/2022/11/23/system-notifications-with-noticed-and-cableready-in-rails.html</id>
    <published>2022-11-23T00:00:00+00:00</published>
    <updated>2022-11-23T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover how to set up notifications for your Rails application using CableReady and Noticed.</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;This article has been partly inspired by an upcoming chapter of the author&amp;#39;s &lt;a href=&quot;https://store.minthesize.com/l/acr&quot;&gt;Advanced CableReady&lt;/a&gt; book, and tailored to fit this guest post for AppSignal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Notifications are a typical cross-cutting concern shared by many web applications.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/excid3/noticed&quot;&gt;Noticed gem&lt;/a&gt; makes developing notifications fantastically easy by providing a database-backed model and pluggable delivery methods for your Ruby on Rails application. It comes with built-in support for mailers, websockets, and &lt;a href=&quot;https://github.com/excid3/noticed#-delivery-methods&quot;&gt;a couple of other delivery methods&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll also examine the merits of using the &lt;a href=&quot;https://cableready.stimulusreflex.com/&quot;&gt;CableReady gem&lt;/a&gt; for triggering system notifications in your Ruby on Rails application.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get into it!&lt;/p&gt;
&lt;h2&gt;Prerequisites and Requirements&lt;/h2&gt;
&lt;p&gt;Every now and then, you might need to trigger system notifications from your app. You can achieve this with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API&quot;&gt;Notifications API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, let&amp;#39;s say your application allows users to upload large files which take a long time to be transcoded. You may want to send users a notification once their video upload completes. This means they can switch to a new task in the meantime and don&amp;#39;t have to keep your application open for several minutes.&lt;/p&gt;
&lt;p&gt;Luckily, it&amp;#39;s easy to implement and flesh out system notifications with these two prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Noticed has support for &lt;a href=&quot;https://github.com/excid3/noticed#-custom-delivery-methods&quot;&gt;custom delivery methods&lt;/a&gt; (Noticed exposes a simple API to implement any transport mechanism you like — for example, posting to Discord servers).&lt;/li&gt;
&lt;li&gt;CableReady has a &lt;a href=&quot;https://cableready.stimulusreflex.com/reference/operations/notifications#notification&quot;&gt;notification operation&lt;/a&gt; arrow in its quiver.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The list of requirements for CableReady and Noticed is short — you simply need the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an ActionCable server running&lt;/li&gt;
&lt;li&gt;an ActiveJob backend (typically used by Noticed)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note: The entire sample code for this project is available on &lt;a href=&quot;https://github.com/julianrubisch/noticed-cableready&quot;&gt;GitHub&lt;/a&gt;. You can also follow along below — we will guide you step-by-step.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;A CableReady Primer for your Ruby on Rails Application&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s start by making ourselves familiar with CableReady. Released in 2017, it can be thought of as the &lt;em&gt;&amp;quot;missing ActionCable standard library&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Before the advent of Turbo, the only way to craft real-time applications in Ruby on Rails was through &lt;a href=&quot;https://guides.rubyonrails.org/action_cable_overview.html&quot;&gt;ActionCable&lt;/a&gt;. ActionCable is the native Rails wrapper around WebSockets, providing both a server-side and client-side API to send messages through a persistent connection in both directions at any time.&lt;/p&gt;
&lt;p&gt;The downside to this approach was (and is) that you have to write a lot of boilerplate code to make it happen.&lt;/p&gt;
&lt;p&gt;This is where CableReady helps by providing an abstraction layer around a multitude of &lt;a href=&quot;https://cableready.stimulusreflex.com/reference/operations&quot;&gt;DOM operations&lt;/a&gt; to be triggered on the server. A few examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DOM mutations (&lt;code&gt;inner_html&lt;/code&gt;, &lt;code&gt;insert_adjacent_html&lt;/code&gt;, &lt;code&gt;morph&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;DOM element property mutations (&lt;code&gt;add_css_class&lt;/code&gt;, &lt;code&gt;remove_css_class&lt;/code&gt;, &lt;code&gt;set_dataset_property&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;dispatching arbitrary DOM events&lt;/li&gt;
&lt;li&gt;browser history manipulations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;notifications&lt;/strong&gt; (which we will make use of)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The CableReady Server and Client&lt;/h3&gt;
&lt;p&gt;How does CableReady work its magic? In a nutshell, it consists of a server-side and client-side part.&lt;/p&gt;
&lt;p&gt;On the server side, the module &lt;code&gt;CableReady::Broadcaster&lt;/code&gt; can be included in any part of your application that calls for it. This could be in a job, a model callback, or a plain controller. But first, an ActionCable channel has to be in place. To cite &lt;a href=&quot;https://cableready.stimulusreflex.com/setup&quot;&gt;CableReady&amp;#39;s official documentation&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class ExampleChannel &amp;lt; ApplicationCable::Channel
  def subscribed
    stream_from &amp;quot;visitors&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that &lt;code&gt;visitors&lt;/code&gt; is called a &lt;strong&gt;stream identifier&lt;/strong&gt;. It can be used to target either a broad audience or only specific clients subscribed to your channel. To conclude the example, we can include the broadcaster module in a model and send a &lt;code&gt;console.log&lt;/code&gt; to the client after sign-up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class User &amp;lt; ApplicationRecord
  include CableReady::Broadcaster

  after_create do
    cable_ready[&amp;quot;visitors&amp;quot;].console_log(message: &amp;quot;Welcome #{self.name} to the site!&amp;quot;)
    cable_ready.broadcast # send queued console_log operation to all ExampleChannel subscribers
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the client side, the logic is simple. Create a subscription to the aforementioned channel. Then, in its received hook, call &lt;code&gt;CableReady.perform&lt;/code&gt; on all operations passed over the wire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import CableReady from &amp;quot;cable_ready&amp;quot;;
import consumer from &amp;quot;./consumer&amp;quot;;

consumer.subscriptions.create(&amp;quot;ExampleChannel&amp;quot;, {
  received(data) {
    if (data.cableReady) CableReady.perform(data.operations);
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;CableReady vs. Turbo for Rails&lt;/h2&gt;
&lt;p&gt;Summing up, when should you use CableReady, and when should you avoid it?&lt;/p&gt;
&lt;p&gt;With the introduction of &lt;a href=&quot;https://turbo.hotwired.dev/&quot;&gt;Turbo&lt;/a&gt;, the web development community received a powerful toolbox to craft server-rendered reactive applications. Being essentially a frontend technology with powerful server-side bindings for Rails, it fits well into the standard Model-View-Controller (MVC) stack. Thus, it can cover most of your typical app&amp;#39;s requirements.&lt;/p&gt;
&lt;p&gt;CableReady, on the other hand, is the Swiss army knife of real-time Rails development and should be used with care. It is a powerful abstraction that can seem very inviting to use pervasively. But if you imagine that every part of your DOM can be mutated from any location in your app, you&amp;#39;ll understand that this can lead to race conditions and hard-to-track-down bugs.&lt;/p&gt;
&lt;p&gt;There are cases like the one at hand, though, where CableReady makes perfect sense because it allows for more fine-grained control over the DOM.&lt;/p&gt;
&lt;p&gt;Asked for a simple TLDR, I would respond that Turbo is for application developers, while CableReady is for library builders. But as we will see, there are gray areas between the two.&lt;/p&gt;
&lt;h2&gt;Noticed — Simple Notifications for Ruby on Rails Applications&lt;/h2&gt;
&lt;p&gt;The second library we will apply to deliver system notifications is Chris Oliver&amp;#39;s &lt;a href=&quot;https://github.com/excid3/noticed&quot;&gt;Noticed gem&lt;/a&gt;. At its heart, it is built upon an ActiveRecord model that models &lt;em&gt;a single notification to a recipient&lt;/em&gt;. It holds common metadata, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;who a notification was sent to (the recipient)&lt;/li&gt;
&lt;li&gt;when the notification was read&lt;/li&gt;
&lt;li&gt;any parameters the notification is associated with (typically a reference to another model)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are familiar with how the ActiveStorage/ActionText meta-tables work, this is very similar.&lt;/p&gt;
&lt;p&gt;Adjacent to this, Noticed employs POROs (&lt;em&gt;Plain Old Ruby Objects&lt;/em&gt;, i.e., objects without any connection to Rails or other frameworks), which serve as blueprints for actual notifications. These are, somewhat misleadingly, also called Notifications and carry logic about how to render and distribute them. Here is an example from the README:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class CommentNotification &amp;lt; Noticed::Base
  deliver_by :database
  deliver_by :action_cable
  deliver_by :email, mailer: &amp;#39;CommentMailer&amp;#39;, if: :email_notifications?

  # I18n helpers
  def message
    t(&amp;quot;.message&amp;quot;)
  end

  # URL helpers are accessible in notifications
  # Don&amp;#39;t forget to set your default_url_options so Rails knows how to generate urls
  def url
    post_path(params[:post])
  end

  def email_notifications?
    !!recipient.preferences[:email]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will see this at work shortly. Of special interest are the &lt;code&gt;deliver_by&lt;/code&gt; invocations, as they determine which delivery methods this notification should use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;deliver_by :database&lt;/code&gt; stores a Notification record (of the model mentioned above) for later access&lt;/li&gt;
&lt;li&gt;&lt;code&gt;deliver_by :action_cable&lt;/code&gt; sends it via a defined ActionCable channel and stream (default &lt;code&gt;Noticed::NotificationChannel&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;deliver_by :email&lt;/code&gt; specifies a mailer used to send the notification. The example displays how to factor in any preferences the recipient(s) might have set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our goal for the remainder of this article is to implement a custom delivery method that will send our system notifications.&lt;/p&gt;
&lt;h2&gt;A Custom Delivery Method: System Notifications via the Notifications API&lt;/h2&gt;
&lt;p&gt;Before we set out to do this, let&amp;#39;s clear the ground by creating a new Rails application. Because integrating with CableReady is easier this way, I opted for the &lt;code&gt;esbuild&lt;/code&gt; JavaScript option over &lt;code&gt;importmaps&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails new noticed-cableready --javascript=esbuild
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; At the time of writing, the current Rails version is 7.0.4.
If you have an existing Rails application, you can skip the next step, but make sure you have a User model or similar concept to act as a notification &lt;em&gt;recipient&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;1. Prepare Recipients&lt;/h3&gt;
&lt;p&gt;Noticed needs a User model to act as recipients, so to be concise, pull in &lt;a href=&quot;https://github.com/heartcombo/devise&quot;&gt;Devise&lt;/a&gt; and generate a User model.&lt;/p&gt;
&lt;p&gt;Afterward, open the Rails console and create a sample user:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add devise
$ bin/rails generate devise:install
$ bin/rails generate devise User
$ bin/rails db:migrate

$ bin/rails c
irb(main):001:1* User.create(
irb(main):002:1*   email: &amp;quot;julian@example.com&amp;quot;,
irb(main):003:1*   password: &amp;quot;mypassword&amp;quot;,
irb(main):004:1*   password_confirmation: &amp;quot;mypassword&amp;quot;
irb(main):005:1&amp;gt; )
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. Add Noticed&lt;/h3&gt;
&lt;p&gt;Next, let&amp;#39;s add Noticed to our bundle and generate the database model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add noticed
$ bin/rails generate noticed:model
    generate  model
       rails  generate model Notification recipient:references{polymorphic} type params:json read_at:datetime:index
      invoke  active_record
      create    db/migrate/20221026184101_create_notifications.rb
      create    app/models/notification.rb
      invoke    test_unit
      create      test/models/notification_test.rb
      create      test/fixtures/notifications.yml
      insert  app/models/notification.rb
      insert  db/migrate/20221026184101_create_notifications.rb

🚚 Your notifications database model has been generated!

Next steps:
1. Run &amp;quot;rails db:migrate&amp;quot;
2. Add &amp;quot;has_many :notifications, as: :recipient, dependent: :destroy&amp;quot; to your User model(s).
3. Generate notifications with &amp;quot;rails g noticed:notification&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We do as told, run &lt;code&gt;db:migrate&lt;/code&gt; and add the polymorphic &lt;code&gt;has_many&lt;/code&gt; association to our User model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/user.rb
class User &amp;lt; ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :notifications, as: :recipient, dependent: :destroy
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final piece before we can put this to the test is to create a blueprint PORO:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails generate noticed:notification TestNotification
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/notifications/test_notification.rb
class TestNotification &amp;lt; Noticed::Base
  deliver_by :database

  def message
    &amp;quot;A system notification&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We tweak it a bit so that it just uses the database delivery method and a placeholder message for the moment. You would typically add required params here, like a model id, to construct the message and the URL to link to — see the &lt;a href=&quot;https://github.com/excid3/noticed&quot;&gt;Noticed README&lt;/a&gt; for details.&lt;/p&gt;
&lt;p&gt;Using only the Rails console, we can demonstrate how to deliver a notification based on this PORO now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails c
irb(main):001:0&amp;gt; TestNotification.deliver(User.first)
  User Load (0.3ms)  SELECT &amp;quot;users&amp;quot;.* FROM &amp;quot;users&amp;quot; ORDER BY &amp;quot;users&amp;quot;.&amp;quot;id&amp;quot; ASC LIMIT ?  [[&amp;quot;LIMIT&amp;quot;, 1]]
Performing Noticed::DeliveryMethods::Database (Job ID: 32f27ac7-fb2e-42e4-9c5e-a3290d2d1297) from Async(default) enqueued at  with arguments: {:notification_class=&amp;gt;&amp;quot;TestNotification&amp;quot;, :options=&amp;gt;{}, :params=&amp;gt;{}, :recipient=&amp;gt;#&amp;lt;GlobalID:0x00000001111e1718 @uri=#&amp;lt;URI::GID gid://noticed-cableready/User/1&amp;gt;&amp;gt;, :record=&amp;gt;nil}
  TRANSACTION (0.1ms)  begin transaction
  Notification Create (0.6ms)  INSERT INTO &amp;quot;notifications&amp;quot; (&amp;quot;recipient_type&amp;quot;, &amp;quot;recipient_id&amp;quot;, &amp;quot;type&amp;quot;, &amp;quot;params&amp;quot;, &amp;quot;read_at&amp;quot;, &amp;quot;created_at&amp;quot;, &amp;quot;updated_at&amp;quot;) VALUES (?, ?, ?, ?, ?, ?, ?)  [[&amp;quot;recipient_type&amp;quot;, &amp;quot;User&amp;quot;], [&amp;quot;recipient_id&amp;quot;, 1], [&amp;quot;type&amp;quot;, &amp;quot;TestNotification&amp;quot;], [&amp;quot;params&amp;quot;, &amp;quot;{\&amp;quot;_aj_symbol_keys\&amp;quot;:[]}&amp;quot;], [&amp;quot;read_at&amp;quot;, nil], [&amp;quot;created_at&amp;quot;, &amp;quot;2022-10-27 07:25:28.865982&amp;quot;], [&amp;quot;updated_at&amp;quot;, &amp;quot;2022-10-27 07:25:28.865982&amp;quot;]]
  TRANSACTION (0.3ms)  commit transaction
Performed Noticed::DeliveryMethods::Database (Job ID: 32f27ac7-fb2e-42e4-9c5e-a3290d2d1297) from Async(default) in 29.93ms
=&amp;gt; [#&amp;lt;User id: 1, email: &amp;quot;julian@example.com&amp;quot;, created_at: &amp;quot;2022-10-26 18:51:40.370635000 +0000&amp;quot;, updated_at: &amp;quot;2022-10-26 18:51:40.370635000 +0000&amp;quot;&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, Noticed performs a database insert, so now we can grab all &lt;code&gt;Notifications&lt;/code&gt; for a specified user:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;irb(main):002:0&amp;gt; User.first.notifications
  User Load (0.6ms)  SELECT &amp;quot;users&amp;quot;.* FROM &amp;quot;users&amp;quot; ORDER BY &amp;quot;users&amp;quot;.&amp;quot;id&amp;quot; ASC LIMIT ?  [[&amp;quot;LIMIT&amp;quot;, 1]]
  Notification Load (0.8ms)  SELECT &amp;quot;notifications&amp;quot;.* FROM &amp;quot;notifications&amp;quot; WHERE &amp;quot;notifications&amp;quot;.&amp;quot;recipient_type&amp;quot; = ? AND &amp;quot;notifications&amp;quot;.&amp;quot;recipient_id&amp;quot; = ?  [[&amp;quot;recipient_type&amp;quot;, &amp;quot;User&amp;quot;], [&amp;quot;recipient_id&amp;quot;, 1]]
=&amp;gt;
[#&amp;lt;Notification:0x00000001113e3b38
  id: 1,
  recipient_type: &amp;quot;User&amp;quot;,
  recipient_id: 1,
  type: &amp;quot;TestNotification&amp;quot;,
  params: {},
  read_at: nil,
  created_at: Thu, 27 Oct 2022 07:25:28.865982000 UTC +00:00,
  updated_at: Thu, 27 Oct 2022 07:25:28.865982000 UTC +00:00&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is enough for us to construct a simple index view that lists all delivered notifications for the current user:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g controller Notifications index
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/controllers/notifications_controller.rb
class NotificationsController &amp;lt; ApplicationController
  def index
    @notifications = current_user.notifications
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/notifications/index.html.erb --&amp;gt;
&amp;lt;h1&amp;gt;Notifications&amp;lt;/h1&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;% @notifications.each do |notification| %&amp;gt;
  &amp;lt;% instance = notification.to_notification %&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;p&amp;gt;Sent at: &amp;lt;%= notification&amp;amp;.created_at.to_s %&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;Message: &amp;lt;%= instance.message %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After spinning up the app with &lt;code&gt;bin/dev&lt;/code&gt;, we can log in and browse to &lt;a href=&quot;http://localhost:3000/notifications&quot;&gt;http://localhost:3000/notifications&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-11/noticed-notifications-index.png&quot; alt=&quot;List of Notifications&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;3. Installing CableReady&lt;/h3&gt;
&lt;p&gt;To make use of CableReady, we need to install it. Luckily, this is quickly done:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle add cable_ready
$ yarn add cable_ready@4.5.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s generate a &lt;code&gt;NotificationChannel&lt;/code&gt; to deliver our messages:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g channel Notification
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will add all the missing ActionCable (JavaScript) dependencies and scaffold the respective channel files, specifically &lt;code&gt;app/channels/notification_channel.rb&lt;/code&gt; and &lt;code&gt;app/javascript/channels/notification_channel.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the server-side channel, we inherit from &lt;code&gt;Noticed::NotificationChannel&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/channels/notification_channel.rb
class NotificationChannel &amp;lt; Noticed::NotificationChannel
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we continue, we need to ensure our ActionCable is authenticated for Devise users. I won&amp;#39;t go into details about that here. The necessary boilerplate looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module ApplicationCable
  class Connection &amp;lt; ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected

    def find_verified_user
      if (current_user = env[&amp;quot;warden&amp;quot;].user)
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.stimulusreflex.com/rtfm/authentication&quot;&gt;Check out the StimulusReflex docs&lt;/a&gt; for other options.&lt;/p&gt;
&lt;p&gt;On the client side, we need to add the tiny bit of setup code mentioned above:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/channels/notification_channel.js
import CableReady from &amp;quot;cable_ready&amp;quot;;
import consumer from &amp;quot;./consumer&amp;quot;;

consumer.subscriptions.create(&amp;quot;NotificationChannel&amp;quot;, {
  received(data) {
    if (data.cableReady) CableReady.perform(data.operations);
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Redis is required for ActionCable to work, so the rest of this article assumes you have a local server running.&lt;/p&gt;
&lt;h3&gt;4. Delivery Method Implementation&lt;/h3&gt;
&lt;p&gt;To broadcast system notifications, let&amp;#39;s generate a new delivery method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails generate noticed:delivery_method System
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scaffolded class looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/notifications/delivery_methods/system.rb
class DeliveryMethods::System &amp;lt; Noticed::DeliveryMethods::Base
  def deliver
    # Logic for sending the notification
  end

  # You may override this method to validate options for the delivery method
  # Invalid options should raise a ValidationError
  #
  # def self.validate!(options)
  #   raise ValidationError, &amp;quot;required_option missing&amp;quot; unless options[:required_option]
  # end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s continue by drafting how we envision our &lt;code&gt;deliver&lt;/code&gt; method to work.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  class DeliveryMethods::System &amp;lt; Noticed::DeliveryMethods::Base
+   include CableReady::Broadcaster
+
    def deliver
-     # Logic for sending the notification
+     cable_ready[channel].notification(
+       title: &amp;quot;My App&amp;quot;,
+       options: {
+         body: notification.message
+       }
+     ).broadcast_to(recipient)
    end

+   def channel
+     @channel ||= begin
+       value = options[:channel]
+       case value
+       when String
+         value.constantize
+       else
+         Noticed::NotificationChannel
+       end
+     end
+   end
-   # You may override this method to validate options for the delivery method
-   # Invalid options should raise a ValidationError
-   #
-   # def self.validate!(options)
-   #   raise ValidationError, &amp;quot;required_option missing&amp;quot; unless options[:required_option]
-   # end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have borrowed the &lt;code&gt;channel&lt;/code&gt; method in part from &lt;a href=&quot;https://github.com/excid3/noticed/blob/4a999c4ab4f2ea563d30f42c9e6a19984cb5f54f/lib/noticed/delivery_methods/action_cable.rb#L18&quot;&gt;the built-in ActionCable delivery method&lt;/a&gt;. It allows us to pass in a channel via a class method option. Otherwise, it falls back to the provided &lt;code&gt;Noticed::NotificationsChannel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then we use CableReady&amp;#39;s &lt;code&gt;notification&lt;/code&gt; method to broadcast the respective instance to the recipient.&lt;/p&gt;
&lt;p&gt;To put it into action, we have to connect it to our notification PORO:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  class TestNotification &amp;lt; Noticed::Base
    deliver_by :database
+   deliver_by :system, class: &amp;quot;DeliveryMethods::System&amp;quot;, channel: &amp;quot;NotificationChannel&amp;quot;

    def message
      &amp;quot;A system notification&amp;quot;
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. Putting It to Work&lt;/h3&gt;
&lt;p&gt;Now all that&amp;#39;s left to do is try it out. We can simply run this again from the Rails console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;irb(main):001:0&amp;gt; TestNotification.deliver(User.first)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming you are still logged in, the browser will first ask you for permission to receive notifications on behalf of the app:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-11/noticed-permissions.png&quot; alt=&quot;Browser Permission Popup&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Once you have confirmed this, you get this beautiful pop-up notification from your browser:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-11/noticed-notification.png&quot; alt=&quot;Browser Notification Popup&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Taking a fast lane tour through CableReady and Noticed, we have demonstrated how to integrate a native browser API into your app. The result is a simple, coherent way to deliver system notifications to your users.&lt;/p&gt;
&lt;p&gt;This use case is also meant to illustrate how easy it is to mix CableReady into your use case. If you think ahead one step, decoupling the drafted delivery method into a library is not hard.&lt;/p&gt;
&lt;p&gt;I hope this inspires you to look for manifestations of vertical reactive problem domains in your app and give CableReady a try.&lt;/p&gt;
&lt;p&gt;Read more by picking up my new book &lt;a href=&quot;https://store.minthesize.com/l/acr&quot;&gt;Advanced CableReady&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Use the coupon code APPSIGNAL-PROMO and save $10!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Scale Ruby on Rails Applications</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/11/09/how-to-scale-ruby-on-rails-applications.html"/>
    <id>https://blog.appsignal.com/2022/11/09/how-to-scale-ruby-on-rails-applications.html</id>
    <published>2022-11-09T00:00:00+00:00</published>
    <updated>2022-11-09T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Check out some ways to scale your Ruby on Rails applications, including with caching and background workers.</summary>
    <content type="html">&lt;p&gt;Today we will dive into some strategies you can use to scale Ruby on Rails applications to a huge user base.&lt;/p&gt;
&lt;p&gt;One obvious way of scaling applications is to throw more money at them. And it works amazingly well — add a few more servers, upgrade your database server, and voila, a lot of the performance issues just go &lt;em&gt;poof&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;But it is often also possible to scale applications without adding more servers. That&amp;#39;s what we will discuss today.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;Use AppSignal for your Rails Application&lt;/h2&gt;
&lt;p&gt;Before we dive into scaling and performance optimization, you first need to identify &lt;em&gt;if&lt;/em&gt; you need to do this, what the bottlenecks are in your application, and what resources can be scaled.&lt;/p&gt;
&lt;p&gt;One easy way to do this is to use &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;AppSignal&amp;#39;s performance monitoring and metrics for Ruby&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.appsignal.com/tour/performance&quot;&gt;performance dashboard&lt;/a&gt; helps you pinpoint the exact controller actions and background jobs that are slow on average.&lt;/p&gt;
&lt;p&gt;For example, here&amp;#39;s how a performance dashboard might look for ActiveRecord:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-11/activerecord.png&quot; alt=&quot;ActiveRecord dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This gives a good starting point to any scaling journey — whether you decide to add more servers or optimize performance through code.&lt;/p&gt;
&lt;p&gt;Now let&amp;#39;s move on to one of the simplest techniques you can use to scale your Rails app — caching.&lt;/p&gt;
&lt;h2&gt;Caching in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Caching allows you to stop computing the same things again and again.&lt;/p&gt;
&lt;p&gt;For example, let&amp;#39;s say you run a social media platform and there&amp;#39;s a very popular post. Caching can immediately help you regain all the CPU cycles you spend rendering that post for every user. And that&amp;#39;s only a part of what caching can help you do.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at all the possible resources that can be cached.&lt;/p&gt;
&lt;h3&gt;Caching Views&lt;/h3&gt;
&lt;p&gt;Rendering views can sometimes be an expensive operation, especially when that view has a lot of data to be rendered. Even when the operation isn&amp;#39;t expensive, using a pre-rendered view will give you a lot of performance as opposed to rendering that same view a million times.&lt;/p&gt;
&lt;p&gt;Rails supports this out of the box using the &lt;a href=&quot;https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache&quot;&gt;&lt;code&gt;cache&lt;/code&gt;&lt;/a&gt; view helper. For example, this is how it can cache each post when rendering a list:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;lt;% @posts.each do |post| %&amp;gt;
  &amp;lt;% cache post do %&amp;gt;
    &amp;lt;%= render post %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this, Rails automatically caches each post under a specific key that depends on the HTML content of the template, post id, and update timestamp.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;To read more about this technique, check out the posts &lt;a href=&quot;https://blog.appsignal.com/2018/03/20/fragment-caching-in-rails&quot;&gt;Fragment caching in Rails&lt;/a&gt; and &lt;a href=&quot;https://blog.appsignal.com/2018/08/14/rails-collection-caching&quot;&gt;Rails collection caching&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One thing to keep in mind, though, is that the cache keys don’t include nested template content. So if you are nesting the cache calls deeper than one level, there might be stale results. Read more about this in &lt;a href=&quot;https://blog.appsignal.com/2018/04/03/russian-doll-caching-in-rails&quot;&gt;Russian doll caching in Rails&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Caching Responses&lt;/h3&gt;
&lt;p&gt;In addition to caching views/fragments, you can also choose to cache the full response of &lt;code&gt;GET&lt;/code&gt; requests. This is supported through the &lt;code&gt;If-None-Match&lt;/code&gt; and &lt;code&gt;If-Modified-Since&lt;/code&gt; headers sent by the browsers.&lt;/p&gt;
&lt;p&gt;When an &lt;code&gt;If-None-Match&lt;/code&gt; header is present on the request, the server can return a &lt;code&gt;304 Not Modified&lt;/code&gt; response with no content if there are no changes to the response. The server-computed &lt;code&gt;Etag&lt;/code&gt; is compared with the value inside that header.&lt;/p&gt;
&lt;p&gt;Similarly, if the &lt;code&gt;If-Modified-Since&lt;/code&gt; header is present without an &lt;code&gt;If-None-Match&lt;/code&gt;, the server can return a &lt;code&gt;304 Not Modified&lt;/code&gt; response with no content (as long as the response hasn’t changed since that date).&lt;/p&gt;
&lt;p&gt;Rails provides easy ways to do this inside controller actions. You can simply write:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class PostsController &amp;lt; ApplicationController
  def show
    @post = Post.find(params[:id])
    fresh_when last_modified: @post.updated_at.utc, etag: @post
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails will send all the required headers to support caching, handle incoming headers, and respond with 304 when the data hasn’t changed. The server can skip rendering the full views again unless things change. You can read more about advanced configuration for this strategy in &lt;a href=&quot;https://blog.appsignal.com/2018/05/01/client-side-caching-in-rails-conditional-get-requests&quot;&gt;Client-side caching in Rails: conditional GET requests&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Caching Values&lt;/h3&gt;
&lt;p&gt;Finally, it is also possible to cache raw values (anything that can be serialized to the cache store). This is usually useful to cache the results of resource-intensive or slow operations and avoid performing them again.&lt;/p&gt;
&lt;p&gt;Identifying a value that can benefit from this caching depends greatly on the application, but usually, looking at your slowest events can help point you in the correct direction.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Finally, when you identify what to cache, the API that Rails provides for this is very simple to use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.cache.fetch(cache_key_with_version, expires_in: 12.hours) do
  perform_the_slow_computation
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code will &lt;code&gt;perform_the_slow_computation&lt;/code&gt; only once and then cache the value under the &lt;code&gt;cache_key_with_version&lt;/code&gt; key. The next time the same code is called, Rails will first check if we already have a cached value and use that instead of triggering &lt;code&gt;perform_the_slow_computation&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;The most important part of this caching strategy is to compute a good cache key that depends on all the inputs used in the value&amp;#39;s computation. This is to ensure we don’t keep using a stale value.&lt;/p&gt;
&lt;h3&gt;Cache Stores&lt;/h3&gt;
&lt;p&gt;Now that we know what to cache and the techniques Rails provides to store things in the cache, the next logical question is — where do we cache this data? Rails comes with several in-built cache store adapters. The most popular cache stores for production use cases are &lt;a href=&quot;https://redis.io/&quot;&gt;Redis&lt;/a&gt; and &lt;a href=&quot;https://memcached.org/&quot;&gt;Memcached&lt;/a&gt;. There are a couple of other options as well — the &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-filestore&quot;&gt;file store&lt;/a&gt; and  &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-memorystore&quot;&gt;memory store&lt;/a&gt;. A full discussion of these stores can be found in the post &lt;a href=&quot;https://blog.appsignal.com/2018/04/17/rails-built-in-cache-stores&quot;&gt;Rails&amp;#39; built-in cache stores: an overview&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;File and memory stores can be great for development use to get things up and running quickly. However, they are usually unsuitable for production, especially if you&amp;#39;re working in a distributed setup with multiple servers. Redis and memcached are both suited for production use. Which one you use usually depends on the application.&lt;/p&gt;
&lt;h2&gt;Background Workers in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Most applications need background jobs for mailers, regular clean-ups, or any other time-consuming operation that doesn&amp;#39;t require a user to be present. Chances are, you already have a background worker set up already.&lt;/p&gt;
&lt;p&gt;Whenever you find yourself doing anything that takes more than a second to do inside a controller action, see if you can move it to a background worker instead. This could range from a user-facing operation like searching for data inside a large table to an API method that ingests a large amount of data.&lt;/p&gt;
&lt;h3&gt;Example Implementation&lt;/h3&gt;
&lt;p&gt;To run custom jobs, Rails provides the &lt;a href=&quot;https://edgeguides.rubyonrails.org/active_job_basics.html&quot;&gt;Active Job framework&lt;/a&gt;. Let&amp;#39;s see how we can use it to move a very complex filtering logic to a background job. First, let’s create our background job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# jobs/filter_huge_dataset_job.rb
class FilterHugeDatasetJob &amp;lt; ApplicationJob
  queue_as :default

  def perform(user, filters)
    # search your data
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can run this job from the controller like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# controllers/huge_datasets_controller.rb
class HugeDatasetsController &amp;lt; ApplicationController
  def index
    FilterHugeDatasetJob.perform_later(@current_user, filters)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to render a loading indicator on our template while we wait for our job to compute data and deliver the results.&lt;/p&gt;
&lt;p&gt;But how can we get the results from our job to the view? Turbo makes this really easy. For example, inside the view, we can subscribe to turbo-stream events on a specific notification channel using &lt;code&gt;turbo_stream_from&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Using this, let’s write our templates:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# view/huge_datasets/index.html.erb
&amp;lt;%= render &amp;quot;index&amp;quot;, user: @current_user %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# view/huge_datasets/_index.html.erb
&amp;lt;%= turbo_stream_from user, :huge_datasets %&amp;gt;
&amp;lt;%= render &amp;quot;filters&amp;quot; %&amp;gt;
&amp;lt;div id=&amp;quot;data-container&amp;quot;&amp;gt;
  &amp;lt;% if defined? data %&amp;gt;
    &amp;lt;%= render partial: &amp;quot;item&amp;quot;, collection: data  %&amp;gt;
  &amp;lt;% else %&amp;gt;
    &amp;lt;%= render &amp;quot;loading&amp;quot; %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since data is not defined in the initial controller action, we will only render a loading indicator. Let’s now deliver the results from our job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# jobs/filter_huge_dataset_job.rb
class FilterHugeDatasetJob &amp;lt; ApplicationJob
  queue_as :default

  def perform(user, filters)
    data = search_huge_dataset(filters)
    notify_completed(user, data)
  end

  def search_huge_dataset(filters)
    # search your data
  end

  def notify_completed(user, data)
    Turbo::StreamsChannel.broadcast_replace_to(
      [user, :huge_datasets],
      target: &amp;quot;data-container&amp;quot;,
      partial: &amp;quot;huge_datasets/index&amp;quot;,
      locals: { user: user, data: data }
    )
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part here is the &lt;code&gt;notify_completed&lt;/code&gt; method. It uses &lt;a href=&quot;https://github.com/hotwired/turbo-rails/blob/main/app/channels/turbo/streams_channel.rb&quot;&gt;Turbo::StreamsChannel&lt;/a&gt;, broadcasting a replace event to the &lt;code&gt;[user, :huge_datasets]&lt;/code&gt; notification stream that we subscribed to from our view.&lt;/p&gt;
&lt;p&gt;That is everything we need to move complex operations from our controller to background jobs. The main advantage of moving tasks to the background is that background workers can be scaled independently of web servers. This frees up resources on the web server side considerably. For the user, such interfaces also feel much more responsive because we can respond quickly and deliver results incrementally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;If you need help deciding between a background job worker, read &lt;a href=&quot;https://blog.appsignal.com/2022/02/15/delayed-job-vs-sidekiq-which-is-better.html&quot;&gt;Delayed Job vs. Sidekiq: Which Is Better?&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Scaling a Database in Your Ruby on Rails Application&lt;/h2&gt;
&lt;p&gt;The last scalable resource that we will discuss in this post is the database. Databases form the core of most applications. As data and the number of servers accessing that data grows, databases start to feel the load.&lt;/p&gt;
&lt;p&gt;The easiest way to scale a database is to add more processing power and memory to the database server. As opposed to scaling web servers, doing this with a database is usually a very slow operation, especially if you have high storage.&lt;/p&gt;
&lt;p&gt;The second option to scale databases is to scale horizontally using multiple databases or by sharding your database. Check out &lt;a href=&quot;https://guides.rubyonrails.org/active_record_multiple_databases.html&quot;&gt;Multiple Databases with Active Record&lt;/a&gt; for more details about this.&lt;/p&gt;
&lt;p&gt;Instead, we&amp;#39;ll focus on optimizing your database&amp;#39;s performance by looking at PostgreSQL.&lt;/p&gt;
&lt;h3&gt;Find Time-Consuming Queries in PostgreSQL&lt;/h3&gt;
&lt;p&gt;First, we need to identify our most time-consuming queries. The way we can do that is to query the &lt;a href=&quot;https://www.postgresql.org/docs/current/pgstatstatements.html&quot;&gt;&lt;code&gt;pg_stat_statements&lt;/code&gt;&lt;/a&gt; table that contains statistics about all SQL statements executed on the server. Let’s see how we can find the top 100 queries with the highest run times:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT query, calls, (total_exec_time/calls)::integer as avg_time_ms
FROM pg_stat_statements
WHERE calls &amp;gt; 1000
ORDER BY avg_time_ms desc
LIMIT 100;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will return the query, the number of calls, and the average run time of these queries. Try to find the ones you think could be faster and analyze why they were slow.&lt;/p&gt;
&lt;p&gt;You can also run &lt;code&gt;EXPLAIN&lt;/code&gt; or &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; on the query to see the query plan and actual execution details, respectively.&lt;/p&gt;
&lt;p&gt;One of the most important things to look out for in the results is &lt;code&gt;Seq Scan&lt;/code&gt;, which indicates that Postgres has to go through all the records sequentially to run the query. If this happens, try to bypass that sequential scan by adding an index to the columns that you&amp;#39;ve filtered.&lt;/p&gt;
&lt;h3&gt;Tables with the Most Sequential Scans&lt;/h3&gt;
&lt;p&gt;Another useful query that I like to run is to find the total number of sequential scans run against a table:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT relname AS name, seq_scan as count
FROM pg_stat_user_tables
ORDER BY seq_scan DESC;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see a very large table (with a high number of rows) and a high count value from this result, then you have a problem. Try to check all queries against that table, find ones that can run sequential scans, and add indices to boil that down.&lt;/p&gt;
&lt;h3&gt;Index Usage&lt;/h3&gt;
&lt;p&gt;You can also find statistics about index usage by running this query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT relname,
   CASE idx_scan
     WHEN 0 THEN &amp;#39;Insufficient data&amp;#39;
     ELSE (100 * idx_scan / (seq_scan + idx_scan))::text
   END percent_of_times_index_used,
   n_live_tup rows_in_table
 FROM
   pg_stat_user_tables
 ORDER BY
   n_live_tup DESC;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns the percentage of index usage for each table. A low number means that you are missing some indexes on that table.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we explored several strategies to scale your Ruby on Rails applications, including caching and background workers. We also looked at optimizing your PostgreSQL database&amp;#39;s performance.&lt;/p&gt;
&lt;p&gt;Rails makes it really easy to add several layers of performance optimization to your application.&lt;/p&gt;
&lt;p&gt;The most important consideration with scalability is to identify bottlenecks in an application before we can act on them. A good performance monitoring tool can help. If you need one, check out &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;AppSignal for Ruby&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Build a Table Editor with Trix and Turbo Frames in Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/10/26/build-a-table-editor-with-trix-and-turbo-frames-in-rails.html"/>
    <id>https://blog.appsignal.com/2022/10/26/build-a-table-editor-with-trix-and-turbo-frames-in-rails.html</id>
    <published>2022-10-26T00:00:00+00:00</published>
    <updated>2022-10-26T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s build a table editor in a Rails application using ActionText&#039;s Trix editor, and use Turbo Frames to customize your table.</summary>
    <content type="html">&lt;p&gt;In this post, we will implement a basic ActionText table editor for your Rails application. We&amp;#39;ll learn how:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ActionText and Trix handle attachments&lt;/li&gt;
&lt;li&gt;To implement our own &lt;code&gt;Attachable&lt;/code&gt; type, and leverage this to build a basic table editor&lt;/li&gt;
&lt;li&gt;Turbo Frames can be used to edit the table&lt;/li&gt;
&lt;li&gt;Turbo helps and gets in the way at the same time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;This article draws inspiration from the excellent &lt;a href=&quot;https://onrails.blog/2020/09/30/adding-tables-to-actiontext-with-stimulus-js/&quot;&gt;&amp;#39;Adding Tables to ActionText With Stimulus.js&amp;#39; blog post&lt;/a&gt; from 2020. That was written before the advent of &lt;a href=&quot;https://turbo.hotwired.dev&quot;&gt;Turbo&lt;/a&gt; though, which we can expect to simplify matters quite a bit.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;ActionText Attachments in Rails 101&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: This demonstration assumes some understanding of Trix and Turbo Frames. You might find our &lt;a href=&quot;https://blog.appsignal.com/2022/07/06/get-started-with-hotwire-in-your-ruby-on-rails-app.html&quot;&gt;&amp;#39;Get Started with Hotwire in Your Ruby on Rails App&amp;#39; post&lt;/a&gt; helpful in learning the basics of Hotwire and Turbo Frames.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/julianrubisch/trix-tables-turbo-frames&quot;&gt;You can follow along with the code demonstration with this GitHub repo&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As described in the &lt;a href=&quot;https://guides.rubyonrails.org/action_text_overview.html&quot;&gt;ActionText documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Action Text brings rich text content and editing to Rails. It includes the Trix editor that handles everything from formatting to links to quotes to lists to embedded images and galleries.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At a high level, attachments are part of ActionText&amp;#39;s document model. They render custom templates for any resource resolvable by a Signed Global ID (SGID). In other words, ActionText stores a reference to a certain SGID as an &lt;code&gt;&amp;lt;action-text-attachment&amp;gt;&lt;/code&gt; element:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;action-text-attachment sgid=&amp;quot;BAh7CEkiCG…&amp;quot;&amp;gt;&amp;lt;/action-text-attachment&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whenever ActionText encounters such an element, it calls the &lt;code&gt;to_attachable_partial_path&lt;/code&gt; method on the respective resource. By default, this method delegates to &lt;code&gt;to_partial_path&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, as a preview, this is how our &lt;code&gt;Table&lt;/code&gt;&amp;#39;s representation in ActionText is going to look when rendered back to HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;action-text-attachment sgid=&amp;quot;...&amp;quot;&amp;gt;
  &amp;lt;table&amp;gt;
    &amp;lt;tbody&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;Cell 1&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;Cell 2&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;!-- more rows --&amp;gt;
    &amp;lt;/tbody&amp;gt;
  &amp;lt;/table&amp;gt;
&amp;lt;/action-text-attachment&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To conform with the ActionText Attachment API, a class has to do only two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Implement &lt;code&gt;to_sgid&lt;/code&gt; by including &lt;code&gt;GlobalID::Identification&lt;/code&gt;. By default, all &lt;code&gt;ActiveRecord::Base&lt;/code&gt; descendants already do this.&lt;/li&gt;
&lt;li&gt;Include the &lt;code&gt;ActionText::Attachable&lt;/code&gt; module.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;https://api.rubyonrails.org/classes/ActionText/Attachable.html&quot;&gt;The &lt;code&gt;ActionText::Attachable&lt;/code&gt; module&lt;/a&gt; offers canonical ways to convert any model to and from an SGID via the &lt;code&gt;attachable_sgid&lt;/code&gt; and &lt;code&gt;from_attachable_sgid&lt;/code&gt; methods. We will make use of this later on.&lt;/p&gt;
&lt;p&gt;It also provides convenience accessors for attachment metadata, such as file size and name, as well as content type.&lt;/p&gt;
&lt;p&gt;Finally, it provides the default locations for the partials used to render an attachment in the editor and rich text views.&lt;/p&gt;
&lt;h2&gt;Adding a Table Model&lt;/h2&gt;
&lt;p&gt;We will capitalize on ActionText&amp;#39;s Attachment API to implement our table solution. For this, we have to create a custom model capturing our tables&amp;#39; data and include &lt;code&gt;Attachable&lt;/code&gt;. We&amp;#39;ll use a simple JSON(B) column to hold a two-dimensional array for the table data.&lt;/p&gt;
&lt;p&gt;To start our exploration, let&amp;#39;s create a new Rails app with ActionText enabled:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails new trix-tables-turbo-frames
$ bin/rails action_text:install
$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because I&amp;#39;m not feeling creative today, let&amp;#39;s scaffold an &lt;code&gt;Article&lt;/code&gt; model with a title and rich text content:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g scaffold Article title:string content:rich_text
$ bin/rails g model ActionText::Table content:json
# or, if using postgres
$ bin/rails g model ActionText::Table content:jsonb

$ bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Watch out, here&amp;#39;s a surprising gotcha! The above install command created a &lt;code&gt;CreateActionTextTables&lt;/code&gt; migration, so we need to rename it to &lt;code&gt;CreateActionTextTablesTable&lt;/code&gt;. Additionally, we&amp;#39;ll have it default to a 2x2 table using &lt;code&gt;null: false, default: [[&amp;quot;&amp;quot;, &amp;quot;&amp;quot;], [&amp;quot;&amp;quot;, &amp;quot;&amp;quot;]]&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CreateActionTextTablesTable &amp;lt; ActiveRecord::Migration[7.0]
  def change
    create_table :action_text_tables do |t|
      t.json :content, null: false, default: [[&amp;quot;&amp;quot;, &amp;quot;&amp;quot;], [&amp;quot;&amp;quot;, &amp;quot;&amp;quot;]] # create a 2x2 table by default

      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Add a Table to a Rails ActionText Model&lt;/h3&gt;
&lt;p&gt;Before we continue with actually adding a table to rich text, we need to patch Trix&amp;#39;s toolbar:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  // app/javascript/application.js
  import &amp;quot;@hotwired/turbo-rails&amp;quot;;
  import &amp;quot;controllers&amp;quot;;
- import &amp;quot;trix&amp;quot;;
+ import Trix from &amp;quot;trix&amp;quot;;
  import &amp;quot;@rails/actiontext&amp;quot;;

+ const buttonHTML =
+   &amp;#39;&amp;lt;button type=&amp;quot;button&amp;quot;
+      class=&amp;quot;trix-button trix-button--icon trix-button--icon-table&amp;quot;
+      title=&amp;quot;table&amp;quot; tabindex=&amp;quot;-1&amp;quot;
+      data-action=&amp;quot;trix-table#attachTable&amp;quot;&amp;gt;table&amp;lt;/button&amp;gt;&amp;#39;;
+
+ const buttonGroupElement = document
+   .querySelector(&amp;quot;trix-editor&amp;quot;)
+   .toolbarElement.querySelector(&amp;quot;[data-trix-button-group=file-tools]&amp;quot;);
+
+ buttonGroupElement.insertAdjacentHTML(&amp;quot;beforeend&amp;quot;, buttonHTML);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we manually append a button to Trix&amp;#39;s &lt;code&gt;toolbarElement&lt;/code&gt;. Wiring this up to a &lt;code&gt;trix-table&lt;/code&gt; Stimulus controller (that we&amp;#39;ve yet to build) will insert a table into the document. Let&amp;#39;s give this button a nice SVG as content in CSS and set up some table styles while we&amp;#39;re at it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;/* app/assets/stylesheets/application.css */
/*
 *
 *= require_tree .
 *= require_self
 */

+  .trix-button--icon-table::before {
+    background-image: url(&amp;quot;data:image/svg+xml,%3Csvg xmlns=&amp;#39;http://www.w3.org/2000/svg&amp;#39; class=&amp;#39;h-6 w-6&amp;#39; fill=&amp;#39;none&amp;#39; viewBox=&amp;#39;0 0 24 24&amp;#39; stroke=&amp;#39;currentColor&amp;#39; stroke-width=&amp;#39;2&amp;#39;%3E%3Cpath stroke-linecap=&amp;#39;round&amp;#39; stroke-linejoin=&amp;#39;round&amp;#39; d=&amp;#39;M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z&amp;#39; /%3E%3C/svg%3E&amp;quot;);
+    top: 8%;
+    bottom: 4%;
+  }
+
+ table {
+   border: 1px solid black;
+   border-collapse: collapse;
+ }
+
+ td {
+   padding: 0.5rem!important;
+   border: 1px solid black;
+ }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we have successfully added it to the &amp;quot;file-tools&amp;quot; group:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/trix-toolbar.png&quot; alt=&quot;Customized Trix Toolbar&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now let&amp;#39;s return to adding and manipulating tables with the help of Turbo. For this, we will first need a controller with a &lt;code&gt;create&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/rails g controller Tables create --no-helper --skip-routes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This action can be more or less borrowed from the &lt;a href=&quot;https://onrails.blog/2020/09/30/adding-tables-to-actiontext-with-stimulus-js/&quot;&gt;&amp;#39;On Rails&amp;#39; blog post we cited in the introduction&lt;/a&gt;. It constructs the JSON necessary to insert an attachment on the client side: including an SGID and &lt;code&gt;content&lt;/code&gt; rendered from an &lt;code&gt;editor&lt;/code&gt; partial, as we shall see later.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/tables_controller.rb

class TablesController &amp;lt; ApplicationController
  layout false

  def create
    @table = ActionText::Table.create

    render json: {
      sgid: @table.attachable_sgid,
      content: render_to_string(partial: &amp;quot;tables/editor&amp;quot;, locals: {table: @table}, formats: [:html])
    }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We add the relevant &lt;strong&gt;resourceful table routes&lt;/strong&gt; to our configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # config/routes.rb

  Rails.application.routes.draw do
    resources :articles
+   resources :tables
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now comes the moment to plunge into the deep end: we need to build our table model. First, let&amp;#39;s include &lt;code&gt;ActionText::Attachable&lt;/code&gt; and define the relevant partial paths:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/action_text/table.rb

  class ActionText::Table &amp;lt; ApplicationRecord
+   include ActionText::Attachable
+
+   attribute :content_type, :string, default: &amp;quot;text/html&amp;quot;
+
+   def to_trix_content_attachment_partial_path
+     &amp;quot;tables/editor&amp;quot;
+   end
+
+   def to_partial_path
+     &amp;quot;tables/table&amp;quot;
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Note that we haven&amp;#39;t defined how the table&amp;#39;s content is stored yet. Because we declared it as a JSON(B) column in our database, we are free to choose any format. Deviating from the cited blog post a bit, let&amp;#39;s go with a two-dimensional array. Thus, we can simply do a nested loop over the &lt;code&gt;content&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/tables/_table.html.erb --&amp;gt;
&amp;lt;table&amp;gt;
  &amp;lt;% table.content.each do |row| %&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;% row.each do |column| %&amp;gt;
        &amp;lt;td&amp;gt;
          &amp;lt;%= column %&amp;gt;
        &amp;lt;/td&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above partial will render whenever it is requested by &lt;code&gt;ActionView&lt;/code&gt;, for example. Next, we also have to devise an &lt;code&gt;editor&lt;/code&gt; partial to be used inline in Trix:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/tables/_editor.html.erb --&amp;gt;
&amp;lt;%= turbo_frame_tag &amp;quot;table_#{table.attachable_sgid}&amp;quot; do %&amp;gt;
  &amp;lt;table&amp;gt;
    &amp;lt;% table.content.each_with_index do |row, row_index| %&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;% row.each_with_index do |column, column_index| %&amp;gt;
          &amp;lt;td&amp;gt;
            &amp;lt;div contenteditable&amp;gt;&amp;lt;%= column %&amp;gt;&amp;lt;/div&amp;gt;
          &amp;lt;/td&amp;gt;
        &amp;lt;% end %&amp;gt;
      &amp;lt;/tr&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/table&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only difference, as you have probably noticed, is that we now have wrapped it in a Turbo Frame, using the SGID as a DOM id. Furthermore, we provide row and column indexes to the separator blocks and prepare for inline editing by making the inner DIV &lt;code&gt;contenteditable&lt;/code&gt; — we&amp;#39;ll get to that later.&lt;/p&gt;
&lt;p&gt;We will now connect our toolbar&amp;#39;s table button to the server-side controller action we have just written. To do this, we first need to bring Rails&amp;#39; &lt;a href=&quot;https://github.com/rails/request.js&quot;&gt;request.js&lt;/a&gt; library into the project. This library will help us administer &lt;code&gt;post&lt;/code&gt; requests from the client, including proper CSRF-tokens, etc.:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bin/importmap pin @rails/request.js
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Build a New Trix Table Stimulus Controller&lt;/h3&gt;
&lt;p&gt;Now that everything is set up, let&amp;#39;s create a new &lt;strong&gt;trix-table&lt;/strong&gt; Stimulus controller. In it, we will implement the &lt;code&gt;attachTable&lt;/code&gt; action referenced by our toolbar button:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/trix_table_controller.js

import { Controller } from &amp;quot;@hotwired/stimulus&amp;quot;;
import Trix from &amp;quot;trix&amp;quot;;
import { post } from &amp;quot;@rails/request.js&amp;quot;;

export default class extends Controller {
  static values = {
    url: String,
  };

  async attachTable(event) {
    const response = await post(this.urlValue);

    if (response.ok) {
      const tableAttachment = await response.json;
      this.insertTable(tableAttachment);
    } else {
      // error handling
    }
  }

  insertTable(tableAttachment) {
    this.attachment = new Trix.Attachment(tableAttachment);
    this.element
      .querySelector(&amp;quot;trix-editor&amp;quot;)
      .editor.insertAttachment(this.attachment);
    this.element.focus();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will POST to the table&amp;#39;s &lt;code&gt;create&lt;/code&gt; route, inserting the JSON response as a &lt;strong&gt;Trix attachment&lt;/strong&gt;. This again borrows from the OnRails blog post, exchanging the deprecated &lt;strong&gt;rails-ujs&lt;/strong&gt; calls for the newer &lt;strong&gt;request.js&lt;/strong&gt; library.&lt;/p&gt;
&lt;p&gt;Now we have to actually make use of this controller in our app by adding it to the form&amp;#39;s markup:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/tables/_form.html.erb --&amp;gt;
- &amp;lt;%= form_with(model: article) do |form| %&amp;gt;
+ &amp;lt;%= form_with(model: article, data: {controller: &amp;quot;trix-table&amp;quot;, trix_table_url_value: tables_path}) do |form| %&amp;gt;
    &amp;lt;% if article.errors.any? %&amp;gt;
      &amp;lt;div style=&amp;quot;color: red&amp;quot;&amp;gt;
        &amp;lt;h2&amp;gt;&amp;lt;%= pluralize(article.errors.count, &amp;quot;error&amp;quot;) %&amp;gt; prohibited this article from being saved:&amp;lt;/h2&amp;gt;

        &amp;lt;ul&amp;gt;
          &amp;lt;% article.errors.each do |error| %&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;%= error.full_message %&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;% end %&amp;gt;
        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;% end %&amp;gt;

    &amp;lt;div&amp;gt;
      &amp;lt;%= form.label :title, style: &amp;quot;display: block&amp;quot; %&amp;gt;
      &amp;lt;%= form.text_field :title %&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div&amp;gt;
      &amp;lt;%= form.label :content, style: &amp;quot;display: block&amp;quot; %&amp;gt;
      &amp;lt;%= form.rich_text_area :content %&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div&amp;gt;
      &amp;lt;%= form.submit %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The beauty of Stimulus.js is that only adding two data attributes to the &lt;code&gt;form&lt;/code&gt; element achieves the desired result. We are now able to add tables to our article&amp;#39;s content with a single button click:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/insert-table.gif&quot; alt=&quot;Inserting a Table Attachment into a Trix Editor&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Manipulating the Table via Turbo Frames&lt;/h2&gt;
&lt;p&gt;Now that we can create table attachments, let&amp;#39;s shift our focus to manipulating the content. As it turns out, Turbo Frames are &lt;em&gt;almost&lt;/em&gt; a natural fit here.&lt;/p&gt;
&lt;h3&gt;Add and Delete Table Rows and Columns&lt;/h3&gt;
&lt;p&gt;To add and delete table rows and columns, we create a mini-toolbar consisting of four buttons, one for each operation. Make use of the &lt;code&gt;button_to&lt;/code&gt; helper and set the URL to the &lt;code&gt;update&lt;/code&gt; route for the respective table. Let&amp;#39;s add the respective operation we want to trigger as additional parameters:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/tables/_editor.html.erb --&amp;gt;
  &amp;lt;%= turbo_frame_tag &amp;quot;table_#{table.attachable_sgid}&amp;quot; do %&amp;gt;
+   &amp;lt;div style=&amp;quot;display: flex&amp;quot;&amp;gt;
+     &amp;lt;%= button_to &amp;quot;+ Row&amp;quot;, table_path(id: table.attachable_sgid), method: :patch, params: {operation: &amp;quot;addRow&amp;quot;} %&amp;gt;
+     &amp;lt;%= button_to &amp;quot;- Row&amp;quot;, table_path(id: table.attachable_sgid), method: :patch, params: {operation: &amp;quot;removeRow&amp;quot;} %&amp;gt;
+     &amp;lt;%= button_to &amp;quot;+ Column&amp;quot;, table_path(id: table.attachable_sgid), method: :patch, params: {operation: &amp;quot;addColumn&amp;quot;} %&amp;gt;
+     &amp;lt;%= button_to &amp;quot;- Column&amp;quot;, table_path(id: table.attachable_sgid), method: :patch, params: {operation: &amp;quot;removeColumn&amp;quot;} %&amp;gt;
+   &amp;lt;/div&amp;gt;
    &amp;lt;table&amp;gt;
      &amp;lt;% table.content.each_with_index do |row, row_index| %&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;% row.each_with_index do |column, column_index| %&amp;gt;
            &amp;lt;td&amp;gt;
              &amp;lt;div contenteditable&amp;gt;&amp;lt;%= column %&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;/td&amp;gt;
          &amp;lt;% end %&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;/table&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In turn, we also need to add the respective controller action(s) to our &lt;code&gt;TablesController&lt;/code&gt;. Observe that the &lt;code&gt;update&lt;/code&gt; action delegates those actions to the model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/controllers/tables_controller.rb

  class TablesController &amp;lt; ApplicationController
+   before_action :set_table, only: %i[show edit update destroy]

    layout false

+   def edit
+   end

    def create
      @table = ActionText::Table.create

      render json: {
        sgid: @table.attachable_sgid,
        content: render_to_string(partial: &amp;quot;tables/editor&amp;quot;, locals: {table: @table}, formats: [:html])
      }
    end

+   def update
+     if params[&amp;quot;operation&amp;quot;] == &amp;quot;addRow&amp;quot;
+       @table.add_row
+     elsif params[&amp;quot;operation&amp;quot;] == &amp;quot;removeRow&amp;quot;
+       @table.remove_row
+     elsif params[&amp;quot;operation&amp;quot;] == &amp;quot;addColumn&amp;quot;
+       @table.add_column
+     elsif params[&amp;quot;operation&amp;quot;] == &amp;quot;removeColumn&amp;quot;
+       @table.remove_column
+     else
+       flash.alert = &amp;quot;Unknown table operation: #{params[&amp;quot;operation&amp;quot;]}&amp;quot;
+     end
+
+     if @table.save
+       redirect_to edit_table_path(id: @table.attachable_sgid)
+     else
+       render :edit
+     end
+   end
+
+   private
+
+   def set_table
+     @table = ActionText::Attachable.from_attachable_sgid params[:id]
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After changes to the table&amp;#39;s structure are saved, we redirect to the table&amp;#39;s edit view. It renders the same &lt;code&gt;editor&lt;/code&gt; partial, which has the side-effect of referring to the same Turbo Frame. Thus Turbo can detect the matching frame and substitute one for the other.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/tables/edit.html.erb --&amp;gt;
&amp;lt;%= render &amp;quot;tables/editor&amp;quot;, table: @table %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have to implement the missing commands on the &lt;code&gt;Table&lt;/code&gt; model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/models/action_text/table.rb

  class ActionText::Table &amp;lt; ApplicationRecord
    include ActionText::Attachable

    attribute :content_type, :string, default: &amp;quot;text/html&amp;quot;

    def to_trix_content_attachment_partial_path
      &amp;quot;tables/editor&amp;quot;
    end

    def to_partial_path
      &amp;quot;tables/table&amp;quot;
    end

+   def rows
+     content.size
+   end
+
+   def columns
+     content.map(&amp;amp;:size).max
+   end
+
+   def add_row(index = rows - 1)
+     content &amp;lt;&amp;lt; Array.new(columns, &amp;quot;&amp;quot;)
+   end
+
+   def remove_row(index = rows - 1)
+     content.delete_at(index)
+   end
+
+   def add_column(index = columns - 1)
+     content.each do |row|
+       row &amp;lt;&amp;lt; &amp;quot;&amp;quot;
+     end
+   end
+
+   def remove_column(index = columns - 1)
+     content.each do |row|
+       row.delete_at(index)
+     end
+   end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notably, due to our simple data structure of a two-dimensional array, the &lt;code&gt;add/remove&amp;lt;sub&amp;gt;column&amp;lt;/sub&amp;gt;/row&lt;/code&gt; methods are mere proxies to modify the column and row count. Once that is in place, we can change our table&amp;#39;s structure with button clicks:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/add-remove-table-rows.gif&quot; alt=&quot;Adding and Removing Table Rows and Columns&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Edit the Content of Table Cells&lt;/h3&gt;
&lt;p&gt;In addition to changing the number of columns and rows, we also want to edit the cells&amp;#39; content. To achieve this, we will again lean heavily on the cited blog post and create a Stimulus table editor controller.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/table_editor_controller.js

import { Controller } from &amp;quot;@hotwired/stimulus&amp;quot;;
import { patch } from &amp;quot;@rails/request.js&amp;quot;;

export default class extends Controller {
  static values = {
    url: String,
  };

  async updateCell(event) {
    const response = await patch(this.urlValue, {
      body: { value: event.target.textContent },
      query: {
        operation: &amp;quot;updateCell&amp;quot;,
        row_index: event.target.dataset.rowIndex,
        column_index: event.target.dataset.columnIndex,
      },
      contentType: &amp;quot;application/json&amp;quot;,
      responseKind: &amp;quot;json&amp;quot;,
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;updateCell&lt;/code&gt; method will issue a PATCH request whenever a cell is edited, passing the row and column index as parameters. Now, all we have to do is connect it to our DOM:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  &amp;lt;!-- app/views/tables/_editor.html.erb --&amp;gt;
- &amp;lt;%= turbo_frame_tag &amp;quot;table_#{table.attachable_sgid}&amp;quot; do %&amp;gt;
+ &amp;lt;%= turbo_frame_tag &amp;quot;table_#{table.attachable_sgid}&amp;quot;,
+    data: {controller: &amp;quot;table-editor&amp;quot;, table_editor_url_value: table_path(id: table.attachable_sgid)} do %&amp;gt;
    &amp;lt;div style=&amp;quot;display: flex&amp;quot;&amp;gt;
      &amp;lt;%= button_to &amp;quot;+ Row&amp;quot;, table_path(id: table.attachable_sgid), method: :patch, params: {operation: &amp;quot;addRow&amp;quot;} %&amp;gt;
      &amp;lt;%= button_to &amp;quot;- Row&amp;quot;, table_path(id: table.attachable_sgid), method: :patch, params: {operation: &amp;quot;removeRow&amp;quot;} %&amp;gt;
      &amp;lt;%= button_to &amp;quot;+ Column&amp;quot;, table_path(id: table.attachable_sgid), method: :patch, params: {operation: &amp;quot;addColumn&amp;quot;} %&amp;gt;
      &amp;lt;%= button_to &amp;quot;- Column&amp;quot;, table_path(id: table.attachable_sgid), method: :patch, params: {operation: &amp;quot;removeColumn&amp;quot;} %&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;table&amp;gt;
      &amp;lt;% table.content.each_with_index do |row, row_index| %&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;% row.each_with_index do |column, column_index| %&amp;gt;
            &amp;lt;td&amp;gt;
-             &amp;lt;div contenteditable&amp;gt;&amp;lt;%= column %&amp;gt;&amp;lt;/div&amp;gt;
+             &amp;lt;div contenteditable
+                data-action=&amp;quot;input-&amp;gt;table-editor#updateCell&amp;quot;
+                data-row-index=&amp;quot;&amp;lt;%= row_index %&amp;gt;&amp;quot;
+                data-column-index=&amp;quot;&amp;lt;%= column_index %&amp;gt;&amp;quot;&amp;gt;
+               &amp;lt;%= column %&amp;gt;
+             &amp;lt;/div&amp;gt;
            &amp;lt;/td&amp;gt;
          &amp;lt;% end %&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;/table&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server-side &lt;code&gt;TablesController&lt;/code&gt;, of course, now needs a way to handle this operation. Luckily, this is easily done in our simplified proof of concept by adding another branch to our condition. We also make sure that the &lt;code&gt;update&lt;/code&gt; action can now handle JSON-type requests, even if it&amp;#39;s merely returning an empty object here.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  # app/controllers/tables_controller.rb

  class TablesController &amp;lt; ApplicationController
    before_action :set_table, only: %i[show edit update destroy]

    layout false

    def edit
    end

    def create
      @table = ActionText::Table.create

      render json: {
        sgid: @table.attachable_sgid,
        content: render_to_string(partial: &amp;quot;tables/editor&amp;quot;, locals: {table: @table}, formats: [:html])
      }
    end

    def update
      if params[&amp;quot;operation&amp;quot;] == &amp;quot;addRow&amp;quot;
        @table.add_row
      elsif params[&amp;quot;operation&amp;quot;] == &amp;quot;removeRow&amp;quot;
        @table.remove_row
      elsif params[&amp;quot;operation&amp;quot;] == &amp;quot;addColumn&amp;quot;
        @table.add_column
      elsif params[&amp;quot;operation&amp;quot;] == &amp;quot;removeColumn&amp;quot;
        @table.remove_column
+     elsif params[&amp;quot;operation&amp;quot;] == &amp;quot;updateCell&amp;quot;
+       @table.content[params[&amp;quot;row_index&amp;quot;].to_i][params[&amp;quot;column_index&amp;quot;].to_i] = params[&amp;quot;value&amp;quot;]
      end

      if @table.save
-       redirect_to edit_table_path(id: @table.attachable_sgid)
+       respond_to do |format|
+         format.html { redirect_to edit_table_path(id: @table.attachable_sgid) }
+         format.json {}
+       end
      else
        render :edit
      end
    end

    private

    def set_table
      @table = ActionText::Attachable.from_attachable_sgid params[:id]
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that in a production app, I would advise you to choose a different strategy for sanitizing the operation than an &lt;code&gt;if/elsif/else&lt;/code&gt; condition. I would probably reach for a Mediator or Proxy in this case.&lt;/p&gt;
&lt;h2&gt;The Limitations of Trix in Ruby&lt;/h2&gt;
&lt;p&gt;Up to this point, I assume this account has made perfect sense, but I have left out a critical detail. While we are persisting the underlying database model just fine, we are not syncing it to Trix&amp;#39;s internal shadow representation. That&amp;#39;s why the table snaps back to the previously stored representation when we focus out of it:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/document-sync-bug.gif&quot; alt=&quot;Bug When Syncing with Trix&amp;#39;s Internal Document Model&quot;/&gt;&lt;/p&gt;
&lt;p&gt;If we were to refresh the page now, the added content would appear, because Trix&amp;#39;s document is freshly initialized.&lt;/p&gt;
&lt;p&gt;I have pinned this problem down to where Trix syncs its internal document when the selection changes. It just unfurls it from the shadow element &lt;a href=&quot;https://github.com/basecamp/trix/blob/v2/src/trix/views/document_view.js#L52&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I tried hooking into the &lt;code&gt;turbo:submit&lt;/code&gt; event and preventing the sync just when blurring a table, but the solutions I came up with all seem very hairy and highly dependent on the internal API.&lt;/p&gt;
&lt;p&gt;The most Turbo-esque way of dealing with this, I guess, would be to wrap the whole form in an eager-loaded Turbo Frame and tell it to reload whenever Trix&amp;#39;s content changes.&lt;/p&gt;
&lt;p&gt;Something like this should do the trick:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/trix_table_controller.js

// ...

connect() {
  this.element.addEventListener(&amp;quot;turbo:submit-end&amp;quot;, (e) =&amp;gt; {
    this.element.closest(&amp;quot;turbo-frame&amp;quot;).reload();
  });
}

// ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you enclose your form in a Turbo Frame that you load from &lt;code&gt;src&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/articles/edit.html.erb --&amp;gt;
&amp;lt;h1&amp;gt;Editing article&amp;lt;/h1&amp;gt;

&amp;lt;%= turbo_frame_tag dom_id(@article, :form), src: form_article_path(@article) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach only works with already persisted base records, though.&lt;/p&gt;
&lt;h2&gt;Final Words of Warning on Trix&lt;/h2&gt;
&lt;p&gt;The proof of concept we&amp;#39;ve built uses server-rendered HTML to do away with the added complexity of serializing tables to JSON and listening for JavaScript events. It is portable to any ActionText installation and could be easily extracted to a gem.&lt;/p&gt;
&lt;p&gt;There are a couple of drawbacks, though, the most obvious one being the necessary re-syncing with Trix&amp;#39;s document model. There might be situations where the proposed workaround is workable and others where it&amp;#39;s a no-go. Until Trix gains a Turbo-compatible interface, there&amp;#39;s no way around it.&lt;/p&gt;
&lt;p&gt;The second catch is that it does not use Trix&amp;#39;s &lt;code&gt;undo&lt;/code&gt; functionality (but that is true of any Trix attachment). Likewise, it would be wise to wait for upstream changes instead of tweaking the internal API.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we started by taking a quick look at the basics of ActionText Attachments. We then added a table to an ActionText model before tweaking it using Turbo Frames. Finally, we touched on some limitations of using Trix.&lt;/p&gt;
&lt;p&gt;Given that Trix v2 is underway, featuring a translation from CoffeeScript to plain modern JavaScript, now would be a good time to address its Turbo compatibility. Currently, the scope of what such a wrapper might look like is beyond my capabilities, but it sure looks like a window of opportunity.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Improve Code in Your Ruby Application with RubyCritic</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/10/19/improve-code-in-your-ruby-application-with-rubycritic.html"/>
    <id>https://blog.appsignal.com/2022/10/19/improve-code-in-your-ruby-application-with-rubycritic.html</id>
    <published>2022-10-19T00:00:00+00:00</published>
    <updated>2022-10-19T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s take a look at RubyCritic and dig into RubyCritic&#039;s reports.</summary>
    <content type="html">&lt;p&gt;RubyCritic provides visual reports highlighting code smells, code structure, ease of testing, and test coverage in your Ruby application.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s in active development, with new code analysis tools often being introduced as new features. It&amp;#39;s well worth keeping track of RubyCritic&amp;#39;s releases.&lt;/p&gt;
&lt;p&gt;This article will touch on some of RubyCritic&amp;#39;s benefits, its dependencies, and how to read its code reports.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;Why Choose RubyCritic for Your Ruby on Rails Application?&lt;/h2&gt;
&lt;p&gt;You should consider using &lt;a href=&quot;https://github.com/whitesmith/rubycritic&quot;&gt;RubyCritic&lt;/a&gt; if you want a single place to review code improvements for your project. Including RubyCritic in your development process will certainly reduce the time a development team spends working on technical debts. Most technical debts will be mapped out at development time.&lt;/p&gt;
&lt;p&gt;Some benefits that RubyCritic can provide to your project and development process include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unified information in one place&lt;/li&gt;
&lt;li&gt;Visual reports&lt;/li&gt;
&lt;li&gt;Easy installation&lt;/li&gt;
&lt;li&gt;Zero configuration&lt;/li&gt;
&lt;li&gt;Customization allowed&lt;/li&gt;
&lt;li&gt;Being extensible — you can make your own open-source integration&lt;/li&gt;
&lt;li&gt;A badge generator 🎉&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To understand how RubyCritic works, let&amp;#39;s look at the internal dependencies it uses to make reports.&lt;/p&gt;
&lt;h2&gt;Internal Dependencies in RubyCritic&lt;/h2&gt;
&lt;p&gt;When you add RubyCritic to your project, some dependencies will also be included.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s highlight the dependencies that make magic happen: the Reek, Flay, and Flog gems. These dependencies allow RubyCritic to show you valuable information about your code. Understanding how they work also makes RubyCritic easier to use.&lt;/p&gt;
&lt;h3&gt;Reek: Detect Code Smells in Ruby&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/troessner/reek&quot;&gt;Reek&lt;/a&gt; is a gem for detecting code smells in Ruby. A bad smell in code is not about identifying wrong code, it is more about analyzing if the code could be written better.&lt;/p&gt;
&lt;p&gt;Reek&amp;#39;s analysis identifies &lt;em&gt;if&lt;/em&gt; something could be implemented in another way. It does not suggest &lt;em&gt;how&lt;/em&gt;, as most code smells are associated with business logic and a developer&amp;#39;s experience with a language.&lt;/p&gt;
&lt;p&gt;For example, you could easily rewrite an &lt;code&gt;if&lt;/code&gt; statement using metaprogramming techniques. The way to correct it, though, is up to a developer according to a project&amp;#39;s context. In this case, no library will be able to indicate the best solution.&lt;/p&gt;
&lt;p&gt;Reek detects an &lt;a href=&quot;https://github.com/troessner/reek/blob/master/docs/Code-Smells.md&quot;&gt;extensive list of smells&lt;/a&gt;. It examines and identifies possible smells in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Classes&lt;/li&gt;
&lt;li&gt;Attributes&lt;/li&gt;
&lt;li&gt;Methods&lt;/li&gt;
&lt;li&gt;Parameters&lt;/li&gt;
&lt;li&gt;Modules&lt;/li&gt;
&lt;li&gt;Iterators&lt;/li&gt;
&lt;li&gt;The implementation of polymorphism&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By finding smells, you can take steps to make your code more readable and maintainable.&lt;/p&gt;
&lt;p&gt;Reek allows for custom configuration to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Disable a detector by its type&lt;/li&gt;
&lt;li&gt;Exclude directories from being scanned&lt;/li&gt;
&lt;li&gt;Use filters to silence warnings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can even define specific code to &lt;a href=&quot;https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md&quot;&gt;suppress in a scan&lt;/a&gt;, a very useful feature when code is not yet finalized or refactored, or even if it is legacy code.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see a sample of how Reek works. In this code, the exception is just defined as &lt;code&gt;e&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/erp/orders_controller.rb

def create
  ...

  rescue JSON::Schema::ValidationError =&amp;gt; e
    render status: :unprocessable_entity, json: {
      type: &amp;quot;invalid-schema&amp;quot;,
      title: &amp;quot;Your request does not match the expected schema.&amp;quot;,
      detail: e.message
    }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is easy to imagine that &lt;code&gt;e&lt;/code&gt; means an exception, but what if we have other exceptions? Identifying them correctly is the best way to maintain good code.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Reek will identify &lt;code&gt;e&lt;/code&gt; as UncommunicativeVariableName and show a warning.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ reek app/controllers/erp/orders_controller.rb
Inspecting 1 file(s):
S

app/controllers/erp/orders_controller.rb -- 1 warning:

  [91]:UncommunicativeVariableName: Erp::OrdersController#create has the variable name &amp;#39;e&amp;#39; [https://github.com/troessner/reek/blob/v6.1.1/docs/Uncommunicative-Variable-Name.md]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Flay: Check for Ruby Code Duplication&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://ruby.sadi.st/Flay.html&quot;&gt;Flay&lt;/a&gt; identifies structural Ruby code similarities, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Detecting code duplication within a project&lt;/li&gt;
&lt;li&gt;Checking the difference at any code level&lt;/li&gt;
&lt;li&gt;Generating a score to measure how good your code is (the lower your score, the better the code)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If Flay reports a similarity in your code, it&amp;#39;s a high indicator that refactoring is needed. Don&amp;#39;t ignore this! Duplicate code is a gateway to bugs. If you fix something in one place but forget about another, more bugs appear.&lt;/p&gt;
&lt;p&gt;We can check how Flay works by running it in its own source code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ flay lib/flay.rb
Total score (lower is better) = 36

1) Similar code found in :iter (mass = 36)
  lib/flay.rb:80
  lib/flay.rb:105
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Flay identifies similarities between these two:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/flay.rb:80
opts.on(&amp;quot;-m&amp;quot;, &amp;quot;--mass MASS&amp;quot;, Integer, &amp;quot;Sets mass threshold (default = #{options[:mass]})&amp;quot;) do |m|
  options[:mass] = m.to_i
end

# lib/flay.rb:105
opts.on(&amp;quot;-t&amp;quot;, &amp;quot;--timeout TIME&amp;quot;, Integer, &amp;quot;Set the timeout. (default = #{options[:timeout]})&amp;quot;) do |t|
  options[:timeout] = t.to_i
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the code&amp;#39;s spelling is not exactly the same, but its functionality is and can be refactored to avoid duplication. That&amp;#39;s the magic of Flay!&lt;/p&gt;
&lt;h3&gt;Flog: Examine Your Code Complexity in Ruby&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://ruby.sadi.st/Flog.html&quot;&gt;Flog&lt;/a&gt; checks how difficult your code is to test. It sets a complexity score for each line of code and sums up the score for each method and class.&lt;/p&gt;
&lt;p&gt;The higher the score, the more your code needs to be refactored because it signifies that you have a highly complex implementation.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see Flog in action! A small change can cause your score to variate.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def validate_expiration
  return if exp_month.blank? || exp_year.blank?

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ flog app/models/credit_card.rb

5.2: CreditCard#validate_expiration   app/models/credit_card.rb:12-15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that in the first part of the code, we have an &lt;code&gt;or&lt;/code&gt; check that increases the score by 0.4 points.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def validate_expiration
  return if exp_month.blank?
  return if exp_year.blank?

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ flog app/models/credit_card.rb

4.8: CreditCard#validate_expiration app/models/credit_card.rb:12-15
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Other RubyCritic Dependencies&lt;/h3&gt;
&lt;p&gt;RubyCritic also uses other runtime dependencies, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/deivid-rodriguez/byebug&quot;&gt;&lt;code&gt;byebug&lt;/code&gt;&lt;/a&gt; - this elevates debugging Ruby applications. It allows you to run a program line by line, add breakpoints, and evaluate and track values at runtime. If you still use &lt;code&gt;puts&lt;/code&gt; for debugging, it&amp;#39;s time you get to know Byebug&amp;#39;s features and commands.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rubocop/rubocop&quot;&gt;&lt;code&gt;rubocop&lt;/code&gt;&lt;/a&gt; - a linter for Ruby code that helps you follow a style guide used by the Ruby community, or even apply your own code style. It&amp;#39;s very useful to set standards in your team and avoid silly conflicts about spaces and tabs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/simplecov-ruby/simplecov&quot;&gt;&lt;code&gt;SimpleCov&lt;/code&gt;&lt;/a&gt; - a tool to check Ruby application code coverage. You can configure it to run alongside your tests. It provides metrics on code coverage so that you can identify what you need to pay attention to and where to invest your time to create better test cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/whitesmith/rubycritic/blob/main/rubycritic.gemspec&quot;&gt;Dive into RubyCritic&amp;#39;s list of dependencies&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Using RubyCritic for Your Ruby on Rails App&lt;/h2&gt;
&lt;p&gt;RubyCritic has good documentation to help you get started without much configuration. Therefore, we will focus on utilizing its resources to help us analyze its reports.&lt;/p&gt;
&lt;p&gt;RubyCritic provides &amp;#39;Code&amp;#39;, &amp;#39;Smells&amp;#39;, and &amp;#39;Coverage&amp;#39; reports. We&amp;#39;ll look at each of these features in turn.&lt;/p&gt;
&lt;h3&gt;The Overview in RubyCritic&lt;/h3&gt;
&lt;p&gt;The &amp;#39;Overview&amp;#39; page shows a total score for your project on a donut chart, along with ratings (A being the best rating, F the worst). The &amp;#39;Summary&amp;#39; section shows the details of each rating, including the number of files, churns (commit changes), and smells found.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/overview.png&quot; alt=&quot;RubyCritic Overview&quot;/&gt;&lt;/p&gt;
&lt;p&gt;In the &amp;#39;Churn vs Complexity&amp;#39; section here, it is already possible to identify the class with the greatest complexity, which should probably be the first point of attention.&lt;/p&gt;
&lt;p&gt;To better understand this graph, it is worth recapping &lt;a href=&quot;https://www.oreilly.com/library/view/making-software/9780596808310/ch23s03.html&quot;&gt;code churn&lt;/a&gt;. Code that changes frequently can raise an alert that something is wrong — maybe in the logic or the business domain, for example. Either way, looking at &amp;#39;Churn vs Complexity&amp;#39; can help you see where the pain points are across your project.&lt;/p&gt;
&lt;h3&gt;Code Report&lt;/h3&gt;
&lt;p&gt;The &amp;#39;Code&amp;#39; report shows a score for each class, including indicators for churn, complexity, duplication, and smells.&lt;/p&gt;
&lt;p&gt;You can sort this list by any column to view the highest ranking factors and address the most critical issues first.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/code.png&quot; alt=&quot;RubyCritic Overview&quot;/&gt;&lt;/p&gt;
&lt;p&gt;In addition, this list has a filter that allows you to search by class name quickly.&lt;/p&gt;
&lt;p&gt;Clicking on the class name will open a detailed page with the class code and metrics such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code line&lt;/li&gt;
&lt;li&gt;Quantity methods&lt;/li&gt;
&lt;li&gt;Calculated churn&lt;/li&gt;
&lt;li&gt;Complexity by method&lt;/li&gt;
&lt;li&gt;Complexity score (total per class)&lt;/li&gt;
&lt;li&gt;Amount of duplicates found&lt;/li&gt;
&lt;li&gt;Number of smells&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The line of code where an issue is found will be highlighted (based on information provided by the Reek gem).&lt;/p&gt;
&lt;p&gt;If Flog identifies any issues, you&amp;#39;ll see a score. You&amp;#39;ll also see if a Flay report has detected any duplicate code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/reports.png&quot; alt=&quot;RubyCritic Overview&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Smells Report&lt;/h3&gt;
&lt;p&gt;The &amp;#39;Smells&amp;#39; page displays the smell type, the exact location where a smell appears, and the fix status.&lt;/p&gt;
&lt;p&gt;As mentioned earlier, the smells are detected by Reek, and sorting and filtering are also available on this page.&lt;/p&gt;
&lt;p&gt;Clicking on a class name will open a page with your code details. You can also see the classes grouped by smell type (this is missing from the &amp;#39;Code&amp;#39; page, which only displays the number of smells).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/smells.png&quot; alt=&quot;RubyCritic Overview&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Coverage Report&lt;/h3&gt;
&lt;p&gt;Finally, you can see class classifications and the percentage of coverage for each class in the &amp;#39;Coverage&amp;#39; report. In contrast to the lists on the &amp;#39;Code&amp;#39; and &amp;#39;Smells&amp;#39; reports, the list in &amp;#39;Coverage&amp;#39; does not allow information to be sorted and filtered.&lt;/p&gt;
&lt;p&gt;You can only see the percentage of code coverage — no additional information is available.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-10/coverage.png&quot; alt=&quot;RubyCritic Overview&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Integrating the SimpleCov report could add more value and usefulness to this page. But in any case, the &amp;#39;Coverage&amp;#39; report can help if you need a simple report to examine your project&amp;#39;s test coverage.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we briefly looked at the benefits of RubyCritic for your Ruby application before diving into its internal dependencies: Reek, Flay, and Flog. We then ran through how to read and analyze RubyCritic&amp;#39;s reports.&lt;/p&gt;
&lt;p&gt;As a next step, figure out how to use RubyCritic in your pipeline.&lt;/p&gt;
&lt;p&gt;Happy code refactoring!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Security Best Practices for Your Rails Application</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/10/05/security-best-practices-for-your-rails-application.html"/>
    <id>https://blog.appsignal.com/2022/10/05/security-best-practices-for-your-rails-application.html</id>
    <published>2022-10-05T00:00:00+00:00</published>
    <updated>2022-10-05T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Ensure your Rails application stays secure by following some best practices and habits.</summary>
    <content type="html">&lt;p&gt;Alongside performance and usability, you should always focus on security when creating any web application. Keep in mind that hacking techniques are constantly evolving, just as fast as technology is. So you must know how to secure your users and their data.&lt;/p&gt;
&lt;p&gt;This article will show you how to create a secure Rails application. The framework is known to be secure by default, but the default configuration is not enough to let you sleep well at night.&lt;/p&gt;
&lt;p&gt;We will share some best coding practices and a few habits in the development process that can help you create secure code.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive in!&lt;/p&gt;
&lt;h2&gt;Security in Your App: Best Coding Practices&lt;/h2&gt;
&lt;p&gt;Modern web applications are often complex. They utilize multiple data sources and handle custom authorization rules as well as different methods of authentication. Knowing how to avoid SQL injections or ensuring that users can only read their data is not enough.&lt;/p&gt;
&lt;p&gt;When you build a Rails application, you have to configure it properly, design the application securely, and write code that makes it bulletproof.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll look at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Application configuration&lt;/strong&gt; - The default configuration is exemplary, but we can make things even better with a few extra steps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Business logic&lt;/strong&gt; - Applications should be secure by design, not only by code. This principle is essential but is often ignored when an MVP has to be delivered quickly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code in controllers&lt;/strong&gt; - These classes are the entry point to our application, so an extra dose of attention is always needed to design a reliable application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code in models&lt;/strong&gt; - Many issues are related to a database, so it is essential to design and perform secure communication with the primary source of the data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code in views&lt;/strong&gt; - The point where we expose data to the browser is also a popular target for hackers, so we have to ensure that we don&amp;#39;t render anything that risks our users&amp;#39; data or privacy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Application Configuration&lt;/h3&gt;
&lt;p&gt;It all starts here, in the configuration files. In most cases, unless you restart the application, the rules will remain the same. Rails&amp;#39; creators made an effort to create secure defaults, but we can still improve them with some extra steps.&lt;/p&gt;
&lt;h3&gt;Force SSL&lt;/h3&gt;
&lt;p&gt;You can force your Rails application to always use a secure connection with the HTTPS protocol. To do this, open the &lt;code&gt;config/environments/production.rb&lt;/code&gt; file and set the following line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;config.force_ssl = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This setting does a few things to the application:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every time there is a request to the HTTP version of the application, Rails will redirect the request to the HTTPS protocol.&lt;/li&gt;
&lt;li&gt;It sets a secure flag on cookies. Thanks to this, browsers won&amp;#39;t send cookies with HTTP requests.&lt;/li&gt;
&lt;li&gt;It tells the browser to remember your application as TLS-only (TLS is Transport Layer Security, an extension of the HTTPS protocol).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CORS&lt;/h3&gt;
&lt;p&gt;Cross-Origin Resource Sharing (CORS) is a security mechanism that defines which website can interact with your application&amp;#39;s API. Of course, if you build a monolith app, you don&amp;#39;t need to care about this protection.&lt;/p&gt;
&lt;p&gt;If you build an API application, you can configure CORS by installing an additional gem called &lt;code&gt;rack-cors&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once you have done that, create a file called &lt;code&gt;cors.rb&lt;/code&gt; in the initializers directory &lt;code&gt;config/initializers&lt;/code&gt;. Define what endpoints a website can access (including request methods):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.config.middleware.insert_before 0, Rack::Cors do
 allow do
   origins &amp;#39;https://your-frontend.com&amp;#39;
   resource &amp;#39;/users&amp;#39;,
     :headers =&amp;gt; :any,
     :methods =&amp;gt; [:post]
   resource &amp;#39;/articles&amp;#39;,
     headers: :any,
     methods: [:get]
 end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example, we allow the your-frontend.com website to call the &lt;code&gt;/users&lt;/code&gt; endpoint (using only the POST method) and the &lt;code&gt;/articles&lt;/code&gt; endpoint (using only the GET method).&lt;/p&gt;
&lt;h3&gt;Secure Environment Variables&lt;/h3&gt;
&lt;p&gt;You should never hardcode your API keys, passwords, or other sensitive credentials in the source code. You might accidentally make them public or give someone who&amp;#39;s not authorized access to some sensitive application resources.&lt;/p&gt;
&lt;p&gt;The Rails framework itself provides a way to store credentials securely. However, the implementation varies depending on the framework version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rails 4&lt;/strong&gt; - The feature is called &amp;#39;secrets&amp;#39;. You store your sensitive information in a &lt;code&gt;config/secrets.yml&lt;/code&gt; file that is not tracked in the git repository.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rails 5&lt;/strong&gt; - The feature is called &amp;#39;credentials&amp;#39;. Your sensitive information is stored encrypted in &lt;code&gt;config/credentials.yml.enc&lt;/code&gt; — you can edit it with the &lt;code&gt;config/master.key&lt;/code&gt; file. So while the YAML configuration file can be stored in the repository because it’s encrypted, you don’t track the &lt;code&gt;master.key&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rails 6&lt;/strong&gt; - Also called &amp;#39;credentials&amp;#39;, you can store credentials per environment. Because of that, for every environment, you have the encrypted YAML file and key to decrypt it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Alternatively, you can always set the values on the server level, so that they only load in the server environment. Locally, each developer sets them individually.&lt;/p&gt;
&lt;h3&gt;Business Logic&lt;/h3&gt;
&lt;p&gt;You should think about security not only when you write code, but also when you design the processes in your application.&lt;/p&gt;
&lt;p&gt;Business logic is a set of rules that apply in the real world, and your goal is to map them in your code. Unfortunately, mapping them can cause weak points in your application and lead to security issues.&lt;/p&gt;
&lt;h3&gt;Strong Authentication and Authorization Rules&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s first explain the difference between authentication and authorization, as these terms are sometimes misinterpreted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt; - You validate a user&amp;#39;s login and password against an application&amp;#39;s database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization&lt;/strong&gt; - You validate the role of a signed-in user and, based on that, render different information for different users. For example, a user with an admin role can access a list of users in an application, while in most cases, the typical user can&amp;#39;t.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To improve the security level of your authentication, you can set high standards for your end-users:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Strong passwords&lt;/strong&gt; - Set a strict password policy that doesn&amp;#39;t allow for passwords that are too simple or weak. Of course, you can&amp;#39;t control if your users share their passwords, but you can make their passwords hard to guess.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Two-factor authentication&lt;/strong&gt; - Another layer of protection that will secure an account even if someone else knows the password.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encrypted passwords&lt;/strong&gt; - Never store passwords in your database as plain text. Then, even if your database is exposed, a hacker won&amp;#39;t be able to get your users&amp;#39; passwords.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Authorization Best Practices&lt;/h3&gt;
&lt;p&gt;If your application is complex, you probably need different roles for users to manage their data. As business logic grows, it may be hard to control everything without making a mistake that leads to an information leak.&lt;/p&gt;
&lt;p&gt;You can avoid issues by following some well-known good practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keep authorization logic in one place&lt;/strong&gt; - It&amp;#39;s hard to modify business logic correctly if you have to change multiple areas in the code. Storing the rules in one file makes it easy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set clear rules&lt;/strong&gt; - You won&amp;#39;t be able to tell whether your application is secure if you can&amp;#39;t validate the business logic against well-defined rules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set roles based on the group, not a single user&lt;/strong&gt; - It is easier to control the authorization process if you have groups of permissions instead of defining rules per user.&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Of course, good code reviews and well-written tests are also a must here to avoid introducing bugs to existing business logic.&lt;/p&gt;
&lt;h3&gt;Code in Controllers&lt;/h3&gt;
&lt;p&gt;Controllers are the first layer of MVC architecture and handle requests coming from users. Therefore, it is essential to filter incoming information correctly as it will be propagated on other application layers.&lt;/p&gt;
&lt;h3&gt;Filter Incoming Parameters&lt;/h3&gt;
&lt;p&gt;You should never pass a raw &lt;code&gt;params&lt;/code&gt; value to your application. Thanks to the strong parameters feature, it is easy to control the data that we&amp;#39;d like to pass along.&lt;/p&gt;
&lt;p&gt;Imagine that we have a &lt;code&gt;User&lt;/code&gt; model we want to update:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UsersController &amp;lt; ApplicationController
  def update
    current_user.update(params[:user])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Someone can manipulate the params, and it would be possible to update other &lt;code&gt;User&lt;/code&gt; model attributes (for example, the &lt;code&gt;admin&lt;/code&gt; flag if you have one). To avoid such a situation, filter params that you pass to your model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UsersController &amp;lt; ApplicationController
  def update
    current_user.update(user_params)
  end

  private

  def user_params
    params.require(:user).permit(:first_name, :last_name)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Use Scopes to Avoid Data Leaking&lt;/h3&gt;
&lt;p&gt;Usually, we don&amp;#39;t want to show the user data that does not belong to them. We may inadvertently expose some records for URL address manipulation due to the wrong code design. Let&amp;#39;s consider the following case:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MessagesController
  before_action :authenticate_user!

  def show
    @message = Message.find(params[:id])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything seems fine; the controller is protected from guests, and we assign the message object. However, a user just needs to change the id in the URL address to get a message that does not belong to them. This is a very dangerous situation.&lt;/p&gt;
&lt;p&gt;We can avoid it by using scopes within the given context. In the mentioned example, the current user is the scope:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MessagesController
  before_action :authenticate_user!

  def show
    @message = current_user.messages.find(params[:id])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Avoid Insecure URL Redirects&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s consider a straightforward example of a redirect based on the user input:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;redirect_to params[:url]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We should never do that, as we expose our user to a redirect that can take them everywhere. The simplest solution to prevent this security issue is to avoid using redirects with user input. If you can&amp;#39;t, you can always redirect to a path without the host:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;path = URI.parse(params[:url]).path.presence || &amp;quot;/&amp;quot;
redirect_to path
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Code in Models&lt;/h3&gt;
&lt;p&gt;Once Rails parses code in a controller, you will probably call models to receive the information from the database. Since models are responsible for communicating with your database as well as performing important computations, they are also often targeted by hackers.&lt;/p&gt;
&lt;h3&gt;Avoid SQL Injection&lt;/h3&gt;
&lt;p&gt;SQL injection is one of the most popular techniques used to access database information from the outside. The Rails framework tries to protect us from that threat, but we also need to write code that won&amp;#39;t let this happen.&lt;/p&gt;
&lt;p&gt;In general, we should avoid passing user input directly as a part of a query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.joins(params[:table]).order(&amp;#39;id DESC&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to, always validate the user input and assign a default value when the input is invalid:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;valid_tables = %w[articles posts messages]
table = valid_tables.include?(params[:table]) ? params[:table] : &amp;quot;articles&amp;quot;
User.joins(table).order(&amp;#39;id DESC&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://rails-sqli.org/&quot;&gt;Check out this extensive set of examples of what &lt;em&gt;not&lt;/em&gt; to do when it comes to SQL injection&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Prevent Command Injection&lt;/h3&gt;
&lt;p&gt;Avoid using methods that allow a program to execute any code. Such methods include &lt;code&gt;exec&lt;/code&gt;, &lt;code&gt;system&lt;/code&gt;, &lt;code&gt;`&lt;/code&gt;, and &lt;code&gt;eval&lt;/code&gt;. You should never pass user input to those functions.&lt;/p&gt;
&lt;p&gt;If your application allows users to execute code, you should run their code in separate Docker containers. In addition, you can remove dangerous methods before executing user input:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Kernel
 remove_method :exec
 remove_method :system
 remove_method :`
 remove_method :eval
 remove_method :open
end

Binding.send :remove_method, :eval
RubyVM::InstructionSequence.send :remove_method, :eval
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Avoid Unsafe Data Serialization&lt;/h3&gt;
&lt;p&gt;Insecure deserialization can lead to the execution of arbitrary code in your application. If you plan to serialize JSON:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;data = { &amp;quot;key&amp;quot; =&amp;gt; &amp;quot;value&amp;quot; }.to_json
# =&amp;gt; &amp;quot;{\&amp;quot;key\&amp;quot;:\&amp;quot;value\&amp;quot;}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of using &lt;code&gt;load&lt;/code&gt; or &lt;code&gt;restore&lt;/code&gt; methods, use &lt;code&gt;parse&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# bad
JSON.load(data)
JSON.restore(data)

# good
JSON.parse(data)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you plan to serialize YAML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;data = { &amp;quot;key&amp;quot; =&amp;gt; &amp;quot;value&amp;quot; }.to_yaml
# =&amp;gt; &amp;quot;---\nkey: value\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of using &lt;code&gt;load&lt;/code&gt; or &lt;code&gt;restore&lt;/code&gt; methods from the &lt;code&gt;Marshal&lt;/code&gt; module, use the &lt;code&gt;safe_load&lt;/code&gt; method from &lt;code&gt;Psych&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# bad
Marshal.load(data)
Marshal.restore(data)

# good
Psych.safe_load(data)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Code in Views&lt;/h3&gt;
&lt;p&gt;Whatever you render in views is visible to the end-user. When malicious code is rendered, it will affect your users directly by exposing them to untrusted websites and data sources.&lt;/p&gt;
&lt;h3&gt;Avoid CSS Injection&lt;/h3&gt;
&lt;p&gt;It would appear that CSS code is always secure; it only modifies the styles of a website. However, if you allow for user-defined styles in your application, there is always a risk of CSS code injection.&lt;/p&gt;
&lt;p&gt;One of the most popular cases of user-defined CSS is a custom page background:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;body style=&amp;quot;background: &amp;lt;%= profile.background_color %&amp;gt;;&amp;quot;&amp;gt;&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A user with bad intentions can input a URL that an application will automatically load, and do damage to the current user viewing the content. To prevent such situations, provide a predefined set of values instead of allowing users to type any value.&lt;/p&gt;
&lt;h3&gt;Sanitize Rendered Output&lt;/h3&gt;
&lt;p&gt;In the modern versions of Rails, output is sanitized by default. Even if a user inputs HTML or JS code and the application renders it, the HTML or JS code will escape.&lt;/p&gt;
&lt;p&gt;If you want to render HTML defined by users, always predefine which tags should render and which should escape:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;lt;%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above code, we allow users to render &lt;code&gt;strong&lt;/code&gt;, &lt;code&gt;em&lt;/code&gt;, and &lt;code&gt;a&lt;/code&gt; HTML elements in their comments.&lt;/p&gt;
&lt;h3&gt;Don&amp;#39;t Include Sensitive Data in Comments&lt;/h3&gt;
&lt;p&gt;This is primarily a reminder for junior developers unfamiliar with how web applications work on the front end. Don&amp;#39;t ever put sensitive information in comments (especially in views, as it will be exposed to end-users):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!--- password for the service is 1234 –&amp;gt;
&amp;lt;%= @some_service.result_of_search %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Habits to Keep Your Rails Application Secure&lt;/h2&gt;
&lt;p&gt;To make your Rails app secure and reduce technical debt, establish some valuable development habits. Taking small preventative actions frequently is much less painful than refactoring more significant portions of your codebase.&lt;/p&gt;
&lt;h3&gt;Upgrade Rails and Other Libraries Often&lt;/h3&gt;
&lt;p&gt;An upgrade is often painful and time-consuming, unless you upgrade to minor versions of a library. Develop a habit of upgrading a library each time a new, stable version is published. Your application will then be in good shape (not only regarding security, but also performance).&lt;/p&gt;
&lt;p&gt;If you use GitHub for day-to-day development, an extension like &lt;a href=&quot;https://github.com/marketplace/depfu&quot;&gt;Depfu&lt;/a&gt; is useful as it performs frequent updates for you.&lt;/p&gt;
&lt;h3&gt;Perform Security Audits&lt;/h3&gt;
&lt;p&gt;I don&amp;#39;t mean expensive audits by external companies. Often, it is enough to install a tool like &lt;a href=&quot;https://brakemanscanner.org/&quot;&gt;Brakeman&lt;/a&gt; and scan code with every commit or pull request.&lt;/p&gt;
&lt;p&gt;Also, it is a good idea to scan your Gemfile and find gems that need updating due to security issues discovered by the community. You can use &lt;a href=&quot;https://github.com/rubysec/bundler-audit&quot;&gt;bundler-audit&lt;/a&gt; to automate this process.&lt;/p&gt;
&lt;h3&gt;Have A Proper Logging Strategy&lt;/h3&gt;
&lt;p&gt;Logs, in many cases, are usually just thousands of lines of information you will never view. However, there might be a case where one of your users is attacked or experiences a suspicious action.&lt;/p&gt;
&lt;p&gt;In such a situation, if you have detailed and easily searchable logs, you can collect information that will help you prevent similar problems in the future.&lt;/p&gt;
&lt;h2&gt;Wrapping Up: Keep Your Rails App Secure&lt;/h2&gt;
&lt;p&gt;In this post, we ran through some security best practices for your Rails app that reduce the risk of a data breach.&lt;/p&gt;
&lt;p&gt;Making sure that your Rails application is secure might be challenging. Sticking to the framework&amp;#39;s defaults is not enough; you have to know how to avoid creating security issues. For example, various injections and remote code executions have been well-known issues for the past few years, but still, you can&amp;#39;t be 100% sure that your application won&amp;#39;t be affected.&lt;/p&gt;
&lt;p&gt;Don&amp;#39;t forget the importance of frequent upgrades, security audits, and a culture of good logging. When combined, all of these things will help to make your Rails application more secure.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Debugging in Ruby with AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/09/21/debugging-in-ruby-with-appsignal.html"/>
    <id>https://blog.appsignal.com/2022/09/21/debugging-in-ruby-with-appsignal.html</id>
    <published>2022-09-21T00:00:00+00:00</published>
    <updated>2022-09-21T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn how you can use AppSignal to log and debug your Ruby application.</summary>
    <content type="html">&lt;p&gt;An application monitoring tool (APM) is not just useful for seeing how your application performs through graphs and visuals. We can go deeper and use an APM to understand how your application behaves in a certain environment.&lt;/p&gt;
&lt;p&gt;As developers, we should aim to be less reactive to errors and more predictive, avoiding crashes for end-users.&lt;/p&gt;
&lt;p&gt;One way to accomplish this is by using monitoring tools to debug our application when an error occurs. AppSignal has advanced features that allow us to intelligently debug our logs and thus reflect this precautionary thinking in our applications.&lt;/p&gt;
&lt;p&gt;This article will show you how to better log and debug your Ruby application using AppSignal.&lt;/p&gt;
&lt;h2&gt;Integrate Your Ruby App with AppSignal&lt;/h2&gt;
&lt;p&gt;To start debugging with AppSignal, you need to have an AppSignal account.&lt;/p&gt;
&lt;p&gt;Integrate your Ruby application with AppSignal by following the steps in the article &lt;a href=&quot;https://blog.appsignal.com/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html&quot;&gt;&amp;#39;Ruby on Rails Application Monitoring with AppSignal&amp;#39;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Log Options&lt;/h2&gt;
&lt;p&gt;Now that you have an application set up to send logs to AppSignal, let&amp;#39;s look at the logging options for your Ruby application.&lt;/p&gt;
&lt;p&gt;AppSignal offers various log levels for your application according to your project&amp;#39;s needs. In this section, we will explore the difference between each log level.&lt;/p&gt;
&lt;p&gt;At first glance, we might think these logs are related to the application logs shown in the &amp;#39;Backtrace&amp;#39; section of AppSignal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-09/backtrace.png&quot; alt=&quot;Backtrace Section&quot;/&gt;&lt;/p&gt;
&lt;p&gt;However, changes to &lt;code&gt;log_level&lt;/code&gt; won&amp;#39;t reflect the information displayed in the backtrace. It is important to understand that setting the &lt;code&gt;log_level&lt;/code&gt; value relates to AppSignal&amp;#39;s communication logs with your application. It has nothing to do with application logs.&lt;/p&gt;
&lt;p&gt;This means that when we set the logging level in our application, we change the type of information that appears in the application logs about execution and integration with AppSignal.&lt;/p&gt;
&lt;p&gt;In most cases, you can just set the &lt;code&gt;log_level&lt;/code&gt; to error (unless you&amp;#39;ve had an issue with the AppSignal integration and need to send log information to AppSignal&amp;#39;s Support team). The &lt;code&gt;log_level&lt;/code&gt; must be defined in &lt;code&gt;config/appsignal.yml&lt;/code&gt;, as in the example below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/appsignal.yml

production:
  &amp;lt;&amp;lt;: *defaults
  log_level: error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Watch the application logs when we set &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, or &lt;code&gt;info&lt;/code&gt; to &lt;code&gt;log_level&lt;/code&gt;. We will see pretty much the same information in the logs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-09/error.png&quot; alt=&quot;error as log_level&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now, check out the logs when we set &lt;code&gt;log_level&lt;/code&gt; as &lt;code&gt;debug&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-09/debug.png&quot; alt=&quot;debug as log_level&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Finally, let&amp;#39;s see what happens when we set &lt;code&gt;log_level&lt;/code&gt; as &lt;code&gt;trace&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-09/trace.png&quot; alt=&quot;trace as log_level&quot;/&gt;&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Ignore Your Ruby App&amp;#39;s Predicted Errors in AppSignal&lt;/h2&gt;
&lt;p&gt;You don&amp;#39;t need to catch and keep all errors in logs. Some common errors can be expected in an application — we don&amp;#39;t need to inflate our logs with 404 errors, for example. Now let&amp;#39;s configure AppSignal to ignore errors that are irrelevant to trace.&lt;/p&gt;
&lt;p&gt;Ignoring an error is useful when a production bug has already been identified, but the fix is still in development. We already know about the problem&amp;#39;s existence, so there&amp;#39;s no need to keep filling the error monitoring tool or be notified about it. We can ignore the error&amp;#39;s appearance until the patch deployment goes into production. This approach can be applied to all known bugs that have already been cataloged in the issues list.&lt;/p&gt;
&lt;p&gt;We can also ignore errors when we know that a service our application communicates with will be down for maintenance. This way, we don&amp;#39;t flood the monitoring tool with errors regarding expected maintenance, alerting our monitoring team unnecessarily.&lt;/p&gt;
&lt;p&gt;AppSignal&amp;#39;s gem provides three ways to handle and ignore predicted errors.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;ignore_errors&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ignore_errors&lt;/code&gt; helps when you need to ignore all errors generated by a class error or when you don&amp;#39;t know the exact error that could happen from there.&lt;/p&gt;
&lt;p&gt;Maybe your application needs to do many math calculations, and AppSignal doesn&amp;#39;t need to track all related errors. &lt;code&gt;ignore_errors&lt;/code&gt; will reduce the errors list and only keep issues that need to be addressed.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;ignore_actions&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This is useful if you know exactly which method will generate an error you don&amp;#39;t need to trace — for example, a method that will be re-executed until it succeeds. You can also use &lt;code&gt;ignore_actions&lt;/code&gt; for queue processing when another monitoring tool handles logs specific to queue item failures.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;ignore_namespace&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ignore_namespace&lt;/code&gt; is great if you have an application separated by domain and want to group errors. Maybe you have a specific module to treat payments or to process an order in the background. Grouping it in a namespace for jobs makes sense. Then you can ignore the exceptions that occur inside this namespace.&lt;/p&gt;
&lt;h3&gt;Configure in Your Ruby App&lt;/h3&gt;
&lt;p&gt;Include &lt;code&gt;ignore_errors&lt;/code&gt;, &lt;code&gt;ignore_actions&lt;/code&gt;, and &lt;code&gt;ignore_namespace&lt;/code&gt; in your &lt;code&gt;config/appsignal.yml&lt;/code&gt; file, with a list of classes to ignore when this error occurs in your application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/appsignal.yml

production:
  &amp;lt;&amp;lt;: *defaults
  ignore_errors:
    - Net::HTTPGatewayTimeout
    - NoMethodError
  ignore_actions:
    - HomeController#index
  ignore_namespace:
    - jobs
    - payment
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Debugging Exceptions in Ruby with AppSignal&lt;/h2&gt;
&lt;p&gt;Now that we know how to ignore irrelevant errors, it&amp;#39;s time to deal with the errors that matter. AppSignal provides functions to configure how an error is sent from your application to the AppSignal monitor. Let&amp;#39;s learn about the &lt;code&gt;listen_for_error&lt;/code&gt; feature and extract valuable information from exceptions.&lt;/p&gt;
&lt;p&gt;Sometimes, we have a situation where an exception may or may not be thrown due to some parameters (if a job is not running or even if an external endpoint is unavailable). We still want to send detailed information to AppSignal, not just the exception itself. For this case, we&amp;#39;ll create a custom exception and use it to send helpful information to the monitoring team.&lt;/p&gt;
&lt;p&gt;The first thing we need to do is create a class for our custom exception. Make a new folder called &lt;code&gt;exceptions&lt;/code&gt; inside your application directory. Then create a new class called &lt;code&gt;CustomException&lt;/code&gt; inherited from &lt;code&gt;StandardError&lt;/code&gt;. You can define any information as the content of this exception. By default, an exception already has &lt;code&gt;msg&lt;/code&gt; as an attribute.&lt;/p&gt;
&lt;p&gt;In this example, you need to include an exception &lt;code&gt;name&lt;/code&gt; and the &lt;code&gt;content&lt;/code&gt; field to report all details about the exception. In the exception constructor, all attributes are set and the method to send to AppSignal is called.&lt;/p&gt;
&lt;p&gt;The block inside &lt;code&gt;Appsignal.listen_for_error&lt;/code&gt; is where the magic happens! The raise will send the exception content to AppSignal in hash format to facilitate the separation of the different contents.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/exceptions/custom_exception.rb

class CustomException &amp;lt; StandardError
  attr_accessor :name, :content

  def initialize(name, msg, content)
    @name = name
    @msg = msg
    @content = content

    send_exception_to_appsignal
  end

  def send_exception_to_appsignal
    Appsignal.listen_for_error do
      exception_hash = { name: @name, msg: @msg, content: @content }
      raise &amp;quot;#{exception_hash}&amp;quot;
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can call this custom exception from anywhere, to use it in your application. For this example, we will include it in the controller inside the &lt;code&gt;Home#index&lt;/code&gt; method. We need a block to handle the exception, to avoid sending it to AppSignal. The &lt;code&gt;nil.object&lt;/code&gt; code will throw a &lt;code&gt;NoMethodError&lt;/code&gt; exception, but this exception is handled, and only &lt;code&gt;CustomException&lt;/code&gt; will be sent to AppSignal.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/home_controller.rb

class HomeController &amp;lt; ApplicationController
  def index
    begin
      nil.object
    rescue =&amp;gt; e
      raise CustomException.new(&amp;quot;Customized Exception&amp;quot;, &amp;quot;A new exception occurred&amp;quot;, e)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All done! After starting your application and accessing &lt;a href=&quot;http://localhost:3000/home#index&quot;&gt;http://localhost:3000/home#index&lt;/a&gt; in your browser, you will see a page with a &lt;code&gt;RuntimeError&lt;/code&gt; exception. In AppSignal, access the &amp;#39;Errors &amp;gt; Issue list&amp;#39; to see the Customized Exception issue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{:name=&amp;gt;&amp;quot;Customized Exception&amp;quot;, :msg=&amp;gt;&amp;quot;A new exception occurred&amp;quot;, :content=&amp;gt;#&amp;lt;NoMethodError: undefined method `object&amp;#39; for nil:NilClass&amp;gt;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-09/custom-exception.png&quot; alt=&quot;Custom Exception&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Wrap Up and Next Steps&lt;/h2&gt;
&lt;p&gt;AppSignal provides a well-documented gem to start truly monitoring your Ruby application. Why not test it? Send data from your local machine without deploying to see if the error configuration is how you want it.&lt;/p&gt;
&lt;p&gt;Many other features in AppSignal make it easier to debug Ruby applications, like &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/exception-handling.html&quot;&gt;exception handling&lt;/a&gt;. &lt;a href=&quot;https://docs.appsignal.com/ruby/&quot;&gt;Check AppSignal&amp;#39;s Ruby documentation&lt;/a&gt; and choose the feature that makes the most sense in your project&amp;#39;s context. Make your app easier to debug by decreasing the time your team needs to spend looking into the root cause of bugs in production.&lt;/p&gt;
&lt;p&gt;Happy debugging!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>JIT Compilers for Ruby and Rails: An Overview</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/09/07/jit-compilers-for-ruby-and-rails-an-overview.html"/>
    <id>https://blog.appsignal.com/2022/09/07/jit-compilers-for-ruby-and-rails-an-overview.html</id>
    <published>2022-09-07T00:00:00+00:00</published>
    <updated>2022-09-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out about the different JIT compilers for Ruby — YJIT, MJIT, and TenderJIT — and their benefits.</summary>
    <content type="html">&lt;p&gt;A program is compiled at runtime using a different method from pre-execution compilation. This process is known as just-in-time compilation or dynamic translation.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll look at why JIT compilation can be a good choice for your Ruby on Rails app, before looking at some of the options available (YJIT, MJIT, and TenderJIT) and how to install them.&lt;/p&gt;
&lt;p&gt;But first: how does JIT compilation work?&lt;/p&gt;
&lt;h2&gt;How a JIT Compiler Works&lt;/h2&gt;
&lt;p&gt;Just-in-time compilation is a method of running computer code that requires compilation while running a program.&lt;/p&gt;
&lt;p&gt;This could entail translating the source code, but it&amp;#39;s most frequently done by converting the bytecode to machine code,
which is then run directly.&lt;/p&gt;
&lt;p&gt;The code being executed is often continuously analyzed by a system using a JIT compiler. This identifies sections of code where the benefit of compilation or recompilation (in terms of speed) outweighs the cost.&lt;/p&gt;
&lt;h2&gt;Benefits of JIT Compilation for Ruby&lt;/h2&gt;
&lt;p&gt;JIT compilation combines some of the benefits (and shortcomings) of the two conventional methods for converting programs into machine code:
interpretation and ahead-of-time compilation (AOT).&lt;/p&gt;
&lt;p&gt;Roughly speaking, it combines the flexibility of interpretation with the speed
of generated code, and the additional overhead of compiling and linking (not just interpreting).&lt;/p&gt;
&lt;p&gt;JIT compilation is a type of dynamic
compilation that enables adaptive optimization techniques, including dynamic recompilation and speed-ups tailored to certain microarchitectures. Due to a runtime system&amp;#39;s ability to handle late-bound data types and impose security guarantees, dynamic programming languages like Ruby are particularly
well-suited for interpretation and JIT compilation.&lt;/p&gt;
&lt;p&gt;An optimizing compiler like GCC can more efficiently optimize instructions — a significant advantage of adopting
a register-oriented architecture. Compilers operate on intermediate representation with register-based architecture.&lt;/p&gt;
&lt;p&gt;Once your instructions reach an intermediate representation during compilation, GCC does additional passes to speed up the CPU&amp;#39;s execution of your instructions.&lt;/p&gt;
&lt;h2&gt;JIT Compilers for Ruby: YJIT, MJIT, and TenderJIT&lt;/h2&gt;
&lt;p&gt;Now let&amp;#39;s explore the different JIT compilers available for Ruby — YJIT, MJIT, and TenderJIT — and how you can set them up.&lt;/p&gt;
&lt;h2&gt;MJIT (Method-based Just-in-time Compiler) for Ruby&lt;/h2&gt;
&lt;p&gt;Vladimir Makarov implemented MJIT, and it was the first compiler methodology implemented in Ruby based on the C language.
It works with Ruby 2.6, uses YARV instructions, and compiles instructions often used in binary code.&lt;/p&gt;
&lt;p&gt;For programs that are not input/output-bound, MJIT enhances performance.&lt;/p&gt;
&lt;p&gt;YJIT is better than this original C-based compiler in terms of performance. Ruby 3&amp;#39;s JIT is the quickest JIT that MRI has ever had, made possible by the excellent work of MJIT.&lt;/p&gt;
&lt;h3&gt;How to Use MJIT&lt;/h3&gt;
&lt;p&gt;To use MJIT, you can enable the JIT in Ruby 2.6 and with the &lt;code&gt;--jit&lt;/code&gt; option.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;ruby --jit app.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you skip this part, MJIT will show an error.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;clang: error: cannot specify -o when generating multiple output files
MJIT warning: Making precompiled header failed on compilation. Stopping MJIT worker...
MJIT warning: failed to remove &amp;quot;/var/folders/3d/fk_588wd4g12syc56pjqybjc0000gn/T//_ruby_mjit_hp25992u0.h.gch&amp;quot;: No such file or directory
Successful MJIT finish
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;A collection of JIT-specific settings included in Ruby 2.6 helps us understand how it functions. Run &lt;code&gt;ruby --help&lt;/code&gt; to view these options.&lt;/p&gt;
&lt;p&gt;In short, MJIT executes in a different thread and is asynchronous. It will begin just-in-time compilation following the first five runs of a calculation.&lt;/p&gt;
&lt;h2&gt;YJIT for Ruby on Rails&lt;/h2&gt;
&lt;p&gt;A recent JIT compiler called YJIT was released with Ruby 3.1. It promises a lot of improvements and better performance.
Still a work-in-progress project designed by Shopify with experimental results, it must be used with caution, especially on larger applications.&lt;/p&gt;
&lt;p&gt;With that in mind, YJIT enhances the performance of Ruby on Rails applications. The majority of real-world software benefits
from the fast warm-up and performance enhancements provided by the YJIT basic block versioning JIT compiler.&lt;/p&gt;
&lt;p&gt;A JIT compiler will be gradually built into CRuby as part of the YJIT project, eventually replacing the interpreter for most of the code execution.&lt;/p&gt;
&lt;p&gt;Official benchmarks — see &lt;a href=&quot;https://shopify.engineering/yjit-just-in-time-compiler-cruby&quot;&gt;&amp;#39;YJIT: Building a New JIT Compiler for CRuby&amp;#39;&lt;/a&gt; — show that YJIT improved performance over the default CRuby interpreter by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;20% on railsbench&lt;/li&gt;
&lt;li&gt;39% on liquid template rendering&lt;/li&gt;
&lt;li&gt;37% on activerecord&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Only about 79% of instructions in railsbench are executed by YJIT,
and the rest run in the default interpreter.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://shopify.engineering/yjit-just-in-time-compiler-cruby&quot;&gt;Source: YJIT: Building a New JIT Compiler for CRuby&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This means that a lot still needs to be done to improve YJIT&amp;#39;s current results.&lt;/p&gt;
&lt;p&gt;Even so, YJIT performs at least as well as the interpreter on every benchmark, even on the hardest
ones, and reaches near-peak performance after just one iteration of every benchmark.&lt;/p&gt;
&lt;h3&gt;How to Use YJIT&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: YJIT is currently limited to macOS and Linux on x86-64 platforms. Also, as mentioned, YJIT is not recommended for large applications (yet).&lt;/p&gt;
&lt;p&gt;YJIT is disabled by default. If you want to enable it, first specify the &lt;code&gt;--yjit&lt;/code&gt; command-line option.&lt;/p&gt;
&lt;p&gt;You need to check if it is installed, so run &lt;code&gt;ruby --enable-yjit -v&lt;/code&gt;. If &lt;code&gt;warning: unknown argument for --enable: &lt;/code&gt;yjit&amp;#39;` shows up, you have to install it.&lt;/p&gt;
&lt;p&gt;Then open &lt;code&gt;irb&lt;/code&gt; and set &lt;code&gt;RUBY_YJIT_ENABLE=1&lt;/code&gt;. You can exit and now, you&amp;#39;re ready to use YJIT. The command &lt;code&gt;ruby --enable-yjit -v&lt;/code&gt; must return something like &lt;code&gt;ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [arm64-darwin21]&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;TenderJIT&lt;/h2&gt;
&lt;p&gt;With a design largely based off YJIT, TenderJIT is an experimental JIT compiler for Ruby. What&amp;#39;s different about TenderJIT is that it&amp;#39;s written in pure Ruby.&lt;/p&gt;
&lt;p&gt;This is a demo project and the aim is to ship it as a gem. In the meantime, you can experiment with it, but bear in mind it&amp;#39;s still a work in progress. Ruby 3.0.2 or later is required for TenderJIT.&lt;/p&gt;
&lt;h3&gt;How to Use TenderJIT&lt;/h3&gt;
&lt;p&gt;TenderJIT does not currently do method compilation automatically. To compile a method, you must manually configure TenderJIT.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tenderlove/tenderjit&quot;&gt;Clone the repository&lt;/a&gt; and run the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle install
$ bundle exec rake test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You must set it manually on your code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;require &amp;quot;tenderjit&amp;quot;

def your_method
  ...
end

jit = TenderJIT.new
jit.compile(method(:your_method))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each YARV instruction in the target method is read by TenderJIT, which then transforms it into machine code.&lt;/p&gt;
&lt;p&gt;For more examples with TenderJIT, check one of these videos:
&lt;a href=&quot;https://www.youtube.com/watch?v=FCjwSOlHqbY&amp;ab_channel=hexdevs&quot;&gt;A JIT compiler for Ruby with Aaron Patterson&lt;/a&gt; and
&lt;a href=&quot;https://www.youtube.com/watch?v=mPtouOS0gxE&amp;ab_channel=Tenderlove%27sCoolStuff&quot;&gt;Hacking on TenderJIT!&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;#39;ve taken a quick look at three JIT compilers for Ruby — MJIT, YJIT, and TenderJIT — and how to set them up. Each of the options is experimental and comes with its own limitations.&lt;/p&gt;
&lt;p&gt;However, YJIT is the most mature at the moment, and it has the biggest potential
to grow and scale. It demonstrates better performance over the other Ruby JITs, was developed with Ruby 3.1.0, and is quickly becoming an important part of CRuby.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://hokstad.com/compiler&quot;&gt;Check out this post if you want to build your own compiler for Ruby&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Monitor Ruby Application Performance with Magic Dashboards</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/08/31/monitor-ruby-application-performance-with-magic-dashboards.html"/>
    <id>https://blog.appsignal.com/2022/08/31/monitor-ruby-application-performance-with-magic-dashboards.html</id>
    <published>2022-08-31T00:00:00+00:00</published>
    <updated>2022-08-31T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s monitor and fix performance issues within a Ruby on Rails application using magic dashboards.</summary>
    <content type="html">&lt;p&gt;Application teams must understand what their customer experience is like. This is true not only from a general perspective (in terms of usability and responsiveness) but also on a day-to-day, minute-by-minute basis.&lt;/p&gt;
&lt;p&gt;In particular, when you work with distributed systems, errors are inevitable. Site traffic fluctuates throughout the day, and any one of a system’s dependencies could also encounter an issue at any time.&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll use magic dashboards to help monitor and resolve performance issues within a Ruby on Rails application.&lt;/p&gt;
&lt;p&gt;But before we dive into magic dashboards, let&amp;#39;s see what we should look out for when designing our apps.&lt;/p&gt;
&lt;h2&gt;Things to Consider When Building A Ruby App&lt;/h2&gt;
&lt;p&gt;As an application owner or team member, you&amp;#39;ll want to know if there are any problems with your application before the customer does. This enables you to take corrective action immediately and hopefully avoid any disruption to your users.&lt;/p&gt;
&lt;p&gt;There are a few key application questions you need to be able to answer at any point in time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the page response time acceptable? According to Google, these days anything &lt;a href=&quot;https://www.youtube.com/watch?v=OpMfx_Zie2g&quot;&gt;slower than two seconds&lt;/a&gt; will cause customers to leave your site and go elsewhere.&lt;/li&gt;
&lt;li&gt;Are your users experiencing any errors? If so, what types of errors? What is the error rate?&lt;/li&gt;
&lt;li&gt;Are any ongoing operational issues affecting your application? This could be with the network, storage, or security services. As we all know, cloud providers have outages that impact our application’s health as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Observability is key, yet it is often the last thing engineers think about. Besides, there is already time built into the schedule for operational readiness. It&amp;#39;s called the weekend before the product launch!&lt;/p&gt;
&lt;p&gt;Joking aside, you have to build your app first before something exists to be observed. This lends itself to delaying performance testing until close to the end of the development lifecycle. You don’t want to spend too much time performance testing when the product isn’t code-complete, because then you’ll have to do it all over again later.&lt;/p&gt;
&lt;p&gt;The same is true for security testing. If you conduct it too early, a vulnerability could still be introduced after testing, but before a product goes into production.&lt;/p&gt;
&lt;p&gt;So, we know monitoring is critical, but all of these concerns are fair points. Monitoring doesn’t get the attention it deserves until late in the process. That’s why AppSignal created &lt;a href=&quot;https://blog.appsignal.com/2019/03/27/magic-dashboards.html&quot;&gt;magic dashboards&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Magic Dashboards in AppSignal&lt;/h2&gt;
&lt;p&gt;AppSignal understands the importance of metrics, dashboards, and your app&amp;#39;s performance — but also that engineers have minimal time to work on these before launch.&lt;/p&gt;
&lt;p&gt;Our magic dashboards are called &amp;quot;magic&amp;quot; because the metrics collection and associated dashboards are created automatically for you, simply when you connect your app and integrated components.&lt;/p&gt;
&lt;h2&gt;Magic Dashboards: An Example Ruby on Rails App&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s find out how we can monitor and resolve performance issues within a Ruby on Rails app using magic dashboards.&lt;/p&gt;
&lt;p&gt;Our application uses a simple machine learning (ML) model for cryptocurrency price prediction. An asynchronous job updates the model daily with the latest price data and improves accuracy over time. This Rails app has both web pages and a REST API, as shown in the architecture diagram below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/magic-dashboard-app-architecture.png&quot; alt=&quot;Architecture diagram for the sample application&quot;/&gt;&lt;/p&gt;
&lt;p&gt;After we deploy our Rails app, magic dashboards are automatically created for the Rails &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/puma.html#minutely-probe&quot;&gt;Puma&lt;/a&gt; web server and the &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/sidekiq.html#minutely-probe&quot;&gt;Sidekiq&lt;/a&gt; asynchronous jobs. Other supported integrations for magic dashboards include &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/mongodb.html&quot;&gt;MongoDB&lt;/a&gt; and the &lt;a href=&quot;https://docs.appsignal.com/elixir/integrations/erlang.html#minutely-probe&quot;&gt;Erlang VM&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can use the Puma magic dashboard to evaluate performance based on threads, pool capacity, and puma workers. The Sidekiq magic dashboard monitors queue length, queue latency, job duration, job status, and memory usage.&lt;/p&gt;
&lt;p&gt;Magic dashboards are detected and created based on the use of event-based metrics such as a Sidekiq job run, as well as &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/minutely-probes.html&quot;&gt;minutely probes&lt;/a&gt;. The minutely probe feature allows you to register a Ruby block or class to send custom metrics to AppSignal. Magic dashboards provide out-of-the-box integration for supported components, but you can leverage this mechanism to send your custom metrics to AppSignal.&lt;/p&gt;
&lt;h2&gt;An Example of Custom Minutely Probes&lt;/h2&gt;
&lt;p&gt;Sidekiq integration is already built in, but imagine you want to monitor a proprietary background job mechanism. You can use the following class example. The probe class obtains a connection that is then used on each call to get the desired metrics.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/appsignal.rb or a file that&amp;#39;s loaded on boot

# Creating a probe using a Ruby class
class BackgroundJobLibraryProbe
  def initialize
    # This is only called when the minutely probe gets initialized
    require &amp;quot;background_job_library&amp;quot;
    @connection = BackgroundJobLibrary.connection
  end

  def call
    stats = @connection.fetch_queue_stats
    Appsignal.set_gauge &amp;quot;background_job_library_queue_length&amp;quot;, stats.queue_length
    Appsignal.set_gauge &amp;quot;background_job_library_processed_jobs&amp;quot;, stats.processed_jobs
  end
end

# Registering a Class probe
Appsignal::Minutely.probes.register(
  :background_job_library_probe, BackgroundJobLibraryProbe
)
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The call to &lt;code&gt;Appsignal::Minutely.probes.register&lt;/code&gt; takes two parameters — a name for the probe and its implementation, which can be either a lambda or a class that implements the call method.&lt;/p&gt;
&lt;h2&gt;Configuration and System Requirements for AppSignal&lt;/h2&gt;
&lt;p&gt;Simply use AppSignal with your Rails application to get magic dashboards. If you haven’t already installed AppSignal, include the appsignal gem in your Gemfile and run a bundle install.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;appsignal&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the &lt;code&gt;appsignal install&lt;/code&gt; command to configure your environment. This configuration can be stored in a config file or environment variables, and connects your application to your AppSignal account and dashboards.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec appsignal install appsignal-license-key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After you install and run your server, you will see a few emails similar to the following.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/email-notification.png&quot; alt=&quot;Informational email that a Sidekiq magic dashboard was created&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The system requirements for this example are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AppSignal gem 2.9.0 or higher, which includes support for the minutely probe.&lt;/li&gt;
&lt;li&gt;Puma integration - requires version 3.11.4 or higher.&lt;/li&gt;
&lt;li&gt;Sidekiq integration - requires the &lt;a href=&quot;https://rubygems.org/gems/redis/&quot;&gt;Redis gem&lt;/a&gt; 3.3.5 or higher. For this integration, the AppSignal gem 2.9.5 or higher is recommended.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Performance Testing our Price Prediction API&lt;/h2&gt;
&lt;p&gt;The above example application uses a simple neural network implemented using the &lt;a href=&quot;https://github.com/tangledpath/ruby-fann&quot;&gt;Ruby FANN gem&lt;/a&gt; to predict the next day&amp;#39;s Bitcoin price. Percentage price changes from the last ten days are used as inputs to the model.&lt;/p&gt;
&lt;p&gt;The REST API doesn&amp;#39;t take any parameters, as it currently only predicts the price for tomorrow. The output is a simple JSON document, as shown below.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;http://localhost:3000/crypto/predict_api

{
  &amp;quot;day&amp;quot;:&amp;quot;2022-07-22&amp;quot;,
  &amp;quot;price&amp;quot;:23080.95619
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A Sidekiq job gets the market price at the beginning of each day and updates the ML model. This job is scheduled to run every five minutes, enabling it to catch up to any outages or errors quickly.&lt;/p&gt;
&lt;p&gt;You can find all of the &lt;a href=&quot;https://github.com/dbroemme/btc-price-prediction&quot;&gt;code from this article on GitHub&lt;/a&gt;. &lt;em&gt;Please note that nothing in this article or the software constitutes investment advice. Consult a financial advisor before making any investment decisions regarding cryptocurrency.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll use JMeter to simulate load on the REST API for performance testing. This allows us to generate traffic and evaluate performance using our magic dashboards. Our first test uses 5 concurrent clients, each making 100 requests.&lt;/p&gt;
&lt;p&gt;The statistics show a fairly high range in response time, with an average of 342ms, but the P99 is 852ms. It seems that we can do better.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/jmeter-report-five-threads.png&quot; alt=&quot;Summary report of test with five Puma threads&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Improving Rails App Performance with the Puma Magic Dashboard&lt;/h2&gt;
&lt;p&gt;Our Puma magic dashboard includes a graph of the thread pool capacity, and we can see that it touches zero during our test run. This would explain why some requests take longer than others, so we&amp;#39;ll increase the number of puma threads to 10.&lt;/p&gt;
&lt;p&gt;Keep in mind, we did nothing to create the dashboard or this graph. AppSignal automatically created this for us.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/puma-dashboard-pool-capacity-10.png&quot; alt=&quot;Graph of Puma pool availability during multiple test runs&quot;/&gt;&lt;/p&gt;
&lt;p&gt;After running the test with the increased puma thread count, the response times are much more consistent, and the dashboard confirms that pool capacity stays within acceptable levels.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/jmeter-report-ten-threads.png&quot; alt=&quot;Summary report of test with ten Puma threads&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now it is time to turn up the dial. The number of JMeter concurrent clients is increased to 50, with a ramp-up time of 5 seconds. This test shows a poor response time and a number of API errors. The Puma magic dashboard again shows the available puma threads reaching zero.&lt;/p&gt;
&lt;p&gt;Looking at the API code, we find that the model is being loaded from the database each time. This is not very efficient, so we change the Sidekiq job to not only grab the new daily price, but also run the ML model and save the prediction in the database.&lt;/p&gt;
&lt;p&gt;We deploy this change.&lt;/p&gt;
&lt;h2&gt;Better API Performance with Sidekiq&amp;#39;s Magic Dashboard&lt;/h2&gt;
&lt;p&gt;Now let&amp;#39;s check our Sidekiq magic dashboard. Unfortunately, there is a bug in the code, but at least we can identify and fix it quickly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/sidekiq-error-graph.png&quot; alt=&quot;Graph of Sidekiq job status over time&quot;/&gt;&lt;/p&gt;
&lt;p&gt;With the Sidekiq &lt;code&gt;PriceUpdateJob&lt;/code&gt; now working, the API is modified so that it only needs to retrieve the predicted price from the database. This improves the API performance, but we still see some API errors and lengthy response times.&lt;/p&gt;
&lt;h2&gt;Back to the Puma Magic Dashboard&lt;/h2&gt;
&lt;p&gt;A glance at the dashboard highlights that we have not yet configured Puma to use additional workers. A Puma worker is an OS-level process that can run several threads. The total thread count is calculated as the number of workers multiplied by the maximum threads. First, we use 2 workers, but the available threads are still exhausted. So we&amp;#39;ll increase the amount to 4 workers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/puma-four-workers.png&quot; alt=&quot;Graph of Puma workers over time&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Our API performance is now very good. The average response time is 419ms, and there are no API errors.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/puma-capacity-four-workers.png&quot; alt=&quot;Graph of Puma thread capacity over time&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The magic dashboard confirms that there is still available thread capacity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/jmeter-report-four-workers.png&quot; alt=&quot;JMeter test report with four workers&quot;/&gt;&lt;/p&gt;
&lt;p&gt;AppSignal&amp;#39;s magic dashboards give us instant insights into the capacity of the Puma web server. The Sidekiq and Active Worker dashboards provide similar insights into our application&amp;#39;s asynchronous jobs.&lt;/p&gt;
&lt;h3&gt;What We&amp;#39;ve Learned from Magic Dashboards&lt;/h3&gt;
&lt;p&gt;During our Rails application performance testing, the Puma magic dashboard helped us easily identify that the number of threads was insufficient for our desired throughput level. It shows the thread pool capacity, number of workers over time, and the total number of threads.&lt;/p&gt;
&lt;p&gt;While the Sidekiq job did not have any performance issues, the magic dashboard helped us quickly identify that there was an error after deployment, which we were able to fix and redeploy rapidly. All of these monitoring capabilities were set up for us automatically by AppSignal.&lt;/p&gt;
&lt;h2&gt;AppSignal&amp;#39;s Dashboard Features for Ruby and Rails Apps&lt;/h2&gt;
&lt;p&gt;Dashboards in AppSignal have namespaces, such as &amp;quot;web&amp;quot; application or &amp;quot;background&amp;quot; jobs, so you can create numerous monitoring views of your application. The built-in summary dashboard shows an overview of your application&amp;#39;s health, including throughput, response time, and the latest errors.&lt;/p&gt;
&lt;p&gt;Note that, as with any dashboard, you can edit what graphs and metrics are shown, as well as change the layout and the configuration of selected graphs.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.appsignal.com/anomaly-detection#main&quot;&gt;Anomaly detection&lt;/a&gt; is a powerful feature. It allows you to define thresholds that send notifications when a metric value goes over or below a given value, such as free memory or the error rate.&lt;/p&gt;
&lt;h2&gt;Wrapping Up: Monitor Your Ruby App Today with AppSignal&lt;/h2&gt;
&lt;p&gt;Observability and performance are critical to the success of our applications. However, we often wait until the last minute to deal with these topics.&lt;/p&gt;
&lt;p&gt;AppSignal&amp;#39;s magic dashboards provide extensive out-of-the-box metrics, dashboards, and insights that are set up automatically. This allows you to rapidly tune your application’s performance and reach a state of operational readiness.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;Read more about AppSignal for Ruby&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Until next time, happy coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Ractors in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/08/24/an-introduction-to-ractors-in-ruby.html"/>
    <id>https://blog.appsignal.com/2022/08/24/an-introduction-to-ractors-in-ruby.html</id>
    <published>2022-08-24T00:00:00+00:00</published>
    <updated>2022-08-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover when and why you should use ractors, and build a ractor in Ruby.</summary>
    <content type="html">&lt;p&gt;In this post, we&amp;#39;ll dive into ractors in Ruby, exploring how to build a ractor. You&amp;#39;ll send and receive messages in ractors, and learn about shareable and unshareable objects.&lt;/p&gt;
&lt;p&gt;But first, let&amp;#39;s define the actor model and ractors, and consider when you should use ractors.&lt;/p&gt;
&lt;h2&gt;What is the Actor Model?&lt;/h2&gt;
&lt;p&gt;In computer science, the object-oriented model is very popular, and in the Ruby community, many people are used to the term &amp;#39;everything is an object&amp;#39;.&lt;/p&gt;
&lt;p&gt;Similarly, let me introduce you to the actor model, within which &amp;#39;everything is an actor&amp;#39;. The actor model is a mathematical model of concurrent computation in which the universal primitive/fundamental agent of computation is an &lt;strong&gt;actor&lt;/strong&gt;. An actor is capable of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Receiving messages and responding to the sender&lt;/li&gt;
&lt;li&gt;Sending messages to other actors&lt;/li&gt;
&lt;li&gt;Determining how to respond to the next message received&lt;/li&gt;
&lt;li&gt;Creating several other actors&lt;/li&gt;
&lt;li&gt;Making local decisions&lt;/li&gt;
&lt;li&gt;Performing actions (e.g., mutating data in a database)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Actors communicate via messages, process one message at a time, and maintain their own private state. However, they can modify this state via messages received, eliminating the need for a lock or mutex.&lt;/p&gt;
&lt;p&gt;Received messages are processed one message at a time in the order of FIFO (first in, first out). The message sender is decoupled (isolated) from the sent communication, enabling asynchronous communication.&lt;/p&gt;
&lt;p&gt;A few examples of the actor model implementation are akka, elixir, pulsar, celluloid, and &lt;strong&gt;ractors&lt;/strong&gt;. A few examples of concurrency models include threads, processes, and futures.&lt;/p&gt;
&lt;h2&gt;What Are Ractors in Ruby?&lt;/h2&gt;
&lt;p&gt;Ractor is an actor-model abstraction that provides a parallel execution feature without &lt;a href=&quot;https://hacks.mozilla.org/2019/02/fearless-security-thread-safety/&quot;&gt;thread-safety concerns&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Just like threads, ractors provide true parallelism. However, unlike threads, they do not share everything. Most objects are unshareable, and when they are made shareable, are protected by an interpreter or locking mechanism.&lt;/p&gt;
&lt;p&gt;Ractors are also unable to access any objects through variables not defined within their scope. This means that we can be free of the possibility of &lt;a href=&quot;https://www.techtarget.com/searchstorage/definition/race-condition&quot;&gt;race conditions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In 2020, when Ruby 3.0.0 was released, these were the &lt;a href=&quot;https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/&quot;&gt;words of Matz&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s multi-core age today. Concurrency is very important. With Ractor, along with Async Fiber, Ruby will be a real concurrent language.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ractors do not claim to have solved all thread-safety problems. In the &lt;a href=&quot;https://docs.ruby-lang.org/en/3.0/ractor_md.html&quot;&gt;Ractor documentation&lt;/a&gt;, the following is clearly stated:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are several blocking operations (waiting send, waiting yield, and waiting take) so you
can make a program which has dead-lock and live-lock issues.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Some kind of shareable objects can introduce transactions (STM, for example). However,
misusing transactions will generate inconsistent state.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Without ractors, you need to trace all state mutations to debug thread-safety issues. However, the beauty of ractors is that we can concentrate our efforts on suspicious shared code.&lt;/p&gt;
&lt;h2&gt;When and Why Should I Use Ractors in Ruby?&lt;/h2&gt;
&lt;p&gt;When you create a ractor for the first time, you&amp;#39;ll get a warning like this one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;&amp;lt;internal:ractor&amp;gt;:267: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, that does not mean that you should avoid using ractors. Due to parallel execution, ractors can complete processes way faster than when processes are carried out synchronously.&lt;/p&gt;
&lt;p&gt;In the Ruby 3.0.0 release notes, you&amp;#39;ll find this benchmark example of the &lt;a href=&quot;https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/&quot;&gt;Tak function&lt;/a&gt;, where it is executed sequentially four times, and four times in parallel with ractors:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def tarai(x, y, z) =
  x &amp;lt;= y ? y : tarai(tarai(x-1, y, z),
                     tarai(y-1, z, x),
                     tarai(z-1, x, y))
require &amp;#39;benchmark&amp;#39;
Benchmark.bm do |x|
  # sequential version
  x.report(&amp;#39;seq&amp;#39;){ 4.times{ tarai(14, 7, 0) } }

  # parallel version with ractors
  x.report(&amp;#39;par&amp;#39;){
    4.times.map do
      Ractor.new { tarai(14, 7, 0) }
    end.each(&amp;amp;:take)
  }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The results are as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;Benchmark result:
          user     system      total        real
seq  64.560736   0.001101  64.561837 ( 64.562194)
par  66.422010   0.015999  66.438009 ( 16.685797)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/&quot;&gt;Ruby 3.0.0 release notes&lt;/a&gt; state:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The result was measured on Ubuntu 20.04, Intel(R) Core(TM) i7-6700 (4 cores, 8 hardware threads). It shows that the parallel version is 3.87 times faster than the sequential version.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So if you need a faster process execution time that can run in parallel on machines with multiple cores, ractors are not a bad idea at all.&lt;/p&gt;
&lt;p&gt;Modifying class/module objects on multi-ractor programs can introduce race conditions and should be avoided as much as possible. However, most objects are unshareable, so the need to implement locks to prevent race conditions becomes obsolete. If objects are shareable, they are protected by an interpreter or locking mechanism.&lt;/p&gt;
&lt;h2&gt;Creating Your First Ractor in Ruby&lt;/h2&gt;
&lt;p&gt;Creating a ractor is as easy as creating any class instance. Call &lt;code&gt;Ractor.new&lt;/code&gt; with a block — &lt;code&gt;Ractor.new { block }&lt;/code&gt;. This block is run in parallel with every other ractor.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It is important to note that every example shown from this point onwards was performed in Ruby 3.1.2.&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;r = Ractor.new { puts &amp;quot;This is my first ractor&amp;quot; }
# This is my first ractor

# create a ractor with a name
r = Ractor.new name: &amp;#39;second_ractor&amp;#39; do
  puts &amp;quot;This is my second ractor&amp;quot;
end
# This is my second ractor

r.name
# =&amp;gt; &amp;quot;second_ractor&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Arguments can also be passed to &lt;code&gt;Ractor.new&lt;/code&gt;, and these arguments become parameters for the ractor block.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;my_array = [4,5,6]
Ractor.new my_array do |arr|
  puts arr.each(&amp;amp;:to_s)
end
# 4
# 5
# 6
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Recall how we talked about ractors being unable to access objects defined outside their scope? Let&amp;#39;s see an example of that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;outer_scope_object = &amp;quot;I am an outer scope object&amp;quot;
Ractor.new do
  puts outer_scope_object
end
# &amp;lt;internal:ractor&amp;gt;:267:in `new&amp;#39;: can not isolate a Proc because it accesses outer variables (outer_scope_object). (ArgumentError)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We get an error on the invocation of &lt;code&gt;.new&lt;/code&gt;, related to a &lt;code&gt;Proc&lt;/code&gt; not being isolated. This is because &lt;code&gt;Proc#isolate&lt;/code&gt; is called at a ractor&amp;#39;s creation to prevent sharing unshareable objects. However, objects can be passed to and from ractors via messages.&lt;/p&gt;
&lt;h2&gt;Sending and Receiving Messages in Ractors&lt;/h2&gt;
&lt;p&gt;Ractors send messages via an &lt;em&gt;outgoing port&lt;/em&gt; and receive messages via an &lt;em&gt;incoming port&lt;/em&gt;. The incoming port can hold an infinite number of messages and runs on the FIFO principle.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.send&lt;/code&gt; method works the same way a mailman delivers a message in the mail. The mailman takes the message and drops it at the door (incoming port) of the ractor.&lt;/p&gt;
&lt;p&gt;However, dropping a message at a person&amp;#39;s door is not enough to get them to open it. &lt;code&gt;.receive&lt;/code&gt; is then available for the ractor to open the door and receive whatever message has been dropped.&lt;/p&gt;
&lt;p&gt;The ractor might want to do some computation with that message and return a response, so how do we get it? We ask the mailman to &lt;code&gt;.take&lt;/code&gt; the response.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;tripple_number_ractor = Ractor.new do
  puts &amp;quot;I will receive a message soon&amp;quot;
  msg = Ractor.receive
  puts &amp;quot;I will return a tripple of what I receive&amp;quot;
  msg * 3
end
# I will receive a message soon
tripple_number_ractor.send(15) # mailman takes message to the door
# I will return a tripple of what I receive
tripple_number_ractor.take # mailman takes the response
# =&amp;gt; 45
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As seen above, the return value of a ractor is also a sent message and can be received via &lt;code&gt;.take&lt;/code&gt;. Since this is an outgoing message, it goes to the outgoing port.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;r = Ractor.new do
  5**2
end
r.take # =&amp;gt; 25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides returning a message, a ractor can also send a message to its outgoing port via &lt;code&gt;.yield&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;r = Ractor.new do
  squared = 5**2
  Ractor.yield squared*2
  puts &amp;quot;I just sent a message out&amp;quot;
  squared*3
end
r.take
# =&amp;gt; 50
r.take
# =&amp;gt; 75
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first message sent to the outgoing port is &lt;code&gt;squared*2&lt;/code&gt;, and the next message is &lt;code&gt;squared*3&lt;/code&gt;. Therefore, when we call &lt;code&gt;.take&lt;/code&gt;, we get &lt;code&gt;50&lt;/code&gt; first. We have to call &lt;code&gt;.take&lt;/code&gt; a second time to get &lt;code&gt;75&lt;/code&gt; as two messages are sent to the outgoing port.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s put this all together in one example of customers sending their orders to a supermarket and receiving the fulfilled orders:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;supermarket = Ractor.new do
  loop do
    order = Ractor.receive
    puts &amp;quot;The supermarket is preparing #{order}&amp;quot;
    Ractor.yield &amp;quot;This is #{order}&amp;quot;
  end
end

customers = 5.times.map{ |i|
  Ractor.new supermarket, i do |supermarket, i|
    supermarket.send(&amp;quot;a pack of sugar for customer #{i}&amp;quot;)
    fulfilled_order = supermarket.take
    puts &amp;quot;#{fulfilled_order} received by customer #{i}&amp;quot;
  end
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output is as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;The supermarket is preparing a pack of sugar for customer 3
The supermarket is preparing a pack of sugar for customer 2
This is a pack of sugar for customer 3 received by customer 3
The supermarket is preparing a pack of sugar for customer 1
This is a pack of sugar for customer 2 received by customer 2
The supermarket is preparing a pack of sugar for customer 0
This is a pack of sugar for customer 1 received by customer 1
This is a pack of sugar for customer 0 received by customer 0
The supermarket is preparing a pack of sugar for customer 4
This is a pack of sugar for customer 4 received by customer 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running it a second time yields:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;The supermarket is preparing a pack of sugar for customer 0
This is a pack of sugar for customer 0 received by customer 0
The supermarket is preparing a pack of sugar for customer 4
This is a pack of sugar for customer 4 received by customer 4
The supermarket is preparing a pack of sugar for customer 1
This is a pack of sugar for customer 1 received by customer 1
The supermarket is preparing a pack of sugar for customer 3
The supermarket is preparing a pack of sugar for customer 2
This is a pack of sugar for customer 3 received by customer 3
This is a pack of sugar for customer 2 received by customer 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output can most definitely be in a different order every time we run this (because ractors run concurrently, as we have established).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A few things to note about sending and receiving messages:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Messages can also be sent using &lt;code&gt;&amp;lt;&amp;lt; msg&lt;/code&gt;, instead of &lt;code&gt;.send(msg)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can add a condition to a &lt;code&gt;.receive&lt;/code&gt; using &lt;code&gt;receive_if&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;.send&lt;/code&gt; is called on a ractor that is already terminated (not running), you get a &lt;code&gt;Ractor::ClosedError&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A ractor&amp;#39;s outgoing port closes after &lt;code&gt;.take&lt;/code&gt; is called on it if it runs just once (not in a loop).&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;r = Ractor.new do
  Ractor.receive
end
# =&amp;gt; #&amp;lt;Ractor:#61 (irb):120 running&amp;gt;
r &amp;lt;&amp;lt; 5
# =&amp;gt; #&amp;lt;Ractor:#61 (irb):120 terminated&amp;gt;
r.take
# =&amp;gt; 5
r &amp;lt;&amp;lt; 9
# &amp;lt;internal:ractor&amp;gt;:583:in `send&amp;#39;: The incoming-port is already closed (Ractor::ClosedError)
r.take
# &amp;lt;internal:ractor&amp;gt;:694:in `take&amp;#39;: The outgoing-port is already closed (Ractor::ClosedError)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Objects can be moved to a destination ractor via &lt;code&gt;.send(obj, move: true)&lt;/code&gt; or &lt;code&gt;.yield(obj, move: true)&lt;/code&gt;. These objects become inaccessible at the previous destination, raising a &lt;code&gt;Ractor::MovedError&lt;/code&gt; when you try to call any other methods on the moved objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;r = Ractor.new do
  Ractor.receive
end
outer_object = &amp;quot;outer&amp;quot;
r.send(outer_object, move: true)
# =&amp;gt; #&amp;lt;Ractor:#3 (irb):7 terminated&amp;gt;
outer_object + &amp;quot;moved&amp;quot;
# `method_missing&amp;#39;: can not send any methods to a moved object (Ractor::MovedError)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Threads cannot be sent as messages using &lt;code&gt;.send&lt;/code&gt; and &lt;code&gt;.yield&lt;/code&gt;. Doing this results in a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;r = Ractor.new do
  Ractor.yield(Thread.new{})
end
# &amp;lt;internal:ractor&amp;gt;:627:in `yield&amp;#39;: allocator undefined for Thread (TypeError)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Shareable and Unshareable Objects&lt;/h2&gt;
&lt;p&gt;Shareable objects are objects that can be sent to and from a ractor without compromising thread safety. An immutable object is a good example because once created, it cannot be changed — e.g., numbers and booleans.&lt;/p&gt;
&lt;p&gt;You can check the shareability of an object via &lt;code&gt;Ractor.shareable?&lt;/code&gt; and make an object shareable via &lt;code&gt;Ractor.make_shareable&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Ractor.shareable?(5)
# =&amp;gt; true
Ractor.shareable?(true)
# =&amp;gt; true
Ractor.shareable?([4])
# =&amp;gt; false
Ractor.shareable?(&amp;#39;string&amp;#39;)
# =&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As seen above, immutable objects are shareable and mutable ones aren&amp;#39;t. In Ruby, we usually call the &lt;code&gt;.freeze&lt;/code&gt; method on a string to make it immutable. This is the same method ractors apply to make an object shareable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;str = &amp;#39;string&amp;#39;
Ractor.shareable?(str)
# =&amp;gt; false
Ractor.shareable?(str.freeze)
# =&amp;gt; true
arr = [4]
arr.frozen?
# =&amp;gt; false
Ractor.make_shareable(arr)
# =&amp;gt; [4]
arr.frozen?
# =&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Messages sent via ractors can either be shareable or unshareable. When shareable, the same object is passed around. However, when unshareable, ractors perform a full copy of the object by default and send the full copy instead.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;SHAREABLE = &amp;#39;share&amp;#39;.freeze
# =&amp;gt; &amp;quot;share&amp;quot;
SHAREABLE.object_id
# =&amp;gt; 350840
r = Ractor.new do
  loop do
    msg = Ractor.receive
    puts msg.object_id
  end
end
r.send(SHAREABLE)
# 350840
NON_SHAREABLE = &amp;#39;can not share me&amp;#39;
NON_SHAREABLE.object_id
# =&amp;gt; 572460
r.send(NON_SHAREABLE)
# 610420
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As seen above, the shareable object is the same within and outside the ractor. However, the unshareable one isn&amp;#39;t because the ractor has a different object, just identical to it.&lt;/p&gt;
&lt;p&gt;Another method to send an exact object when it is unshareable is the previously discussed &lt;code&gt;move: true&lt;/code&gt;. This moves an object to a destination without needing to perform a copy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A few things to note about sharing objects in ractors:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ractor objects are also shareable objects.&lt;/li&gt;
&lt;li&gt;Constants that are shareable, but defined outside the scope of a ractor, can be accessed by a ractor. Recall our &lt;code&gt;outer_scope_object&lt;/code&gt; example? Give it another try, defined as &lt;code&gt;OUTER_SCOPE_OBJECT = &amp;quot;I am an outer scope object&amp;quot;.freeze&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Class and module objects are shareable, but instance variables or constants defined within them are not if assigned to unshareable values.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class C
  CONST = 5
  @share_me = &amp;#39;share me&amp;#39;.freeze
  @keep_me = &amp;#39;unaccessible&amp;#39;
  def bark
   &amp;#39;barked&amp;#39;
  end
end

Ractor.new C do |c|
  puts c::CONST
  puts c.new.bark
  puts c.instance_variable_get(:@share_me)
  puts c.instance_variable_get(:@keep_me)
end
# 5
# barked
# share me
# (irb):161:in `instance_variable_get&amp;#39;: can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;An incoming port or outgoing port can be closed using &lt;code&gt;Ractor#close_incoming&lt;/code&gt; and &lt;code&gt;Ractor#close_outgoing&lt;/code&gt;, respectively.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap Up and Further Reading on Ractors&lt;/h2&gt;
&lt;p&gt;In this article, we introduced the concept of ractors, including when and why to use them and how to get started. We also looked at how they communicate with one another, what objects are shareable and unshareable, and how to make objects shareable.&lt;/p&gt;
&lt;p&gt;Ractors go deeper than this. Many other public methods can be called on ractors, like &lt;code&gt;select&lt;/code&gt; to wait for the success of take, yield and receive, &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;current&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;To expand your knowledge about ractors, check out the &lt;a href=&quot;https://docs.ruby-lang.org/en/3.0/ractor_md.html&quot;&gt;ractor documentation&lt;/a&gt;. &lt;a href=&quot;https://gist.github.com/Kukunin/960ccef0d3c0a2c4b28ff5345911c2a5&quot;&gt;This GitHub gist&lt;/a&gt; might also interest you if you&amp;#39;d like to experimentally compare ractors with threads.&lt;/p&gt;
&lt;p&gt;Ractors are indeed experimental, but they certainly look like they have a bright future in Ruby&amp;#39;s evolution.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Deep Dive into Memory Leaks in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/08/10/a-deep-dive-into-memory-leaks-in-ruby.html"/>
    <id>https://blog.appsignal.com/2022/08/10/a-deep-dive-into-memory-leaks-in-ruby.html</id>
    <published>2022-08-10T00:00:00+00:00</published>
    <updated>2022-08-10T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the second and final part of our series on memory leaks in Ruby, let&#039;s dive a bit deeper into some tools you can use.</summary>
    <content type="html">&lt;p&gt;In the &lt;a href=&quot;https://blog.appsignal.com/2022/07/27/how-to-track-down-memory-leaks-in-ruby.html&quot;&gt;first part of this two-part series on memory leaks&lt;/a&gt;, we looked at how Ruby manages memory and how
Garbage Collection (GC) works.&lt;/p&gt;
&lt;p&gt;You might be able to afford powerful machines with more memory, and your app might
restart often enough that your users don&amp;#39;t notice, but memory usage matters.&lt;/p&gt;
&lt;p&gt;Allocation and Garbage Collection aren&amp;#39;t free. If you have a leak, you
spend more and more time on Garbage Collection instead of doing
what you built your app to do.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll look
deeper into the tools you can use to discover and diagnose a memory leak.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s continue!&lt;/p&gt;
&lt;h2&gt;Finding Leaks in Ruby&lt;/h2&gt;
&lt;p&gt;Detecting a leak is simple enough. You can use &lt;code&gt;GC&lt;/code&gt;, &lt;code&gt;ObjectSpace&lt;/code&gt;,
and the RSS graphs in your APM tool to watch your memory usage increase. But
just knowing you have a leak is not enough to fix it. You need to
know where it is coming from. Raw numbers can&amp;#39;t tell you that.&lt;/p&gt;
&lt;p&gt;Fortunately, the Ruby ecosystem has some great tools to attach context to
those numbers. Two are &lt;code&gt;memory-profiler&lt;/code&gt; and &lt;code&gt;derailed_benchmarks&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;memory_profiler&lt;/code&gt; in Ruby&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/SamSaffron/memory_profiler&quot;&gt;&lt;code&gt;memory_profiler&lt;/code&gt; gem&lt;/a&gt; offers a very simple API and a detailed (albeit a
little overwhelming) allocated and retained memory report — that includes the
classes of objects that are allocated, their size, and where they were allocated.
It&amp;#39;s straightforward to add to our leaky program.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# leaky.rb
require &amp;quot;memory_profiler&amp;quot;

an_array = []

report = MemoryProfiler.report do
  11.times do

    1000.times { an_array &amp;lt;&amp;lt; &amp;quot;A&amp;quot; + &amp;quot;B&amp;quot; + &amp;quot;C&amp;quot; }
    puts &amp;quot;Array is #{an_array.size} items long&amp;quot;
  end

  GC.start
end

report.pretty_print
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Outputting a report that looks similar to this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Total allocated: 440072 bytes (11001 objects)
Total retained:  440072 bytes (11001 objects)

allocated memory by gem
-----------------------------------
    440072  other

allocated memory by file
-----------------------------------
    440072  ./leaky.rb

allocated memory by location
-----------------------------------
    440000  ./leaky.rb:9
        72  ./leaky.rb:10

allocated memory by class
-----------------------------------
    440000  String
        72  Thread::Mutex

allocated objects by gem
-----------------------------------
     11001  other

allocated objects by file
-----------------------------------
     11001  ./leaky.rb

allocated objects by location
-----------------------------------
     11000  ./leaky.rb:9
         1  ./leaky.rb:10

allocated objects by class
-----------------------------------
     11000  String
         1  Thread::Mutex

retained memory by gem
-----------------------------------
    440072  other

retained memory by file
-----------------------------------
    440072  ./leaky.rb

retained memory by location
-----------------------------------
    440000  ./leaky.rb:9
        72  ./leaky.rb:10

retained memory by class
-----------------------------------
    440000  String
        72  Thread::Mutex

retained objects by gem
-----------------------------------
     11001  other

retained objects by file
-----------------------------------
     11001  ./leaky.rb

retained objects by location
-----------------------------------
     11000  ./leaky.rb:9
         1  ./leaky.rb:10

retained objects by class
-----------------------------------
     11000  String
         1  Thread::Mutex


Allocated String Report
-----------------------------------
     11000  &amp;quot;ABC&amp;quot;
     11000  ./leaky.rb:9


Retained String Report
-----------------------------------
     11000  &amp;quot;ABC&amp;quot;
     11000  ./leaky.rb:9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is a lot of information here, but generally, the
&lt;code&gt;allocated objects by location&lt;/code&gt; and &lt;code&gt;retained objects by location&lt;/code&gt; sections
can be the most useful when looking for leaks. These are the file locations
that allocate objects, ordered by the number of allocated objects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;allocated&lt;/code&gt;&lt;/strong&gt; objects are all objects allocated (created) within the
&lt;code&gt;report&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;retained&lt;/code&gt;&lt;/strong&gt; objects are objects that have not been garbage collected by
the end of the &lt;code&gt;report&lt;/code&gt; block. We forced a GC run before the end of the
block so we could see the leaked objects more clearly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Be careful about trusting the &lt;code&gt;retained&lt;/code&gt; object counts. They depend heavily
on what portion of the leaking code is within the &lt;code&gt;report&lt;/code&gt; block.&lt;/p&gt;
&lt;p&gt;For example,
if we move the declaration of &lt;code&gt;an_array&lt;/code&gt; into the &lt;code&gt;report&lt;/code&gt; block, we might be
fooled into thinking the code isn&amp;#39;t leaky.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# leaky.rb
require &amp;quot;memory_profiler&amp;quot;

report = MemoryProfiler.report do
  an_array = []

  11.times do

    1000.times { an_array &amp;lt;&amp;lt; &amp;quot;A&amp;quot; + &amp;quot;B&amp;quot; + &amp;quot;C&amp;quot; }
    puts &amp;quot;Array is #{an_array.size} items long&amp;quot;
  end

  GC.start
end

report.pretty_print
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The top of the resulting report won&amp;#39;t report many retained objects
(just the report itself).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Total allocated: 529784 bytes (11002 objects)
Total retained:  72 bytes (1 objects)
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;&lt;code&gt;derailed_benchmarks&lt;/code&gt; in Ruby&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/zombocom/derailed_benchmarks&quot;&gt;&lt;code&gt;derailed_benchmarks&lt;/code&gt; gem&lt;/a&gt; is a suite of very useful tools for all
kinds of performance work, primarily aimed at Rails apps. For finding
leaks, we want to look at &lt;code&gt;perf:mem_over_time&lt;/code&gt;, &lt;code&gt;perf:objects&lt;/code&gt;, and
&lt;code&gt;perf:heap_diff&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These tasks work by sending &lt;code&gt;curl&lt;/code&gt; requests to a running app, so we can&amp;#39;t add
them to our little leaky program. Instead, we&amp;#39;ll need to set up a small
Rails app with an endpoint that leaks memory, then install the
&lt;code&gt;derailed_benchmarks&lt;/code&gt; on that app.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Create a rails app with no database
rails new leaky --skip-active-record --minimal

## Add derailed benchmarks
cd leaky
bundle add derailed_benchmarks
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/routes.rb
Rails.application.routes.draw do
  root &amp;quot;leaks#index&amp;quot;
end

# app/controllers/leaks_controller.rb
class LeaksController &amp;lt; ApplicationController
  def index
    1000.times { $an_array &amp;lt;&amp;lt; &amp;quot;A&amp;quot; + &amp;quot;B&amp;quot; + &amp;quot;C&amp;quot; }

    render plain: &amp;quot;Array is #{$an_array.size} items long&amp;quot;
  end
end

# config/initializers/array.rb
$an_array = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now be able to boot the app with &lt;code&gt;bin/rails s&lt;/code&gt;. You&amp;#39;ll be able
to &lt;code&gt;curl&lt;/code&gt; an endpoint that leaks on each request.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ curl http://localhost:3000

Array is 1000 items long

$ curl http://localhost:3000

Array is 2000 items long
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now use &lt;code&gt;derailed_benchmarks&lt;/code&gt; to see our leak in action.&lt;/p&gt;
&lt;h4&gt;&lt;code&gt;perf:mem_over_time&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;This will show us memory use over time (similarly to how we watched the
memory growth of our leaky script with &lt;code&gt;watch&lt;/code&gt; and &lt;code&gt;ps&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Derailed will boot the app in production mode, repeatedly hit an endpoint
(&lt;code&gt;/&lt;/code&gt; by default), and report the memory usage. If it never stops growing, we
have a leak!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ TEST_COUNT=10000 DERAILED_SKIP_ACTIVE_RECORD=true \
  bundle exec derailed exec perf:mem_over_time

Booting: production
Endpoint: &amp;quot;/&amp;quot;
PID: 4417
104.33984375
300.609375
455.578125
642.69140625
751.6953125
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Derailed will boot the Rails app in production mode to perform the tests. By default, it will also &lt;code&gt;require rails/all&lt;/code&gt; first. Since we don&amp;#39;t have a database in this app, we need to override this behavior with &lt;code&gt;DERAILED_SKIP_ACTIVE_RECORD=true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can run this benchmark against different endpoints to see which one/s (if
any) leak.&lt;/p&gt;
&lt;h4&gt;&lt;code&gt;perf:objects&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;perf:objects&lt;/code&gt; task uses &lt;code&gt;memory_profiler&lt;/code&gt; under the hood so the produced
report will look familiar.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ TEST_COUNT=10 DERAILED_SKIP_ACTIVE_RECORD=true \
  bundle exec derailed exec perf:objects

Booting: production
Endpoint: &amp;quot;/&amp;quot;
Running 10 times
Total allocated: 2413560 bytes (55476 objects)
Total retained:  400000 bytes (10000 objects)

# The rest of the report...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This report can help narrow down where your leaked memory is being
allocated. In our example, the report&amp;#39;s last section — the
&lt;code&gt;Retained String Report&lt;/code&gt; — tells us exactly what our problem is.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Retained String Report
-----------------------------------
     10000  &amp;quot;ABC&amp;quot;
     10000  /Users/tonyrowan/playground/leaky/app/controllers/leaks_controller.rb:3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ve leaked 10,000 strings containing &amp;quot;ABC&amp;quot; from the &lt;code&gt;LeaksController&lt;/code&gt; on
line 3. In a non-trivial app, this report would be significantly larger and
contain retained strings that you want to retain — query caches, etc. —
but this and the other &amp;#39;by location&amp;#39; sections should help you narrow down your
leak.&lt;/p&gt;
&lt;h4&gt;&lt;code&gt;perf:heap_diff&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;perf:heap_diff&lt;/code&gt; benchmark can help if the report from &lt;code&gt;perf:objects&lt;/code&gt; is too complex to
see where your leak is coming from.&lt;/p&gt;
&lt;p&gt;As the name suggests, &lt;code&gt;perf:heap_diff&lt;/code&gt; produces three
heap dumps and calculates the difference between them. It creates a report
that includes the types of objects retained between dumps and the
location that allocated them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ DERAILED_SKIP_ACTIVE_RECORD=true bundle exec derailed exec perf:heap_diff

Booting: production
Endpoint: &amp;quot;/&amp;quot;
Running 1000 times
Heap file generated: &amp;quot;tmp/2022-06-15T11:08:28+01:00-heap-0.ndjson&amp;quot;
Running 1000 times
Heap file generated: &amp;quot;tmp/2022-06-15T11:08:28+01:00-heap-1.ndjson&amp;quot;
Running 1000 times
Heap file generated: &amp;quot;tmp/2022-06-15T11:08:28+01:00-heap-2.ndjson&amp;quot;

Diff
====
Retained STRING 999991 objects of size 39999640/40008500 (in bytes) at: /Users/tonyrowan/playground/leaky/app/controllers/leaks_controller.rb:3
Retained STRING 2 objects of size 148/40008500 (in bytes) at: /Users/tonyrowan/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/derailed_benchmarks-2.1.1/lib/derailed_benchmarks/tasks.rb:265
Retained STRING 1 objects of size 88/40008500 (in bytes) at: /Users/tonyrowan/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/derailed_benchmarks-2.1.1/lib/derailed_benchmarks/tasks.rb:266
Retained DATA 1 objects of size 72/40008500 (in bytes) at: /Users/tonyrowan/.asdf/installs/ruby/3.1.2/lib/ruby/3.1.0/objspace.rb:87
Retained IMEMO 1 objects of size 40/40008500 (in bytes) at: /Users/tonyrowan/.asdf/installs/ruby/3.1.2/lib/ruby/3.1.0/objspace.rb:88
Retained IMEMO 1 objects of size 40/40008500 (in bytes) at: /Users/tonyrowan/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/derailed_benchmarks-2.1.1/lib/derailed_benchmarks/tasks.rb:259
Retained IMEMO 1 objects of size 40/40008500 (in bytes) at: /Users/tonyrowan/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/derailed_benchmarks-2.1.1/lib/derailed_benchmarks/tasks.rb:260
Retained FILE 1 objects of size 8432/40008500 (in bytes) at: /Users/tonyrowan/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/derailed_benchmarks-2.1.1/lib/derailed_benchmarks/tasks.rb:266

Run `$ heapy --help` for more options
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also read &lt;em&gt;&lt;a href=&quot;https://medium.com/klaxit-techblog/tracking-a-ruby-memory-leak-in-2021-9eb56575f731#875b&quot;&gt;Tracking a Ruby memory leak in 2021&lt;/a&gt;&lt;/em&gt; to understand better what&amp;#39;s going on.&lt;/p&gt;
&lt;p&gt;The report points us exactly where we need to go for our leaky baby app. At the
top of the diff, we see 999991 retained string objects allocated
from the &lt;code&gt;LeaksController&lt;/code&gt; on line 3.&lt;/p&gt;
&lt;h2&gt;Leaks in Real Ruby and Rails Apps&lt;/h2&gt;
&lt;p&gt;Hopefully, the examples we&amp;#39;ve used so far have never been put into real-life
apps — I hope no one intends to leak memory!&lt;/p&gt;
&lt;p&gt;In non-trivial apps, memory
leaks can be much harder to track down. Retained objects are not always bad —
a cache with garbage collected items would not be of much use.&lt;/p&gt;
&lt;p&gt;There is something common between all leaks, though. Somewhere, a root-level
object (a class/global, etc.) holds a reference to an object.&lt;/p&gt;
&lt;p&gt;One common example is a cache without a limit or an eviction policy. By
definition, this will leak memory since every object put into the cache will
remain forever. Over time, this cache will occupy more and more of the memory
of an app, with a smaller and smaller percentage of it actually in use.&lt;/p&gt;
&lt;p&gt;Consider the following code that fetches a high score for a game. It&amp;#39;s
similar to something I&amp;#39;ve seen in the past. This is an expensive request, and
we can easily bust the cache when it changes, so we want to cache it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Score &amp;lt; ApplicationModel
  def self.user_high_score(game, user)
    @scores = {} unless @scores

    if (score = @scores[&amp;quot;#{game.id}:#{user.id}&amp;quot;])
      score
    else
      Score.where(game: game, user: user).order(:score).first.tap do |score|
        @scores[&amp;quot;#{game.id}:#{user.id}&amp;quot;] = score
      end
    end
  end

  def self.save_score(game, user, raw_score)
    score = create!(game: game, user: user, score: raw_score)

    if raw_score &amp;gt; user_high_score(game, user).score
      @scores[&amp;quot;#{game.id}:#{user.id}&amp;quot;] = score
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;@scores&lt;/code&gt; hash is completely unchecked. It will grow to hold
every single high score for every user — not ideal if you have a lot of
either.&lt;/p&gt;
&lt;p&gt;In a Rails app, we would probably want to use &lt;code&gt;Rails.cache&lt;/code&gt;
with a sensible expiry (a memory leak in Redis is still a memory leak!) instead.&lt;/p&gt;
&lt;p&gt;In a non-Rails app, we want to limit the hash size, evicting the oldest
or least recently used items. &lt;a href=&quot;https://github.com/SamSaffron/lru_redux&quot;&gt;&lt;code&gt;LruRedux&lt;/code&gt;&lt;/a&gt; is a nice
implementation.&lt;/p&gt;
&lt;p&gt;A more subtle version of this leak is a cache with a limit, but
whose keys are of arbitrary size. If the keys themselves grow, so too will the
cache. Usually, you won&amp;#39;t hit this. But, if you&amp;#39;re serializing objects as JSON and
using that as a key, double-check that you&amp;#39;re not serializing things that grow
with usage as well — such as a list of a user&amp;#39;s read messages.&lt;/p&gt;
&lt;h2&gt;Circular References&lt;/h2&gt;
&lt;p&gt;Circular references &lt;em&gt;can&lt;/em&gt; be garbage collected. Garbage Collection in Ruby uses
the &amp;quot;Mark and Sweep&amp;quot; algorithm. &lt;a href=&quot;https://rubykaigi.org/2021-takeout/presentations/peterzhu2118.html&quot;&gt;During their
presentation introducing variable
width allocation&lt;/a&gt;, Peter Zhu and Matt Valentine-House gave an excellent
explanation of how this algorithm works.&lt;/p&gt;
&lt;p&gt;Essentially, there are two phases: marking and sweeping.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;marking&lt;/strong&gt; phase, the garbage collector starts at root objects
(classes, globals, etc.), marks them, and then looks at their referenced
objects.&lt;/p&gt;
&lt;p&gt;It then marks all of the referenced objects. Referenced objects that are already
marked are not looked at again. This continues until there are no more
objects to look at — i.e., all referenced objects have been marked.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The garbage collector then moves on to the &lt;strong&gt;sweeping&lt;/strong&gt; phase. Any object not
marked is cleaned up.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Therefore, objects with live references can still be cleaned up. As long as a root object does not eventually reference an object, it will be collected. In
this way, clusters of objects with circular references can still be garbage
collected.&lt;/p&gt;
&lt;h2&gt;Application Performance Monitoring: The Event Timeline and Allocated Objects Graph&lt;/h2&gt;
&lt;p&gt;As mentioned in the &lt;a href=&quot;https://blog.appsignal.com/2022/07/27/how-to-track-down-memory-leaks-in-ruby.html&quot;&gt;first part of this series&lt;/a&gt;, any production-level app should use some form of Application Performance
Monitoring (APM).&lt;/p&gt;
&lt;p&gt;Many options are available, including rolling your
own (only recommended for larger teams). One key feature you should get from an APM is the ability
to see the number of allocations an action (or background job) makes. Good APM
tools will break this down, giving insight into where allocations come from — the controller, the view, etc.&lt;/p&gt;
&lt;p&gt;This is often called something like an &amp;#39;event timeline.&amp;#39; Bonus points if your
APM allows you to &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/instrumentation.html&quot;&gt;write custom code that further breaks down the timeline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Consider the following code for a Rails controller.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LeaksController &amp;lt; ApplicationController
  before_action :leak

  def index
    @leaks = $leak.sample(100)
  end


  private

  def leak
    1000.times { $leak &amp;lt;&amp;lt; Leak.new }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When reported by an APM, the &amp;#39;event timeline&amp;#39; might look something like the
following screenshot from AppSignal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/event-timeline-bare.png&quot; alt=&quot;Bare Event Timeline&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This can be instrumented so we can see which part of the
code makes the allocations in the timeline. In real apps, it is probably going to be less obvious
from the code 😅&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LeaksController &amp;lt; ApplicationController
  before_action :leak

  def index
    Appsignal.instrument(&amp;#39;leak.fetch_leaks&amp;#39;) do
      @leaks = $leak.sample(100)
    end
  end


  private

  def leak
    return unless params[:leak]

    Appsignal.instrument(&amp;#39;leak.create_leaks&amp;#39;) do
      1000.times { $leak &amp;lt;&amp;lt; Leak.new }
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&amp;#39;s an example of an instrumented event timeline, again from AppSignal:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/event-timeline-instrumented.png&quot; alt=&quot;Instrumented Event Timeline&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Knowing where to instrument can often be difficult to grasp. There&amp;#39;s no substitute for really understanding your application&amp;#39;s code, but there are some signals that can serve as &amp;#39;smells&amp;#39;.&lt;/p&gt;
&lt;p&gt;If your APM surfaces GC runs or allocations over time, you can look for spikes to see if they match up with certain endpoints being hit or certain running background jobs. Here&amp;#39;s another example from &lt;a href=&quot;https://blog.appsignal.com/2022/07/28/appsignal-for-ruby-gem-3-1-mri-vm-magic-dashboard.html&quot;&gt;AppSignal&amp;#39;s Ruby VM magic dashboard&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/allocated-objects.png&quot; alt=&quot;Allocations&quot;/&gt;&lt;/p&gt;
&lt;p&gt;By looking at allocations in this way, we can narrow down our search when
looking into memory problems. This makes it much easier to use tools like
&lt;code&gt;memory_profiler&lt;/code&gt; and &lt;code&gt;derailed_benchmarks&lt;/code&gt; efficiently.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.appsignal.com/2022/07/28/appsignal-for-ruby-gem-3-1-mri-vm-magic-dashboard.html&quot;&gt;Read about the latest additions to AppSignal&amp;#39;s Ruby gem, like allocation and GC stats tracking&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this post, we dived into some tools that can help find and fix memory leaks, including &lt;code&gt;memory_profiler&lt;/code&gt;, &lt;code&gt;derailed_benchmarks&lt;/code&gt;, &lt;code&gt;perf:mem_over_time&lt;/code&gt;, &lt;code&gt;perf:objects&lt;/code&gt;, &lt;code&gt;perf:heap_diff&lt;/code&gt;, the event timeline and allocated objects graph in AppSignal.&lt;/p&gt;
&lt;p&gt;I hope you&amp;#39;ve found this post, alongside &lt;a href=&quot;https://blog.appsignal.com/2022/07/27/how-to-track-down-memory-leaks-in-ruby.html&quot;&gt;part one&lt;/a&gt;, useful in diagnosing and sorting out memory leaks in your Ruby app.&lt;/p&gt;
&lt;p&gt;Read more about the tools we used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/SamSaffron/memory_profiler&quot;&gt;&lt;code&gt;memory_profiler&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zombocom/derailed_benchmarks&quot;&gt;&lt;code&gt;derailed_benchmarks&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tony-rowan/leaky-rails-app&quot;&gt;The leaky Rails app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additional detailed reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ruby-doc.org/core-3.1.2/GC.html&quot;&gt;&lt;code&gt;GC&lt;/code&gt; module documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ruby-doc.org/core-3.1.2/ObjectSpace.html&quot;&gt;&lt;code&gt;ObjectSpace&lt;/code&gt; module documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jemma.dev/blog/ruby-garbage-collection-deep-dive/&quot;&gt;Garbage Collection Deep Dive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rubykaigi.org/2021-takeout/presentations/peterzhu2118.html&quot;&gt;Variable Width Allocation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Connect a Ruby on Rails App with React in a Monolith</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/08/03/connect-a-ruby-on-rails-app-with-react-in-a-monolith.html"/>
    <id>https://blog.appsignal.com/2022/08/03/connect-a-ruby-on-rails-app-with-react-in-a-monolith.html</id>
    <published>2022-08-03T00:00:00+00:00</published>
    <updated>2022-08-03T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn about the three main types of app architecture, then connect React with your monolith Ruby on Rails app.</summary>
    <content type="html">&lt;p&gt;More and more people are using Ruby on Rails to create a back-end API application for a front-end app.&lt;/p&gt;
&lt;p&gt;But what if you want to create a rich and functional interface with JavaScript and use Rails for the back-end, without having them in separate repositories? You can create a monolithic Rails application.&lt;/p&gt;
&lt;p&gt;This article will show you how to connect a Rails application with a front-end developed in React (without splitting the code into two separate applications). We&amp;#39;ll also give some alternatives if React is not your framework of choice, but you like the idea of a monolithic app with a rich front-end.&lt;/p&gt;
&lt;p&gt;But first, let’s start with an architecture overview to see why linking Rails and React is an option worth considering.&lt;/p&gt;
&lt;h2&gt;App Architecture: An Overview&lt;/h2&gt;
&lt;p&gt;A while back, when a developer started to build a web application, they didn’t have a list of possible architectures they could use. For the most part, web apps were monoliths without a very interactive user interface.&lt;/p&gt;
&lt;p&gt;In modern software development, we have much more to choose from. We can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use the old monolith architecture&lt;/li&gt;
&lt;li&gt;Go for a separated back-end and front-end&lt;/li&gt;
&lt;li&gt;Use service-oriented architecture&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s take a closer look at the most common types of architecture.&lt;/p&gt;
&lt;h3&gt;Headless Architecture&lt;/h3&gt;
&lt;p&gt;In headless architecture, the head of an application is detached from its body. In other words, you create a back-end application using Ruby, Python, Node.js, or another programming language. This application manages a connection with a database and provides computing power.&lt;/p&gt;
&lt;p&gt;The body is a front-end application created with a framework like React, Vue, or Angular.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/headless.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;CDN stands for content delivery network, a service designed to deliver assets faster for visitors worldwide. We can build the architecture in the cloud environment, or each setup piece can be a separate server.&lt;/p&gt;
&lt;p&gt;Choose headless architecture when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You aim for a large codebase.&lt;/strong&gt; If your application is going to be very large, the separate back-end and front-end layers will make it more maintainable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You expect different workloads for different system elements.&lt;/strong&gt; With a more modular approach, you can scale modules separately.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You foresee that you&amp;#39;ll change the technology in the future.&lt;/strong&gt; It is easier to adopt new technology with a headless architecture, as the communication, in most cases, will be the same between the back-end and front-end, regardless of the stack.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avoid a headless approach when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You want to develop an MVP version of an app very quickly.&lt;/strong&gt; You will likely start over again in the future and consider a different angle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You have a very small team.&lt;/strong&gt; Finding the right person to do the back-end, front-end, and DevOps stuff simultaneously might be hard.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You want to build a straightforward application.&lt;/strong&gt; Unfortunately, the headless approach increases your app&amp;#39;s complexity.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Monolith Architecture&lt;/h3&gt;
&lt;p&gt;One application handles the presentation layer (front) and computation layer (back) in a monolith architecture. Such an approach speeds up the application creation process and simplifies the server setup.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/monolith.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;As you&amp;#39;ll notice, in a monolith, we eliminate a part of the communication that&amp;#39;s in the headless approach. Such architecture improves the performance and security of an application.&lt;/p&gt;
&lt;p&gt;Choose this architecture when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You develop an MVP version of your app.&lt;/strong&gt; Frameworks like Ruby on Rails or Django make it easy and fast to build robust monolith applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You start with a small team.&lt;/strong&gt; Back-end developers can easily handle the front-end in such an architecture, and the deployment process is straightforward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Your application doesn’t have to depend on a very interactive front-end, and you don’t want to build a single-page application.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avoid the monolith approach if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You know that the front-end of the application will be huge.&lt;/strong&gt; It is harder to maintain this part as it&amp;#39;s connected directly with the back-end codebase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You know the front-end will have a completely different workload than the back-end.&lt;/strong&gt; As a result, you won’t scale certain elements of the monolith easily.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You may change the front-end or back-end tech stack in the future.&lt;/strong&gt; Such a transition would be pretty complicated with this architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Hybrid Architecture&lt;/h3&gt;
&lt;p&gt;An alternative approach to the monolith and headless architectures is a hybrid. You create a monolith application, but instead of using the front-end produced by the back-end framework, use the technology of headless applications.&lt;/p&gt;
&lt;p&gt;Yet this comes with a few disadvantages which you should consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The codebase is shared.&lt;/strong&gt; So as the application grows, it is harder to navigate through all the files and build a structure that is easy to understand.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scaling only the back-end or front-end is very difficult&lt;/strong&gt; as both parts connect in one application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Being agile is not easy.&lt;/strong&gt; With every change, you deploy the whole application, even if it’s just a change in the front-end style.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, you also get some benefits that you wouldn’t get with a standard monolith architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You can work separately on the back-end and front-end and update libraries independently.&lt;/strong&gt; Changing the stack of the front-end is easier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You can build a very interactive, single-page application&lt;/strong&gt; based on the monolith with a reasonably simple deployment process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You can be flexible.&lt;/strong&gt; It&amp;#39;s possible to use the front-end served by the JavaScript framework for some parts of your application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these pros and cons of hybrid architecture in mind, we will now go through the design process of a monolith application (created with Rails and a front-end React framework).&lt;/p&gt;
&lt;h2&gt;Designing a Monolith with React&lt;/h2&gt;
&lt;p&gt;There are three main things to keep in mind before you build a monolith application with a React front-end:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Front-end framework installation process&lt;/strong&gt; - there are a few ways of adding React to a Rails application. However, each has some limitations, so choosing the right one for your project is essential.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communication inside the application&lt;/strong&gt; - The back-end needs to expose data to the front-end, and the front needs to present this data. This element of the system needs to be designed carefully to make information exchange as smooth and fast as possible. As always, your choice should depend on the type of application you would like to build.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alternatives&lt;/strong&gt; - You can still go for the hybrid approach but not use React. We will show you other solutions that you can quickly adapt to build a more interactive monolith.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Install React in Your Ruby on Rails App&lt;/h2&gt;
&lt;p&gt;There are three main ways to install React in your Rails application.&lt;/p&gt;
&lt;h3&gt;Install React Using Ruby Gems&lt;/h3&gt;
&lt;p&gt;We can extend the functionality of our app by installing external libraries called Ruby gems. If you are unfamiliar with JavaScript, this is the fastest way to add React to Rails.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/reactjs/react-rails&quot;&gt;react-rails library&lt;/a&gt; is one of the most popular Ruby gems to integrate React with Rails. It provides generators for components, testing helpers, and view helpers to render JavaScript code inside the views.&lt;/p&gt;
&lt;p&gt;If you accept another layer of logic for your front-end, using this Ruby gem is the best way to get started with React quickly. However, remember that besides the JS framework updates, you also have to depend on the gem updates. The more gems you use, the more problematic the Rails upgrade process can become.&lt;/p&gt;
&lt;h3&gt;Install React Using Import Maps&lt;/h3&gt;
&lt;p&gt;Import maps were first presented in Rails 7. The lib lets you build modern JS applications using libraries made for ES modules without transpiling or bundling.&lt;/p&gt;
&lt;p&gt;Installing React with import maps is as easy as calling this command from the terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ ./bin/importmap pin react react-dom
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You don’t even need to host source files on your server, as they are served from JavaScript’s CDN by default. By applying the &lt;code&gt;--download&lt;/code&gt; flag, you can download the files to the vendor directory inside your application.&lt;/p&gt;
&lt;p&gt;However, you cannot use JSX (the most popular rendering extension in the React community) with import maps. HTM library is an alternative solution.&lt;/p&gt;
&lt;h3&gt;Install React Using Package Manager&lt;/h3&gt;
&lt;p&gt;This option seems to be the most flexible and optimal way to add React to a Rails project. You don’t create another level of abstraction for the front-end as you fetch the library directly. Updating React is also simpler.&lt;/p&gt;
&lt;p&gt;The most popular package managers in the Rails community are NPM and Yarn. You can add React libraries by invoking this command in the terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;yarn add react react-dom # or npm install react react-dom
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this installation method, you won’t get any Rails helpers to render JavaScript code. However, we&amp;#39;ll show you how to install and run React components using this approach shortly.&lt;/p&gt;
&lt;h2&gt;Communication Between Your Ruby on Rails App and React&lt;/h2&gt;
&lt;p&gt;Choosing the right way to exchange information between your Rails application and React components is essential. Selecting the wrong method can harm your application&amp;#39;s performance and make your code less maintainable.&lt;/p&gt;
&lt;p&gt;There are two common, standardized ways of exposing information from the back-end — REST and GraphQL. Let&amp;#39;s take a look at both options.&lt;/p&gt;
&lt;h3&gt;REST Communication&lt;/h3&gt;
&lt;p&gt;The REST API is a pretty simple way of exchanging information. We have different request types at our disposal to perform CRUD (create, retrieve, update, delete) operations. If you are familiar with the concept of resources in Rails, the REST API works the same way.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/rest.png&quot; alt=&quot;REST&quot;/&gt;&lt;/p&gt;
&lt;p&gt;You call the &lt;code&gt;/authors&lt;/code&gt; endpoint if you want to pull information about authors or a single author. You call the &lt;code&gt;/posts&lt;/code&gt; endpoint to do the same, but for posts. If a single page in your application requires a lot of different data, you will perform multiple HTTP requests.&lt;/p&gt;
&lt;p&gt;You should consider using REST in your application if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Your front-end application doesn’t need to render data from different sources on a single page.&lt;/strong&gt; On the other hand, this doesn’t have to mean that the application is very simple.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You don’t want to implement the cache mechanism on the application level.&lt;/strong&gt; Using REST API, you can benefit from the HTTP cache provided by browsers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You care about error reporting.&lt;/strong&gt; Implementing an error report with REST is easy as you work on different HTTP responses. With GraphQL, it’s not, as your application always returns a 200 response status.&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;GraphQL Communication&lt;/h3&gt;
&lt;p&gt;GraphQL represents an entirely different approach to data fetching. Using this technology, you perform one request and get only the data you need. You can hit multiple endpoints at once and pull only one attribute per endpoint.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-08/graphql.png&quot; alt=&quot;GraphQL&quot;/&gt;&lt;/p&gt;
&lt;p&gt;With GraphQL implemented on the back-end, you always call the same endpoint and modify the query parameter.&lt;/p&gt;
&lt;p&gt;You should consider using GraphQL in your application if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You are building a very interactive application&lt;/strong&gt; requiring the front-end to pull a lot of different data at once.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You want to build your application very quickly.&lt;/strong&gt; Building an API with REST is slower. With GraphQL, you get maximum flexibility, and all you need to care about in every step is the content of the query.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You are building an application where the API is used by multiple different applications&lt;/strong&gt; like mobile, front-end, or services.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Front-end Alternatives to React for Your Ruby on Rails App&lt;/h2&gt;
&lt;p&gt;If you don’t want to use React but like the idea of having a front-end application inside the Rails monolith, there are some alternative solutions available.&lt;/p&gt;
&lt;h3&gt;Rails Turbo&lt;/h3&gt;
&lt;p&gt;Turbo allows you to write single-page applications without the need for any JavaScript code. You design the page from independent frames. They behave like components and can also be lazy-loaded if needed.&lt;/p&gt;
&lt;p&gt;With Rails Turbo, you don’t spend time building the JSON responses from an API. You can simply reuse Rails views and render HTML — it’s automatically transferred and rendered in your components. This reduces the amount of code in your application.&lt;/p&gt;
&lt;p&gt;You should consider using Rails Turbo if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You don’t like writing JavaScript.&lt;/strong&gt; It is possible to design rich and interactive web applications without the need for a single line of JavaScript.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You like the Rails framework.&lt;/strong&gt; The Turbo library provides helper methods to quickly build interactive pages or transform existing ones into more dynamic forms.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You want to move fast.&lt;/strong&gt; As mentioned before, you don’t have to care about the JSON responses from an API. You can simply serve HTML views and the Turbo library will automatically pull them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Stimulus JS&lt;/h3&gt;
&lt;p&gt;Stimulus is a JavaScript framework created by the Rails team. It was designed to extend the HTML that you already have. The main concept of this framework is the controller — one controller per view.&lt;/p&gt;
&lt;p&gt;Your Rails application will automatically load the JavaScript controller for the given view and map the HTML elements. You can respond to events or modify the view when you need to.&lt;/p&gt;
&lt;p&gt;You should consider using Stimulus if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You are already using Rails Turbo, but want more control over a page&amp;#39;s granular elements.&lt;/li&gt;
&lt;li&gt;You don’t want to build a single-page application but still want to make some page elements interactive or dynamic.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Other JavaScript Frameworks&lt;/h3&gt;
&lt;p&gt;Besides React, other interesting JavaScript frameworks on the market right now include Vue, Svelte, and Ember.js. So if you like writing JavaScript components, you can choose one of these.&lt;/p&gt;
&lt;p&gt;Rails Turbo and/or Stimulus JS are the perfect choice if you&amp;#39;re a Rails developer who&amp;#39;s familiar with JavaScript but doesn’t want to write much of it. Both libraries heavily follow the principles of the Rails framework, so if you already work in this ecosystem, it will be easier for you to use these extensions.&lt;/p&gt;
&lt;h2&gt;Build a Monolith with React&lt;/h2&gt;
&lt;p&gt;It’s time to build a simple Rails application to manage a list of books we want to read. Let&amp;#39;s start with generating the Rails skeleton.&lt;/p&gt;
&lt;h3&gt;Ruby on Rails Project Bootstrap&lt;/h3&gt;
&lt;p&gt;Ensure you have the latest version of Ruby on Rails installed locally on your operating system. Then generate the skeleton using the &lt;code&gt;rails new&lt;/code&gt; command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails new booklist -d postgresql -j esbuild
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above command will generate a new Rails project in the &lt;code&gt;booklist&lt;/code&gt; directory, with support for a PostgreSQL database. We can now enter the project’s directory, create the database, and run the server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;cd booklist/
./bin/rails db:create
rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Visit the localhost:3000 address in your browser to see the default Rails welcome screen.&lt;/p&gt;
&lt;p&gt;Now generate a home controller for a single endpoint in the application so we can attach the React application to the layout. Run this command in the terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;./bin/rails g controller Home index
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last step is to update &lt;code&gt;config/routes.rb&lt;/code&gt; file to set the root path:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.routes.draw do
  root &amp;quot;home#index&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now focus on the front-end part with React.&lt;/p&gt;
&lt;h3&gt;React Installation&lt;/h3&gt;
&lt;p&gt;We will install the &lt;code&gt;react&lt;/code&gt;, &lt;code&gt;react-dom&lt;/code&gt;, and &lt;code&gt;node-uuid&lt;/code&gt; libraries using yarn in our terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;yarn add react react-dom node-uuid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To properly handle JSX, we also have to update our build script. First, open the &lt;code&gt;package.json&lt;/code&gt; file, and in the &lt;code&gt;scripts&lt;/code&gt; section, ensure that the &lt;code&gt;build&lt;/code&gt; command is the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets --loader:.js=jsx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now run &lt;code&gt;./bin/dev&lt;/code&gt; to kickstart both &lt;code&gt;yarn build&lt;/code&gt; and the rails server or run them separately. For example, if you want processes in separate terminal tabs, run &lt;code&gt;rails s&lt;/code&gt; in one and &lt;code&gt;yarn build –watch&lt;/code&gt; in the second.&lt;/p&gt;
&lt;h3&gt;Render a React Application in a Rails Layout&lt;/h3&gt;
&lt;p&gt;We want to run the React application when a user enters the main root in our Rails application. To do this, update the &lt;code&gt;app/views/layout.html.erb&lt;/code&gt; file, so that the &lt;code&gt;body&lt;/code&gt; section looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;body&amp;gt;
  &amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The React application will render the component with JavaScript logic inside the &lt;code&gt;app&lt;/code&gt; div.&lt;/p&gt;
&lt;p&gt;Let’s now create the entry point for the React application. Open the &lt;code&gt;app/javascript/application.js&lt;/code&gt; file and write the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import React from &amp;quot;react&amp;quot;;
import { createRoot } from &amp;quot;react-dom/client&amp;quot;;
import App from &amp;quot;./App&amp;quot;;

const container = document.getElementById(&amp;quot;app&amp;quot;);
const root = createRoot(container);
root.render(&amp;lt;App /&amp;gt;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have the container element, but we are missing the &lt;code&gt;App&lt;/code&gt; component so the application will throw an error. Let’s create the &lt;code&gt;App&lt;/code&gt; component then.&lt;/p&gt;
&lt;h2&gt;Writing a React Component&lt;/h2&gt;
&lt;p&gt;We won’t create a separate directory for components. Let&amp;#39;s place our tiny application in one component for demonstration purposes.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: When writing a production app, always design the React app using multiple components and follow the best design principles.&lt;/p&gt;
&lt;p&gt;Create a file &lt;code&gt;App.js&lt;/code&gt; in the &lt;code&gt;app/javascript&lt;/code&gt; directory and place the application skeleton there, with the header as a placeholder:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import React, { useState } from &amp;quot;react&amp;quot;;

export default function App() {
  const [books, setBooks] = useState([]);

  return (
    &amp;lt;&amp;gt;
      &amp;lt;h1&amp;gt;Books {books.length}&amp;lt;/h1&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Rendering a Book List&lt;/h3&gt;
&lt;p&gt;Now it’s time to render a simple list of books. To do this, we have to update the &lt;code&gt;render&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;return (
  &amp;lt;&amp;gt;
    &amp;lt;h1&amp;gt;Books {books.length}&amp;lt;/h1&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;table&amp;gt;
        &amp;lt;thead&amp;gt;
          &amp;lt;tr&amp;gt;
            &amp;lt;th&amp;gt;Title&amp;lt;/th&amp;gt;
            &amp;lt;th&amp;gt;Author&amp;lt;/th&amp;gt;
          &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;
          {books &amp;amp;&amp;amp;
            books.map(({ id, title, author }, i) =&amp;gt; (
              &amp;lt;tr key={id}&amp;gt;
                &amp;lt;td&amp;gt;{title}&amp;lt;/td&amp;gt;
                &amp;lt;td&amp;gt;{author}&amp;lt;/td&amp;gt;
              &amp;lt;/tr&amp;gt;
            ))}
        &amp;lt;/tbody&amp;gt;
      &amp;lt;/table&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Create a New Book&lt;/h3&gt;
&lt;p&gt;We don’t have anything we can render. Let’s add the form to create a new book so we can fill our list with some data that we can later edit and delete if needed.&lt;/p&gt;
&lt;p&gt;However, before we do this, we have to update our component to store some more information:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { default as UUID } from &amp;quot;node-uuid&amp;quot;;

const [action, setAction] = useState(&amp;quot;list&amp;quot;);
const [formData, setFormData] = useState({ title: &amp;quot;&amp;quot;, author: &amp;quot;&amp;quot; });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m not going to create a separate component for the form, so I have to use a conditional in the render function. Thanks to the &lt;code&gt;action&lt;/code&gt;, I can tell when to render the list or form.&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;render&lt;/code&gt; function is the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;return (
  &amp;lt;&amp;gt;
    &amp;lt;h1&amp;gt;Books {books.length}&amp;lt;/h1&amp;gt;
    {action == &amp;quot;list&amp;quot; ? (
      &amp;lt;div&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; setAction(&amp;quot;form&amp;quot;)}&amp;gt;New book&amp;lt;/button&amp;gt;
        &amp;lt;table&amp;gt;
          &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;th&amp;gt;Title&amp;lt;/th&amp;gt;
              &amp;lt;th&amp;gt;Author&amp;lt;/th&amp;gt;
              &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
          &amp;lt;/thead&amp;gt;
          &amp;lt;tbody&amp;gt;
            {books &amp;amp;&amp;amp;
              books.map(({ id, title, author }, i) =&amp;gt; (
                &amp;lt;tr key={id}&amp;gt;
                  &amp;lt;td&amp;gt;{title}&amp;lt;/td&amp;gt;
                  &amp;lt;td&amp;gt;{author}&amp;lt;/td&amp;gt;
                  &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;
              ))}
          &amp;lt;/tbody&amp;gt;
        &amp;lt;/table&amp;gt;
      &amp;lt;/div&amp;gt;
    ) : (
      &amp;lt;div&amp;gt;
        &amp;lt;form&amp;gt;
          &amp;lt;label&amp;gt;Title:&amp;lt;/label&amp;gt;
          &amp;lt;input
            onChange={(e) =&amp;gt;
              setFormData({ ...formData, title: e.target.value })
            }
            name=&amp;quot;title&amp;quot;
            value={formData.title}
          /&amp;gt;
          &amp;lt;label&amp;gt;Author:&amp;lt;/label&amp;gt;
          &amp;lt;input
            onChange={(e) =&amp;gt;
              setFormData({ ...formData, author: e.target.value })
            }
            name=&amp;quot;author&amp;quot;
            value={formData.author}
          /&amp;gt;
          &amp;lt;button onClick={(e) =&amp;gt; saveBook(e)}&amp;gt;Submit&amp;lt;/button&amp;gt;
          &amp;lt;button onClick={() =&amp;gt; setAction(&amp;quot;list&amp;quot;)}&amp;gt;Back&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/div&amp;gt;
    )}
  &amp;lt;/&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each time you type something into the title or author input, &lt;code&gt;formData&lt;/code&gt; will update with values from the form. The last missing piece is the &lt;code&gt;saveBook&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const saveBook = (e) =&amp;gt; {
  e.preventDefault();

  setBooks([...books, { ...formData, id: UUID.v4() }]);
  setFormData({ title: &amp;quot;&amp;quot;, author: &amp;quot;&amp;quot;, id: &amp;quot;&amp;quot; });
  setAction(&amp;quot;list&amp;quot;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first step is to prevent a form submission — we don’t want to reload the page. Then we update the books collection and reset the form data. The last step is to render back the list view.&lt;/p&gt;
&lt;h3&gt;Update an Existing Book&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s reuse the form we created in the previous step. We also have to store the id of the book we are currently editing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const [currentBookId, setCurrentBookId] = useState(null);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;editBook&lt;/code&gt; function will set the id of the book we want to edit. Fill the form with values and render the form view:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const editBook = (id) =&amp;gt; {
  const currentBook = books.find((book) =&amp;gt; book.id == id);
  setCurrentBookId(id);
  setFormData({
    ...formData,
    title: currentBook.title,
    author: currentBook.author,
  });
  setAction(&amp;quot;form&amp;quot;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we are going to use the &lt;code&gt;saveBook&lt;/code&gt; function for a creation and update action, we have to modify it accordingly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const saveBook = async (e) =&amp;gt; {
  e.preventDefault();

  if (currentBookId) {
    bookIndex = books.findIndex((book) =&amp;gt; book.id == currentBookId);
    updatedBooks = [...books];
    updatedBooks[bookIndex] = formData;
    setBooks(updatedBooks);
    setCurrentBookId(null);
  } else {
    setBooks([...books, { ...formData, id: UUID.v4() }]);
  }

  setFormData({ title: &amp;quot;&amp;quot;, author: &amp;quot;&amp;quot;, id: &amp;quot;&amp;quot; });
  setAction(&amp;quot;list&amp;quot;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Delete a Single Book&lt;/h3&gt;
&lt;p&gt;Let’s start with building the simple code for destroying the book:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const deleteBook = (id) =&amp;gt; {
  setBooks(books.filter((book) =&amp;gt; book.id != id));
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We get the array of books that aren&amp;#39;t deleted and update the &lt;code&gt;books&lt;/code&gt; collection. React will update our component automatically.&lt;/p&gt;
&lt;h3&gt;The Complete Component&lt;/h3&gt;
&lt;p&gt;We implemented all the actions needed to create, update, list, and destroy a book. &lt;a href=&quot;https://gist.github.com/pdabrowski6/ac2c04a71f8c98e6ee386d2a3c3807cd&quot;&gt;View the code for the complete component in this Gist&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Recap&lt;/h2&gt;
&lt;p&gt;Congratulations, you created a React application inside a Rails monolith! Let’s summarize what we have learned and done during this article.&lt;/p&gt;
&lt;h3&gt;Different Types of Architecture for Web Apps&lt;/h3&gt;
&lt;p&gt;When you build a web application, it has a front and back part. So you need to decide how you want to connect these two sides. Here&amp;#39;s a reminder of the most common types of architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Headless architecture&lt;/strong&gt; - The front-end application is separate from the back-end. Choose it if you expect to write a lot of code, want to change technologies in the future, or expect a different workload for the front-end and back-end. However, keep in mind that such architecture increases the complexity of an application, takes more time to build, and the development process might be complex for a small development team.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monolith architecture&lt;/strong&gt; - One application that handles both front-end and back-end. You can build one with popular frameworks like Ruby on Rails or Django. With this architecture, you can build fast with a small team. However, you can run into trouble in the future if you want to change the technology or scale only part of the application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid architecture&lt;/strong&gt; - A compromise between a monolith and headless architecture. You build one codebase, but the front-end is served by a different technology from the back-end. The codebase might be hard to navigate, and it will be challenging to scale only one piece of the system. However, it is more flexible than a monolith. If you don’t like monoliths, but don’t want to fully detach the front from the back-end, this solution is for you.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The Design Process to Build Hybrid Architecture&lt;/h3&gt;
&lt;p&gt;Don’t jump into coding straight away. Think about the specifications of your application. When building a Rails application that uses a React framework for the front-end, you have to consider the following things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Installation process&lt;/strong&gt; - You can install React using import maps if you don’t need to use JSX templates. Otherwise, you can select package managers like Yarn or NPM, or install React using a Ruby gem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communication between Rails and React&lt;/strong&gt; - Both applications are inside the same codebase, but need a bridge to exchange information. Use REST API for simpler applications and GraphQL for more complex interfaces where a lot of different data is necessary for the view layer.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Alternatives to React for a Rails App&lt;/h3&gt;
&lt;p&gt;If you decide that you don’t want to use React, but you like the idea of hybrid architecture, you can consider the following alternative solutions for the front-end:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rails Turbo&lt;/strong&gt; - With this library, you can split the page into independent frames and update them when needed. Go for this option if you like the Rails framework, and don’t want to write a lot of JavaScript code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stimulus JS&lt;/strong&gt; - A small library for the HTML you already have in your views. Choose it when you are happy with your Rails monolith, but you want to make some views more interactive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Other JavaScript frameworks&lt;/strong&gt; - If you want to write more JavaScript, you can use Vue.js, Svelte, or Ember.js, instead of React. You can easily incorporate these solutions into your Rails application.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summing Up&lt;/h2&gt;
&lt;p&gt;In this post, we explored the three main types of architecture — headless, monolith, and hybrid — and looked at their pros and cons. We then created a React application inside a Rails monolith.&lt;/p&gt;
&lt;p&gt;Now it should hopefully be easier for you to choose the right architecture and tools for your next project.&lt;/p&gt;
&lt;p&gt;Until next time, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Track Down Memory Leaks in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/07/27/how-to-track-down-memory-leaks-in-ruby.html"/>
    <id>https://blog.appsignal.com/2022/07/27/how-to-track-down-memory-leaks-in-ruby.html</id>
    <published>2022-07-27T00:00:00+00:00</published>
    <updated>2022-07-27T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore how Ruby manages memory, garbage collection, and how to uncover a leak, in the first of this two-part series on memory leaks in Ruby.</summary>
    <content type="html">&lt;p&gt;A memory leak is an unintentional, uncontrolled, and unending increase in
memory usage. No matter how small, eventually, a leak will cause your process
to run out of memory and crash. Even if you periodically restart your app to
avoid this crash (no judgment, I&amp;#39;ve done that!), you still suffer the
performance implications of a memory leak.&lt;/p&gt;
&lt;p&gt;In this post, the first of a two-part series on memory leaks, we&amp;#39;ll start by looking at how Ruby manages memory, how Garbage Collection (GC) works, and how to find a leak.&lt;/p&gt;
&lt;p&gt;In the second part, we&amp;#39;ll take a deeper dive into tracking down leaks.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Ruby Memory Management&lt;/h2&gt;
&lt;p&gt;Ruby objects are stored on the heap, and each object fills one slot on the
heap.&lt;/p&gt;
&lt;p&gt;Prior to Ruby 3.1, all slots on the heap were the same size — 40 bytes,
to be exact. Objects too large to fit in a slot were stored outside
the heap. Each slot included a reference to where objects were moved.&lt;/p&gt;
&lt;p&gt;In Ruby 3.1,
&lt;a href=&quot;https://bugs.ruby-lang.org/issues/17816&quot;&gt;variable width allocation&lt;/a&gt; for &lt;code&gt;String&lt;/code&gt;
objects was merged. Soon, variable width allocation will be the norm for all
object types.&lt;/p&gt;
&lt;p&gt;Variable width allocation aims to improve performance by improving cache
locality — all the information of an object will be stored in one place
rather than across two memory locations.&lt;/p&gt;
&lt;p&gt;It should also simplify (some parts) of memory management. At the moment,
there are two &amp;#39;heaps&amp;#39;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Ruby heap (or GC heap) that stores smaller Ruby objects.&lt;/li&gt;
&lt;li&gt;The C heap (or malloc/transient heap) that stores larger objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once variable width allocation is the norm, there should be no need
for the latter heap.&lt;/p&gt;
&lt;p&gt;The heap starts at a given size (10,000 slots by default) and objects are
assigned to free slots as they are created. When Ruby tries to create an object
and there are no free slots available, Garbage Collection (GC) occurs to make
some free slots available.&lt;/p&gt;
&lt;p&gt;If there are too few free slots after GC, the
heap will be expanded (more on this a little later).&lt;/p&gt;
&lt;p&gt;Here are the factors you can control, alongside their &lt;a href=&quot;https://docs.ruby-lang.org/ja/latest/class/GC.html&quot;&gt;environment variables&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initial size of the heap - &lt;code&gt;RUBY_GC_HEAP_INIT_SLOTS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Number of free slots that should be available after GC occurs - &lt;code&gt;RUBY_GC_HEAP_FREE_SLOTS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Amount the heap is expanded by - &lt;code&gt;RUBY_GC_HEAP_GROWTH_FACTOR&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Garbage Collection in Ruby&lt;/h2&gt;
&lt;p&gt;Garbage Collection in Ruby &amp;#39;stops the world&amp;#39; — no other
process occurs when GC occurs. Garbage Collection in Ruby
(since 2.1) is also &lt;em&gt;generational&lt;/em&gt;, meaning that the garbage collector has two
modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Minor GC&lt;/strong&gt; - inspects &amp;#39;young&amp;#39; objects (objects created recently)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Major GC&lt;/strong&gt; - inspects &amp;#39;old&amp;#39; objects as well as &amp;#39;young&amp;#39; objects (&lt;em&gt;all&lt;/em&gt; the
objects)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: An &amp;#39;old&amp;#39; object has survived &lt;code&gt;3&lt;/code&gt; GC runs, major or minor.&lt;/p&gt;
&lt;p&gt;When the heap is full, minor GC is invoked first. If it can&amp;#39;t free up enough
slots to be below the limit, major GC will be invoked. Only then, if there are
still not enough free slots, will the heap be expanded.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Major GC is more expensive than minor GC because it looks at more objects.&lt;/p&gt;
&lt;p&gt;The theory behind why generational GC is more performant is that objects
usually fall into two categories:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Objects that are allocated and then quickly go out of scope.&lt;/strong&gt; In a Rails app,
models fetched from the DB to render a page will go out of scope when the
request ends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Objects that are allocated and kept around for a long time.&lt;/strong&gt; Classes and
caches are likely to still be in use throughout the lifetime of an app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Major GC will also run after minor GC if the number of old objects is above a
certain threshold, even if there are sufficient free slots. This limit
increases as the size of the heap grows and can be controlled by the
&lt;code&gt;RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;p&gt;When you have a leak, you create objects that can&amp;#39;t be cleaned up — more and more &lt;code&gt;old&lt;/code&gt; objects. This means that major (expensive) GC
will run much more often than it should. Since nothing else runs when GC is running, this is time that you waste.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ve left some links at the end of this article for further reading on memory
layout and the garbage collector in Ruby.&lt;/p&gt;
&lt;h2&gt;What Does A Memory Leak Look Like in Ruby?&lt;/h2&gt;
&lt;p&gt;You can see a memory leak using simple tools available on any
Unix system. Take the following code as an example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# leaky.rb

an_array = []

loop do
  1000.times { an_array &amp;lt;&amp;lt; &amp;quot;A&amp;quot; + &amp;quot;B&amp;quot; + &amp;quot;C&amp;quot; }
  puts an_array.size
  sleep 1
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To say this code &amp;#39;leaks&amp;#39; is a little unfair — all it does is leak! —
but it serves our purposes.&lt;/p&gt;
&lt;p&gt;We can observe the leak quite simply from the command line by running this
program in one terminal and &lt;code&gt;watch&lt;/code&gt;-ing the memory increase over time with
&lt;code&gt;ps&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# In terminal one
$ ruby ./leaky.rb

# In terminal two
$ watch ps -p `pgrep -f &amp;quot;ruby ./leaky.rb&amp;quot;` -o pmem,pcpu,rss,args
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;pgrep -f &amp;quot;ruby ./leaky.rb&amp;quot;&lt;/code&gt; finds the process ID for us, so that we can restrict the
&lt;code&gt;ps&lt;/code&gt; output to only the process we&amp;#39;re interested in. As you may be able to
guess, it&amp;#39;s like &lt;code&gt;grep&lt;/code&gt; for processes.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;watch&lt;/code&gt; tool allows us to poll the
output of a given command and update it in place, giving us a live dashboard
within our terminal.&lt;/p&gt;
&lt;p&gt;You&amp;#39;ll get output like this, which updates every couple of seconds.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Every 2.0s: ps -p 50866 -o pmem,pcpu,rss,args

%MEM  %CPU    RSS ARGS
 0.2   4.1 163408 /Users/tonyrowan/.asdf/installs/ruby/3.1.1/bin/ruby ./leaky.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the &lt;code&gt;%MEM&lt;/code&gt; and &lt;code&gt;RSS&lt;/code&gt; increasing. They are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;%MEM&lt;/code&gt;&lt;/strong&gt; - The amount of memory the process uses as a percentage of
memory on the host machine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;RSS&lt;/code&gt;&lt;/strong&gt; (resident set size) - The amount of RAM the process uses in bytes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This basic OS-only information is enough to spot if you have a leak — if
the memory keeps going up, it means you do!&lt;/p&gt;
&lt;h2&gt;Find Ruby Leaks with the Garbage Collector Module&lt;/h2&gt;
&lt;p&gt;We can also detect leaks within Ruby code itself with the &lt;code&gt;GC&lt;/code&gt; module.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# leaky.rb

GC.disable # Only run GC when manually called

an_array = []

loop do
  1000.times { an_array &amp;lt;&amp;lt; &amp;quot;A&amp;quot; + &amp;quot;B&amp;quot; + &amp;quot;C&amp;quot; }
  puts &amp;quot;Array is #{an_array.size} items long&amp;quot;

  GC.start # Run a major GC - use full_mark: false for minor GC
  puts &amp;quot;There are #{GC.stat(:heap_live_slots)} live objects&amp;quot;

  sleep 1
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;GC.stat&lt;/code&gt; method will return a hash with a lot of useful information. Here,
we&amp;#39;re interested in &lt;code&gt;:heap_live_slots&lt;/code&gt;, which is the number of slots on the
heap that are in use. That&amp;#39;s the opposite of &lt;code&gt;:heap_free_slots&lt;/code&gt;.
At the end of the loop, we force a major GC and print out the number of used
slots, i.e., the number of objects that remain after GC.&lt;/p&gt;
&lt;p&gt;When we run our little program, we see this increase ad infinitum.
We have a leak! We could also have used &lt;code&gt;GC.stat(:old_objects)&lt;/code&gt; to the same
effect.&lt;/p&gt;
&lt;p&gt;While the &lt;code&gt;GC&lt;/code&gt; module can be used to see &lt;em&gt;if&lt;/em&gt; we have a leak and (if you&amp;#39;re
smart with your &lt;code&gt;puts&lt;/code&gt; statements) where the leak might be occurring, we can see
the type of objects that might be leaking with the &lt;code&gt;ObjectSpace&lt;/code&gt; module.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# leaky.rb

GC.disable # Only run GC when manually called

an_array = []

loop do
  1000.times { an_array &amp;lt;&amp;lt; &amp;quot;A&amp;quot; + &amp;quot;B&amp;quot; + &amp;quot;C&amp;quot; }
  puts &amp;quot;Array is #{an_array.size} items long&amp;quot;

  GC.start # Run a major GC - use full_mark: false for minor GC
  pp ObjectSpace.count_objects

  sleep 1
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ObjectSpace.count_objects&lt;/code&gt; method returns a hash with the counts of
live objects. &lt;code&gt;T_STRING&lt;/code&gt;, for instance, is the number of strings live in
memory. For our rather leaky program, this value increases with each loop,
even after GC. We can see that we are leaking string objects.&lt;/p&gt;
&lt;h2&gt;Application Performance Monitoring in Production with AppSignal&lt;/h2&gt;
&lt;p&gt;While playing with &lt;code&gt;ps&lt;/code&gt; and &lt;code&gt;GC&lt;/code&gt; can be a sensible route for toy projects —
they&amp;#39;re also fun and informative to use! — I would &lt;em&gt;not&lt;/em&gt; recommend them as your
memory leak detection solution in production apps.&lt;/p&gt;
&lt;p&gt;This is where you would
use an Application Performance Monitoring (APM) tool. If you&amp;#39;re a very large company,
you can build these yourself. For smaller outfits, though, picking an APM off-the-shelf is the way to go. You do need to pay a monthly subscription, but the information they provide
more than makes up for it.&lt;/p&gt;
&lt;p&gt;For detecting memory leaks, you want to find server or process memory use
(sometimes called RSS) graphs over time. Here&amp;#39;s an example screenshot from
AppSignal&amp;#39;s &amp;#39;process memory usage&amp;#39; dashboard of a healthy app shortly after being deployed:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-07/healthy-app-rss-appsignal.png&quot; alt=&quot;Healthy App RSS&quot;/&gt;&lt;/p&gt;
&lt;p&gt;And here&amp;#39;s an unhealthy app after deployment:
&lt;img src=&quot;/images/blog/2022-07/unhealthy-app-rss-appsignal.png&quot; alt=&quot;Unhealthy App RSS&quot;/&gt;&lt;/p&gt;
&lt;p&gt;AppSignal will even surface Ruby VM stats like GC and heap slots, which can
give you an even clearer signal for a memory leak. If the number of live slots
keeps growing, you have a leak!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-07/heap-slots.png&quot; alt=&quot;Heap Slots showing leak&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;Read more about AppSignal for Ruby&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrap Up and Further Reading&lt;/h2&gt;
&lt;p&gt;In this post, we took a quick tour of Ruby&amp;#39;s memory management and garbage collector. We then diagnosed how to discover a memory leak using Unix tools and Ruby&amp;#39;s GC module.&lt;/p&gt;
&lt;p&gt;Next time, we&amp;#39;ll see how to use &lt;code&gt;memory_profiler&lt;/code&gt; and &lt;code&gt;derailed_benchmarks&lt;/code&gt; to find and fix leaks.&lt;/p&gt;
&lt;p&gt;In the meantime, you can read more about the tools we used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://linux.die.net/man/1/watch&quot;&gt;&lt;code&gt;watch&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://linux.die.net/man/1/ps&quot;&gt;&lt;code&gt;ps&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://linux.die.net/man/1/pgrep&quot;&gt;&lt;code&gt;pgrep&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additional further reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ruby-doc.org/core-3.1.2/GC.html&quot;&gt;&lt;code&gt;GC&lt;/code&gt; module documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ruby-doc.org/core-3.1.2/ObjectSpace.html&quot;&gt;&lt;code&gt;ObjectSpace&lt;/code&gt; module documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jemma.dev/blog/ruby-garbage-collection-deep-dive/&quot;&gt;Garbage Collection Deep Dive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rubykaigi.org/2021-takeout/presentations/peterzhu2118.html&quot;&gt;Variable Width Allocation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy coding, and see you next time!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Deploy Your Ruby on Rails App Using Capistrano</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/07/13/deploy-your-ruby-on-rails-app-using-capistrano.html"/>
    <id>https://blog.appsignal.com/2022/07/13/deploy-your-ruby-on-rails-app-using-capistrano.html</id>
    <published>2022-07-13T00:00:00+00:00</published>
    <updated>2022-07-13T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out how to configure Capistrano in your Rails app, then deploy the app.</summary>
    <content type="html">&lt;p&gt;In this article, we will configure Capistrano in a Ruby on Rails application. We will then deploy the app to a cloud instance that runs Ubuntu as an operating system, independent of your hosting provider. You can use any cloud service, or even an on-premises server, to test or replicate the steps we&amp;#39;ll take.&lt;/p&gt;
&lt;p&gt;Once we&amp;#39;ve deployed the app, we&amp;#39;ll look briefly at how you can monitor your app&amp;#39;s deployments using AppSignal.&lt;/p&gt;
&lt;p&gt;But first, you might ask: why should I use Capistrano in the first place?&lt;/p&gt;
&lt;h2&gt;Why Choose Capistrano for Your Ruby on Rails App?&lt;/h2&gt;
&lt;p&gt;You might be wondering if it still makes sense to continue using Capistrano for deployment these days. We now have many tools and services available for deployments and continuous integration.&lt;/p&gt;
&lt;p&gt;But to answer that, we need to revisit the origin of Capistrano and its evolution to the present day. The first tag created in the Capistrano repository was in 2006. Its objective was to allow the execution of commands remotely via SSH in parallel on several machines, all using DSL. Since then, Capistrano has evolved, by adding several features to each version.&lt;/p&gt;
&lt;p&gt;Currently at version 3 after 16 years, Capistrano continues to be actively developed and is now known as a framework for building automated deployment scripts.&lt;/p&gt;
&lt;p&gt;So it&amp;#39;s no longer just a tool to run commands on remote machines. In addition to &lt;a href=&quot;https://capistranorb.com/documentation/plugins&quot;&gt;Capistrano&amp;#39;s features&lt;/a&gt;, many &lt;a href=&quot;https://capistranorb.com/documentation/third-party-plugins&quot;&gt;plugins&lt;/a&gt; and other gems specifically run with Capistrano.&lt;/p&gt;
&lt;p&gt;Because of this and the fact that a large community still uses Capistrano, it remains a great tool for automated deployment.&lt;/p&gt;
&lt;h2&gt;Adding Capistrano to Your Ruby on Rails App&lt;/h2&gt;
&lt;p&gt;First, you&amp;#39;ll need to configure a Ruby on Rails application to integrate it with Capistrano. You can follow &lt;a href=&quot;https://capistranorb.com/documentation/getting-started/installation&quot;&gt;Capistrano&amp;#39;s documentation&lt;/a&gt;. However, it has more information than you&amp;#39;ll need for the scope of this post. So for simplicity&amp;#39;s sake, this article only includes the necessary steps and configuration that you will need.&lt;/p&gt;
&lt;p&gt;To start the configuration, add the Capistrano gem in the development section of your Gemfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;group :development do
  # Deployment
  gem &amp;quot;capistrano&amp;quot;, &amp;quot;~&amp;gt; 3.10&amp;quot;, require: false
  gem &amp;quot;capistrano-rails&amp;quot;, &amp;quot;~&amp;gt; 1.3&amp;quot;, require: false
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You need the capistrano-rails gem to run a migration on your database and handle the assets that we will see in the next sections.&lt;/p&gt;
&lt;p&gt;Now run &lt;code&gt;bundle install&lt;/code&gt; to add Capistrano to your application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can install Capistrano:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec cap install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will create a file called &lt;code&gt;Capfile&lt;/code&gt; that imports and configures the required libraries. Furthermore, a file called &lt;code&gt;deploy.rb&lt;/code&gt; (created inside the &lt;code&gt;config&lt;/code&gt; folder) will contain all the configurations that should be included in a deployment with Capistrano.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Capistrano creates a file to override the &lt;code&gt;deploy.rb&lt;/code&gt; configuration for both the staging and production environments, but that will not be covered in this article.&lt;/p&gt;
&lt;p&gt;With that, you&amp;#39;re ready to start configuring and communicating between your Ruby on Rails application and the server.&lt;/p&gt;
&lt;h2&gt;Server Configuration with Capistrano for Your Rails App&lt;/h2&gt;
&lt;p&gt;In this step, we&amp;#39;ll add instructions to the Capistrano configuration file. The instructions will communicate to your server on where your app will be deployed. This is where we&amp;#39;ll define access information and the security configuration.&lt;/p&gt;
&lt;p&gt;To ensure that your production environment runs the same version of Ruby as the version you use in development, use a Ruby version manager, such as &lt;em&gt;RVM&lt;/em&gt; or &lt;em&gt;rbenv&lt;/em&gt; (Capistrano supports both).&lt;/p&gt;
&lt;p&gt;In this article, we&amp;#39;ll use RVM. Make sure you have RVM installed on your development machine and production server. Also, check if your version of Ruby is the same in your environments and project.&lt;/p&gt;
&lt;h3&gt;Adding &lt;code&gt;capistrano-rvm&lt;/code&gt; Gem&lt;/h3&gt;
&lt;p&gt;The Capistrano project provides a gem called &lt;em&gt;capistrano-gem&lt;/em&gt; that lets you easily configure RVM. To include it in your project, add it to your Gemfile within your development group, and run &lt;code&gt;bundle install&lt;/code&gt; again.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;quot;capistrano-rvm&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update your Capfile with the capistrano-rvm import:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Capfile
require &amp;#39;capistrano/rvm&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the deploy file, add a configuration to set the Ruby version and the path to RVM (especially if you used a different path to install RVM).&lt;/p&gt;
&lt;p&gt;If no version of Ruby is defined, Capistrano chooses the latest version that you have installed on the server where your application will be deployed.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/deploy.rb
set :rvm_ruby_version, &amp;quot;ruby-2.6.3&amp;quot;
set :default_env, { rvm_bin_path: &amp;quot;~/.rvm/bin&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/capistrano/rvm&quot;&gt;Capistrano::RVM&lt;/a&gt; has other settings for RVM, but the ones shown here are what you&amp;#39;ll need to get started.&lt;/p&gt;
&lt;h3&gt;Set Up Your Server Configuration&lt;/h3&gt;
&lt;p&gt;It is good practice to define a user for the deployment. Don&amp;#39;t forget to follow these steps on your server before you start a deployment:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the public key generated on your local machine to the &lt;code&gt;~/home/YOUR_USER/.ssh/authorized_keys&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Create a key and include the public key in the code repository. This gives you access to clone your project within the production server. If you are using GitHub, you can &lt;a href=&quot;https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent&quot;&gt;follow this documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Set permissions to write to the &lt;code&gt;/var/www/&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can configure Capistrano for any environment you need. You just need to create a new configuration file inside &lt;code&gt;config/deploy/&amp;lt;environment_name&amp;gt;.rb&lt;/code&gt;, then configure the IP or DNS, and username, to connect to your server.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/deploy/production.rb

server &amp;quot;11.22.333.444&amp;quot;, user: &amp;quot;ubuntu&amp;quot;, roles: %w{app db web}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To complete the deployment setup, include information about your application, such as the application name and code repository URL. Any information which is the same for all environments must be added to &lt;code&gt;config/deploy.rb&lt;/code&gt;. Keep specific environment configurations in &lt;code&gt;config/deploy/&amp;lt;environment_name&amp;gt;.rb&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/deploy.rb

set :application, &amp;quot;ruby_rails_app&amp;quot;
set :repo_url, &amp;quot;git@github.com:username/ruby_rails_app.git&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Set Up &lt;code&gt;capistrano-secrets-yml&lt;/code&gt; Gem for Your Rails App&lt;/h3&gt;
&lt;p&gt;You should also set up the &lt;a href=&quot;https://github.com/capistrano-plugins/capistrano-secrets-yml&quot;&gt;capistrano-secrets-yml&lt;/a&gt; gem.&lt;/p&gt;
&lt;p&gt;Add the gem to your Gemfile and run the bundle to install:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile
gem &amp;quot;capistrano-secrets-yml&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Import it into Capfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Capfile
require &amp;quot;capistrano/secrets_yml&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create &lt;code&gt;config/secret.yml&lt;/code&gt; in your application and include the secret key base as an environment variable. This variable will be created on your production server. Remember &lt;em&gt;not&lt;/em&gt; to commit this file to your repository.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/secrets.yml
production:
  secret_key_base: &amp;lt;%= ENV[&amp;quot;SECRET_KEY_BASE&amp;quot;] %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside your application folder in the terminal, run this command to generate the secret key base:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ rails secret
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy the generated key and use it to set your SECRET_KEY_BASE on the production server. Access your production server and export the SECRET_KEY_BASE variable in the final &lt;code&gt;~/.bashrc&lt;/code&gt; file, so it is always available:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# ~/.bashrc
export SECRET_KEY_BASE=&amp;quot;YOUR_SECRET_KEY_BASE&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the source to make it instantly available in your environment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, back on your machine, access the application folder in the terminal and run the Capistrano command below to create &lt;code&gt;config/secrets.yml&lt;/code&gt; on the production server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ cap production setup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All done! Now it&amp;#39;s time to configure Capistrano to run a migration.&lt;/p&gt;
&lt;h2&gt;Configure Capistrano to Perform a Database Migration&lt;/h2&gt;
&lt;p&gt;Now it&amp;#39;s time to configure Capistrano to run database-related commands.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;capistrano-rails&lt;/em&gt; gem already included in your Gemfile can be configured to perform a migration on each deployment. We just need to import it into Capfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Capfile
require &amp;quot;capistrano/rails/migrations&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if you want to run the seeds on deployment, you need to create a new task to run after the migration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/deploy.rb

namespace :deploy do
  desc &amp;quot;Run seed&amp;quot;
  task :seed do
    on roles(:all) do
      within current_path do
        execute :bundle, :exec, &amp;#39;rails&amp;#39;, &amp;#39;db:seed&amp;#39;, &amp;#39;RAILS_ENV=production&amp;#39;
      end
    end
  end

  after :migrating, :seed
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, your app is finally ready to be deployed!&lt;/p&gt;
&lt;h2&gt;Start Deployment of Your Ruby on Rails App&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s see how to perform a deployment from our local machine by directly updating our server to the new version of our application.&lt;/p&gt;
&lt;p&gt;When you execute a deployment command, Capistrano will connect to your server. From there, Capistrano will try to clone the code from the repository defined in the configuration file (&lt;code&gt;config/deploy.rb&lt;/code&gt;). Then other tasks will be performed, following the deployment flow.&lt;/p&gt;
&lt;p&gt;Capistrano&amp;#39;s &lt;code&gt;deploy:check&lt;/code&gt; command validates that the Git configuration is alright and that Capistrano has correct access to the directories that will be used in the deployment.&lt;/p&gt;
&lt;p&gt;To use it, pass the environment. In this case, we are using production:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ cap production deploy:check
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If everything is ok with the configuration, you can start your deployment to production:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ cap production deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition, you can check all available tasks in Capistrano with the command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ cap --tasks
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, in our final step, let&amp;#39;s integrate our Ruby on Rails application with AppSignal and start monitoring it.&lt;/p&gt;
&lt;h2&gt;How to Integrate AppSignal with Capistrano and Your Rails App&lt;/h2&gt;
&lt;p&gt;AppSignal is a monitoring tool, so you might wonder why it&amp;#39;s included in this article. That is a valid question, as monitoring is not directly related to deployments.&lt;/p&gt;
&lt;p&gt;That said, AppSignal is a valuable tool when it comes to monitoring your Ruby on Rails application and keeping track of deployments in each environment. So let&amp;#39;s see how we can set up AppSignal for your app.&lt;/p&gt;
&lt;p&gt;You will need an AppSignal account, which you can create on &lt;a href=&quot;https://appsignal.com/users/sign_up&quot;&gt;AppSignal&amp;#39;s sign-up page&lt;/a&gt;. Pick Ruby as your language and follow the steps to install AppSignal in your application.&lt;/p&gt;
&lt;p&gt;Once installed, a file will be created to configure AppSignal. What&amp;#39;s important here is to define the &lt;em&gt;revision&lt;/em&gt; — information that will be checked after deployment to see if a new version of an application has been deployed. In this case, we will use the Git log as revision information to ensure that each new code deployment will notify AppSignal.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/appsignal.yml

production:
  &amp;lt;&amp;lt;: *defaults
  active: true
  revision: &amp;quot;&amp;lt;%= `git log --pretty=format:&amp;#39;%h&amp;#39; -n 1` %&amp;gt;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is also good practice to export AppSignal environment variables to your production server (especially the API key, so as not to keep it in the AppSignal configuration file).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;export APPSIGNAL_PUSH_API_KEY=XXX
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we can rerun the deployment. If everything is set up correctly, you will see a message like this at the end of the deployment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;Notifying AppSignal of deploy with: revision: ee5e626fd31613ef068873f9cddb5f8c91538396, user: monteirobrena
AppSignal has been notified of this deploy!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will now be able to see your application in AppSignal! 🎉&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-07/applications-list.png&quot; alt=&quot;Applications list&quot;/&gt;&lt;/p&gt;
&lt;p&gt;And information about any deployments:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-07/deployments-list.png&quot; alt=&quot;Deployments list&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.appsignal.com/application/markers/deploy-markers.html&quot;&gt;Visit AppSignal&amp;#39;s deployment documentation page&lt;/a&gt; to learn more about deployment integration and other features available for Ruby on Rails applications.&lt;/p&gt;
&lt;h2&gt;Wrap Up and Next Steps&lt;/h2&gt;
&lt;p&gt;In this post, we explored how to use Capistrano to deploy your Ruby on Rails app. We first added Capistrano to an app, then configured and deployed it, before finally setting up monitoring with AppSignal.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;d like to explore further, you can configure Capistrano to run from an integration connected to your repository&amp;#39;s pipeline. Tools like Bitbucket, GitHub, and GitLab provide settings to manage this process and can be used in conjunction with Capistrano.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Get Started with Hotwire in Your Ruby on Rails App</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/07/06/get-started-with-hotwire-in-your-ruby-on-rails-app.html"/>
    <id>https://blog.appsignal.com/2022/07/06/get-started-with-hotwire-in-your-ruby-on-rails-app.html</id>
    <published>2022-07-06T00:00:00+00:00</published>
    <updated>2022-07-06T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn the basics of Hotwire and how to use it in your Rails app.</summary>
    <content type="html">&lt;p&gt;Hotwire is a hot topic at the moment for every Rails developer.
If you work with Rails, there is a good chance you have already heard a lot about it.&lt;/p&gt;
&lt;p&gt;Hotwire is a completely new way of adding interactivity to your app with very few lines of code, and it works blazing fast by transmitting HTML over the wire.
That means you can keep your hands clean from most Single Page Applications (SPA) frameworks. You can also keep your rendering logic centralized on the server, while still maintaining quick page load times and interactivity.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll look at the main components of Hotwire and how to use it in your Rails app. But first: what is Hotwire and why should you use it?&lt;/p&gt;
&lt;h2&gt;What Is Hotwire?&lt;/h2&gt;
&lt;p&gt;Hotwire is not a single library, but a new approach to building web and mobile applications by sending HTML over the wire.
It includes Turbo, Stimulus, and Strada (coming later this year).
We will discuss each of these in detail in the next section.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Side note&lt;/em&gt;: While Hotwire is highly linked with Rails, it is completely language-agnostic, so it can work just as well with other applications.
I have been using Stimulus in production on several non-Rails apps and some static websites. You can use
Turbo without Rails as well.&lt;/p&gt;
&lt;p&gt;But let us come back to the Rails world for now.&lt;/p&gt;
&lt;h2&gt;Why Use Hotwire in Your Rails App?&lt;/h2&gt;
&lt;p&gt;So when should you use Hotwire? The answer is anywhere you want to add interactivity to your application. For example, if you want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some content to be displayed/hidden conditionally based on a user&amp;#39;s interaction (e.g., an address form where the list of states automatically changes based on the selected country).&lt;/li&gt;
&lt;li&gt;To update some content in real-time (e.g., a feed like Twitter where new Tweets automatically get added to the page).&lt;/li&gt;
&lt;li&gt;To lazy-load some parts of your pages (e.g., inside an accordion, you can load the titles and mark the details to be lazy-loaded to speed up load times).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hotwire Components&lt;/h2&gt;
&lt;p&gt;As mentioned before, Hotwire is a collection of new (and some old) techniques for building web apps.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s discuss each of these in the next few sections.&lt;/p&gt;
&lt;h3&gt;Turbo&lt;/h3&gt;
&lt;p&gt;HTML drives Turbo at its core.
Turbo provides several techniques to handle HTML data coming over the wire and display it on your application without performing a full page reload.
It is composed of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Turbo Drive&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you have used &lt;a href=&quot;https://github.com/turbolinks/turbolinks&quot;&gt;Turbolinks&lt;/a&gt; in the past, you will feel right at home with Turbo Drive.
At its core, some JS code intercepts JavaScript events on your application, loads HTML asynchronously, and replaces parts of your HTML markup.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Turbo Frames&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Turbo Frames decouple parts of your markup into different sections that can be loaded independently.&lt;/p&gt;
&lt;p&gt;For example, if you have a blog application, the content of your post and the comments are two related but independent parts of the page.
You can decouple them so navigation works independently or even load them asynchronously with turbo frames.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Turbo Streams&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Turbo Streams offers utilities to easily bring in real-time data to your application.
For example, let&amp;#39;s say you are building a news feed like Twitter. You want to pull new tweets into a user&amp;#39;s feed as soon as they are posted without reloading the page.
Turbo Streams allow you to do this without writing a single line of JS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Turbo Native&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Turbo Native lets you build a native wrapper around your web application. Navigations and interactions will feel native without you having to redo all the screens natively.&lt;/p&gt;
&lt;p&gt;You&amp;#39;ll keep delivering the rest of the application through the web. That way, you can focus on the really interactive parts of your application and get them right.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Stimulus&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://stimulus.hotwired.dev/&quot;&gt;Stimulus&lt;/a&gt; is a JavaScript framework for writing controllers that interact with your HTML.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say we need to add some JavaScript attributes like &lt;code&gt;data-controller&lt;/code&gt;, &lt;code&gt;data-action&lt;/code&gt;, and &lt;code&gt;data-target&lt;/code&gt; to elements on a page. We&amp;#39;ll write a stimulus controller with access to elements that receives events based on those attributes.
Here&amp;#39;s an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div data-controller=&amp;quot;clipboard&amp;quot;&amp;gt;
  PIN:
  &amp;lt;input data-clipboard-target=&amp;quot;source&amp;quot; type=&amp;quot;text&amp;quot; value=&amp;quot;1234&amp;quot; readonly /&amp;gt;
  &amp;lt;button data-action=&amp;quot;clipboard#copy&amp;quot;&amp;gt;Copy to Clipboard&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is very easy to get an idea about what this does without even reading the associated Stimulus controller.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s a controller that goes with the HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// src/controllers/clipboard_controller.js
import { Controller } from &amp;quot;@hotwired/stimulus&amp;quot;;

export default class extends Controller {
  static targets = [&amp;quot;source&amp;quot;];

  copy() {
    navigator.clipboard.writeText(this.sourceTarget.value);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is at the core of Stimulus: keeping things simple and reusable.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Now, if you ever need a copy-to-the-clipboard button on another page, you can just re-use that controller. Add the &lt;code&gt;data-*&lt;/code&gt; attributes on the markup to get everything working.&lt;/p&gt;
&lt;h3&gt;Strada&lt;/h3&gt;
&lt;p&gt;Unfortunately, we don&amp;#39;t know much about Strada yet.
But it will allow a web application to communicate (and possibly perform actions) with a native app using HTML bridge attributes.&lt;/p&gt;
&lt;h2&gt;How to Use Hotwire in Your Ruby on Rails Application&lt;/h2&gt;
&lt;p&gt;I don&amp;#39;t want to spend too much time discussing Hotwire installation or a basic use case.
The Hotwire team has already done an excellent job of it in their &lt;a href=&quot;https://hotwired.dev/&quot;&gt;Hotwire screencast&lt;/a&gt;. For full instructions, see &lt;a href=&quot;https://github.com/hotwired/turbo-rails#installation&quot;&gt;&lt;code&gt;turbo-rails&lt;/code&gt; installation&lt;/a&gt; and &lt;a href=&quot;https://stimulus.hotwired.dev/handbook/installing&quot;&gt;Stimulus installation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s jump straight into some common Hotwire use cases.&lt;/p&gt;
&lt;h3&gt;Endless Scroll&lt;/h3&gt;
&lt;p&gt;Using Turbo Frames, we can easily make a page with automatic pagination as the user scrolls. For this, we need to do two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Render each &amp;quot;page&amp;quot; inside its own frame by appending the page number to the frame id (e.g., &lt;code&gt;turbo_frame_tag &amp;quot;posts_#{@posts.current_page}&amp;quot;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Use a &lt;a href=&quot;https://turbo.hotwired.dev/handbook/frames#lazy-loading-frames&quot;&gt;&lt;code&gt;lazy&lt;/code&gt; frame&lt;/a&gt; for the next page so that it doesn&amp;#39;t load automatically unless it comes into view.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= turbo_frame_tag &amp;quot;posts_#{@posts.current_page}&amp;quot; do %&amp;gt;
  &amp;lt;%= render @posts %&amp;gt;
  &amp;lt;% unless @posts.last_page? %&amp;gt;
    &amp;lt;%= turbo_frame_tag &amp;quot;posts_#{@posts.next_page}&amp;quot;, :src =&amp;gt; path_to_next_page(@posts), :loading =&amp;gt; &amp;quot;lazy&amp;quot; do %&amp;gt;
      &amp;lt;%= render &amp;quot;loading&amp;quot; %&amp;gt;
    &amp;lt;% end  %&amp;gt;
  &amp;lt;% end  %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that this example uses methods from &lt;a href=&quot;https://github.com/kaminari/kaminari&quot;&gt;Kaminari&lt;/a&gt;, but you can adapt it to any other pagination method.&lt;/p&gt;
&lt;p&gt;We don&amp;#39;t need anything special in the controller. A standard &lt;code&gt;index&lt;/code&gt; method works:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class PostsController &amp;lt; ApplicationController
  def index
    @posts = Post.page(params[:page]).per(params[:per_page])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The trick here is that we use nested frames, with the frame for the next page nested inside the frame for the previous page.
That way, when the first page loads, the frame for the next page is placed at the end.
When the user scrolls to that frame, it is replaced with the content of the second page.
The lazy frame for the third page renders at the end.&lt;/p&gt;
&lt;h3&gt;Dynamic Forms&lt;/h3&gt;
&lt;p&gt;You can easily implement dynamic forms with Hotwire without custom logic for toggling fields on the front end.
This is a bit more involved than the endless scroll use case, as it includes the use of both Turbo Stream and Stimulus.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start with our form first.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/posts/new.html.erb --&amp;gt;
&amp;lt;div data-controller=&amp;quot;refresh-form&amp;quot; data-refresh-form-url=&amp;quot;&amp;lt;%= refresh_form_posts_url(:target =&amp;gt; &amp;quot;new_post&amp;quot;) %&amp;gt;&amp;quot;&amp;gt;
  &amp;lt;%= render &amp;quot;form&amp;quot; %&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;!-- app/views/posts/_form.html.erb --&amp;gt;
&amp;lt;%= form_for(@post, :data =&amp;gt; { :target =&amp;gt; &amp;quot;refresh-form.form&amp;quot; }) do |f| %&amp;gt;
  &amp;lt;%= f.select :kind, options_for_select([[&amp;quot;News&amp;quot;, :news], [&amp;quot;Blog&amp;quot;, :blog]], @post.kind), {}, data: { action: &amp;quot;change-&amp;gt;refresh-form#refreshForm&amp;quot; } %&amp;gt;

  &amp;lt;%= f.select :category, options_for_select(categories_for_kind(@post.kind), @post.category) %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The form is simple enough — we display a &lt;code&gt;kind&lt;/code&gt; select with &lt;code&gt;News&lt;/code&gt; and &lt;code&gt;Blog&lt;/code&gt; options.
We want to change the available categories&amp;#39; values based on the kind that is selected (assuming that &lt;code&gt;categories_for_kind(@post.kind)&lt;/code&gt; returns the list of categories for the given kind).&lt;/p&gt;
&lt;p&gt;If you look closer, you&amp;#39;ll see that we&amp;#39;ve added some data attributes to the form.
The &lt;code&gt;data-target&lt;/code&gt; will link the form element to the &lt;code&gt;RefreshFormController&lt;/code&gt; Stimulus Controller&amp;#39;s &lt;code&gt;form&lt;/code&gt; target.
And the &lt;code&gt;data-action&lt;/code&gt; with the value of &lt;code&gt;change-&amp;gt;refresh-form#refreshForm&lt;/code&gt; will call the &lt;code&gt;refreshForm&lt;/code&gt; method on the linked Stimulus Controller every time the &lt;code&gt;kind&lt;/code&gt; select is changed.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at our Stimulus Controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// app/javascript/controllers/refresh_form_controller.js
import { Controller } from &amp;quot;stimulus&amp;quot;;
import { put } from &amp;quot;@rails/request.js&amp;quot;;

export default class extends Controller {
  static targets = [&amp;quot;form&amp;quot;];

  refreshForm() {
    put(this.data.get(&amp;quot;url&amp;quot;), {
      body: new FormData(this.formTarget),
      responseKind: &amp;quot;turbo-stream&amp;quot;,
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On all &lt;code&gt;refreshForm&lt;/code&gt; calls, we just make a new &lt;code&gt;PUT&lt;/code&gt; request to the controller&amp;#39;s URL (set using the &lt;code&gt;data-refresh-form-url&lt;/code&gt; on the same element with a &lt;code&gt;data-controller=&amp;quot;refresh-form&amp;quot;&lt;/code&gt;).
The important part here is that the &lt;code&gt;responseKind&lt;/code&gt; is set to &lt;code&gt;turbo-stream&lt;/code&gt;.
The &lt;code&gt;@rails/request&lt;/code&gt; library understands this response and performs instructions based on the response stream.&lt;/p&gt;
&lt;p&gt;Now all that&amp;#39;s left is to return the correct stream from our &lt;code&gt;refresh_form&lt;/code&gt; call for Turbo to understand and update our form.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class PostsController &amp;lt; ApplicationController
  def refresh_form
    @post = Post.new
    @post.attributes = post_params
    @post.valid?
    respond_to do |format|
      format.turbo_stream
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just update the attributes on the post and mark that you want to respond in a &lt;code&gt;turbo_stream&lt;/code&gt; format (so that it looks up &lt;code&gt;refresh_form.turbo_stream.erb&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/posts/refresh_form.turbo_stream.erb --&amp;gt;
&amp;lt;%= turbo_stream.replace params[:target] do %&amp;gt;
  &amp;lt;%= render &amp;quot;form&amp;quot; %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this step, we are reusing our &lt;code&gt;form&lt;/code&gt; partial, wrapping it inside a &lt;code&gt;turbo_stream&lt;/code&gt; with a &lt;code&gt;replace&lt;/code&gt; action.&lt;/p&gt;
&lt;p&gt;And that&amp;#39;s all you need to get a dynamic form working.
I know this looks a bit advanced, but the &lt;code&gt;refresh&lt;/code&gt; stimulus controller is a shared part you can now use for all your dynamic forms by adding the correct &lt;code&gt;data-*&lt;/code&gt; attributes.
So essentially, you now get server-side dynamic form refresh without writing any new JS for other forms.
Pretty awesome, right?&lt;/p&gt;
&lt;h3&gt;Append Content to Pages Without Reloading&lt;/h3&gt;
&lt;p&gt;The next use case that Hotwire makes easy is streaming HTML over a WebSocket connection and updating a page with new content as it comes in.
A good example of this is the GitHub comments section.
You can implement this very easily using Turbo Streams.&lt;/p&gt;
&lt;p&gt;There are two parts to this.&lt;/p&gt;
&lt;p&gt;First, we embed a turbo stream listener on the listing page that opens a WebSocket connection to the server and listens for events.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/comments/index.html.erb --&amp;gt;
&amp;lt;div id=&amp;quot;comments&amp;quot;&amp;gt;
  &amp;lt;%= turbo_stream_from @post, :comments %&amp;gt;

  &amp;lt;% @comments.each do |comment| %&amp;gt;
    &amp;lt;%= render comment %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we update the model to broadcast new comments to the stream.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/coment.rb
class Comment &amp;lt; ApplicationRecord
  belongs_to :post

  after_create_commit :stream

  private

  def stream
    broadcast_prepend_later_to(post, :comments, target: :comments)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You don&amp;#39;t need anything else. Turbo will automatically render the &lt;code&gt;app/views/comments/_comment.html.erb&lt;/code&gt; partial for each new comment and send it over a WebSocket connection. It will be picked up by Turbo&amp;#39;s JS and prepended to the target with id &lt;code&gt;comments&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s go one step ahead and add an indication to all newly added comments with a small Stimulus Controller.&lt;/p&gt;
&lt;p&gt;First, modify the broadcast and &lt;code&gt;comment&lt;/code&gt; partial to include the controller conditionally.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/coment.rb
# ...
def stream
  broadcast_prepend_later_to(post, :comments,
                             target: :comments,
                             locals: { highlight: true })
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;!-- app/views/comments/_comment.html.erb --&amp;gt;
&amp;lt;div &amp;lt;%= %s(data-controller=&amp;quot;highlight&amp;quot;) if local_assigns[:highlight] %&amp;gt;
  &amp;lt;%= comment.body %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This small Stimulus controller adds a special highlight class on connection for 3 seconds and then removes it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;export default class extends Controller {
  connect() {
    this.element.classList.add(&amp;quot;highlight&amp;quot;);
    this.timeout = setTimeout(
      () =&amp;gt; this.element.classList.remove(&amp;quot;highlight&amp;quot;),
      3000,
    );
  }

  disconnect() {
    clearTimeout(this.timeout);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: You also need to update the CSS highlighting based on the presence of that class.&lt;/p&gt;
&lt;p&gt;Once this controller is done, you can re-use it on anything that requires a highlight class.
You could even modify it to get the duration and class name from data attributes if you need that flexibility.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the great thing about Hotwire — it takes you a long way, and you don&amp;#39;t have to dip your hands in JS.
When you do need to write some JS, Stimulus gives you the tools to build small generic controllers that can be re-used.&lt;/p&gt;
&lt;h2&gt;Wrap Up and Further Reading&lt;/h2&gt;
&lt;p&gt;The Rails community has been really excited with the introduction of Hotwire, and rightly so.&lt;/p&gt;
&lt;p&gt;In this post, we looked at the key components of Hotwire and how to use Hotwire in your Rails app. We touched on how you can bring your application to life using Turbo and Stimulus.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://hotwired.dev/#screencast&quot;&gt;official Hotwire screencast introduction&lt;/a&gt; and the &lt;a href=&quot;https://turbo.hotwired.dev/handbook/introduction&quot;&gt;Turbo documentation&lt;/a&gt; are great places to see what Hotwire and Turbo can do for you.&lt;/p&gt;
&lt;p&gt;For advanced usage, I suggest heading over to the &lt;a href=&quot;https://github.com/hotwired/turbo-rails&quot;&gt;turbo-rails GitHub repo&lt;/a&gt;.
Sadly, the documentation is a bit sparse, but if you are not afraid to get your hands dirty, read the code and inline comments in:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/frames_helper.rb&quot;&gt;&lt;code&gt;Turbo::FramesHelper&lt;/code&gt;&lt;/a&gt; for &lt;em&gt;Turbo Frames&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb&quot;&gt;&lt;code&gt;Turbo::Broadcastable&lt;/code&gt;&lt;/a&gt; for broadcasting to &lt;em&gt;Turbo Streams&lt;/em&gt; from the code.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hotwired/turbo-rails/blob/main/app/models/turbo/streams/tag_builder.rb&quot;&gt;&lt;code&gt;Turbo::Streams::TagBuilder&lt;/code&gt;&lt;/a&gt; for broadcasting to &lt;em&gt;Turbo Streams&lt;/em&gt; as part of inline controller actions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>State Machines in Ruby: An Introduction</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/06/22/state-machines-in-ruby-an-introduction.html"/>
    <id>https://blog.appsignal.com/2022/06/22/state-machines-in-ruby-an-introduction.html</id>
    <published>2022-06-22T00:00:00+00:00</published>
    <updated>2022-06-22T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s build a simple state machine in Ruby and use the state machines gem.</summary>
    <content type="html">&lt;p&gt;A state machine can hold all possible states of &lt;em&gt;something&lt;/em&gt; and the allowed transitions between these states.
For example, the state machine for a door would have only two states (&lt;code&gt;open&lt;/code&gt; and &lt;code&gt;closed&lt;/code&gt;) and only two transitions (&lt;code&gt;opening&lt;/code&gt; and &lt;code&gt;closing&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;On the other hand, complex state machines can have several different states with hundreds of transitions between them.
In fact, if you look around, you will notice finite state machines surrounding you — when you buy from a website, get a pack of crisps from the vending machine, or even just withdraw money from an ATM.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll look at how to set up a state machine in Ruby and use the state machines gem.&lt;/p&gt;
&lt;h2&gt;State Machines in Development&lt;/h2&gt;
&lt;p&gt;When do we need a state machine in development? The simple answer is whenever you want to model multiple rules for state transitions or perform side-effects on some transitions.
The key here is to identify parts of your application that would benefit from a state machine.
A good example that always works for me is an &lt;code&gt;Order&lt;/code&gt; in the context of an e-commerce application.&lt;/p&gt;
&lt;p&gt;For a simple application selling products online, an &lt;code&gt;Order&lt;/code&gt; can be in one of several states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;created&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;processing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ready&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shipped&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delivered&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;void&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see the allowed transitions in the following diagram.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-06/order-state-machine.png&quot; alt=&quot;State machine diagram&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Visualizing the order in the form of a state machine immediately allows us to clearly understand the full flow, from ordering the product to delivery.
And for us developers, it enables clear, set steps on what can and cannot be done at particular points in that flow.&lt;/p&gt;
&lt;p&gt;That being said, it is easy to jump down a rabbit hole by picking this up for a very wide problem and then end up with hundreds of states that are difficult to reason about and follow through.
So always have a top-level idea and a state chart for your proposed state machine before implementing it.&lt;/p&gt;
&lt;h2&gt;Our First State Machine in Ruby&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s try to implement our &lt;code&gt;OrderStateMachine&lt;/code&gt; with Ruby.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class OrderStateMachine
  def initialize(order)
    @order = order
    @order.state = :created if @order.state.blank?
  end

  def mark_as_paid!
    raise &amp;quot;Invalid state #{@order.state}&amp;quot; unless @order.created?

    @order.state = :processing
  end

  def packed!
    raise &amp;quot;Invalid state #{@order.state}&amp;quot; unless @order.processing?

    @order.state = :ready
  end

  def shipped!
    raise &amp;quot;Invalid state #{@order.state}&amp;quot; unless @order.ready?

    @order.state = :shipped
  end

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s a simple enough implementation.
For each possible transition in the state machine, we define a new method that does some sanity checks and performs the transition.&lt;/p&gt;
&lt;p&gt;The great thing about the above implementation is that everything is explicit: any new developer can very quickly understand the full extent of the state machine.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Let&amp;#39;s see how we can add some side-effects to the transitions.
We&amp;#39;ll automatically ship orders that are packed and deliverable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class OrderStateMachine
  # ...

  def packed!
    raise &amp;quot;Invalid state #{@order.state}&amp;quot; unless @order.processing?

    @order.state = :ready
    ship_order if @order.deliverable?
  end

  # ...

  private

  def ship_order
    DeliveryService.create_consigment!(@order)
    shipped!
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While a naïve implementation works great, it is overly verbose, especially when we have a lot of transitions and conditions.&lt;/p&gt;
&lt;p&gt;A quick check on &lt;a href=&quot;https://www.ruby-toolbox.com/categories/state_machines&quot;&gt;Ruby Toolbox&lt;/a&gt; brings up several gems for state machines.
&lt;a href=&quot;https://github.com/state-machines/state_machines&quot;&gt;&lt;code&gt;state_machines&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/aasm/aasm&quot;&gt;&lt;code&gt;aasm&lt;/code&gt;&lt;/a&gt; are the most popular ones, and both come with an ActiveRecord adapter if you want to use them with Rails.
Both are thoroughly tested and production-ready, so do check them out if you need to implement a state machine.&lt;/p&gt;
&lt;h2&gt;Using the State Machines Gem in Ruby&lt;/h2&gt;
&lt;p&gt;For this post, I will describe how we can model the state machine for our &lt;code&gt;Order&lt;/code&gt; using the &lt;code&gt;state_machines&lt;/code&gt; gem.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Order
  state_machine :state, initial: :created do
    event :confirm_payment do
      transition created: :processing
    end
    event :pack do
      transition processing: :ready
    end
    event :cancel do
      transition %i[created processing ready] =&amp;gt; :void
    end
    event :return do
      transition delivered: :void
    end
    event :ship do
      transition ready: :shipped
    end
    event :fail_delivery do
      transition shipped: :processing
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above class defines our simple state machine and all of its possible transitions.
On top of this, it also automatically exposes a lot of utility methods on an order:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;order.state           # =&amp;gt; &amp;quot;created&amp;quot;
order.state_name      # =&amp;gt; :created
order.created?        # =&amp;gt; true
order.can_pack?       # =&amp;gt; false (since payment has not been confirmed yet)
order.confirm_payment # =&amp;gt; true (and transitions to `processing`)
order.can_pack?       # =&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Execute Side-Effects with the State Machines Gem in Ruby&lt;/h2&gt;
&lt;p&gt;As is usual for any real-world system, many transitions in a state machine will come with side-effects.
The &lt;code&gt;state_machines&lt;/code&gt; gem makes it easy to define and execute them.
Let&amp;#39;s add two side-effects:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;On all transitions to &lt;code&gt;ready&lt;/code&gt;, create a delivery consignment if the order is &lt;code&gt;deliverable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On all &lt;code&gt;fail_delivery&lt;/code&gt; events, send an email to the user notifying them of the event.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Order
  attr_accessor :deliverable

  state_machine :state, initial: :created do
    # ...

    after_transition to: :ready, do: :create_delivery_consignment, if: :deliverable
    after_transition on: :fail_delivery, do: :notify_delivery_failure
  end

  private

  def create_delivery_consignment
    DeliveryService.create_consigment!(self)
    ship
  end

  def notify_delivery_failure
    UserMailer.delivery_failure_email(self).deliver_later
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can perform transitions as usual in the order:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;order.deliverable = true
order.pack            # =&amp;gt; true
order.state           # =&amp;gt; &amp;quot;shipped&amp;quot; (through side-effect)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I would suggest checking out the &lt;a href=&quot;https://github.com/state-machines/state_machines&quot;&gt;Github page for the &lt;code&gt;state_machines&lt;/code&gt; gem&lt;/a&gt; to learn about all the possible options the gem offers.&lt;/p&gt;
&lt;h2&gt;Use the State Machines Gem with ActiveRecord in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;As discussed before, both &lt;code&gt;state_machines&lt;/code&gt; and &lt;code&gt;aasm&lt;/code&gt; support ActiveRecord.
When you use the &lt;code&gt;state_machines&lt;/code&gt; gem with ActiveRecord, not a lot of things change with the implementation. You can continue using most of what we&amp;#39;ve already discussed in the previous sections of this post.&lt;/p&gt;
&lt;p&gt;Here are some additional features that you get:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Side-effects happen inside a transaction.
This means that if you do some database operations inside transition hooks and the transaction fails, the state change won&amp;#39;t be committed.
&lt;a href=&quot;https://www.rubydoc.info/github/state-machines/state_machines-activerecord/StateMachines/Integrations/ActiveRecord#label-Transactions&quot;&gt;See &lt;code&gt;use_transactions&lt;/code&gt;&lt;/a&gt; if you want to disable this behavior.&lt;/li&gt;
&lt;li&gt;Like all other ActiveRecord callbacks, if you want to update the attributes of the current model from a transition, you must do this inside a &lt;code&gt;before_transition&lt;/code&gt; callback.
To update attributes inside &lt;code&gt;after_transition&lt;/code&gt;, you need to save the model again.
Check out the following example:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Order &amp;lt; ActiveRecord::Base
  state_machine initial: :created do

    before_transition any =&amp;gt; :processing do |order|
      # You can just update the attribute here and it will be saved.
      order.processing_start_at = Time.zone.now
    end

    after_transition any =&amp;gt; :shipped do |order|
      # Call update! to update the order.
      # Just setting the attribute like above would not work here.
      order.update!(shipped_at: Time.zone.now)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;The gem automatically provides scopes to filter models by their state.
For example, you can do &lt;code&gt;Order.with_state(:processing)&lt;/code&gt; to find all the processing orders or &lt;code&gt;Order.without_state(:processing)&lt;/code&gt; to find all orders that are not under processing.&lt;/li&gt;
&lt;li&gt;If a state change is attempted without a matching transition (for example, from &lt;code&gt;processing&lt;/code&gt; to &lt;code&gt;delivered&lt;/code&gt;), the record will fail to save, and an error will be added to the validation errors.
To internationalize the generated error messages, just add some keys to provide translations for states and events inside the config files (for example, &lt;code&gt;en.yml&lt;/code&gt;):&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;en:
  activerecord:
    state_machines:
      order:
        states:
          created: Created
          processing: Under Processing
          # ...
        events:
          return: Return Order
          # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Side note&lt;/em&gt;: &lt;a href=&quot;https://blog.appsignal.com/2021/02/24/troubleshooting-activerecord-performance.html&quot;&gt;Check out this post to troubleshoot ActiveRecord performance issues&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrap Up: Build State Machines in Ruby&lt;/h2&gt;
&lt;p&gt;In this post, we explored why we&amp;#39;d use a state machine in development, before building a simple state machine. Finally, we looked at using the state machines gem in Ruby and Ruby on Rails.&lt;/p&gt;
&lt;p&gt;Now that you have a basic understanding of a state machine, I am sure you will recognize several scenarios around you that already use a state machine or will benefit greatly from one.&lt;/p&gt;
&lt;p&gt;Until next time, I wish you luck building state machines!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Add Feature Flags in Ruby on Rails with Flipper</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/06/08/add-feature-flags-in-ruby-on-rails-with-flipper.html"/>
    <id>https://blog.appsignal.com/2022/06/08/add-feature-flags-in-ruby-on-rails-with-flipper.html</id>
    <published>2022-06-08T00:00:00+00:00</published>
    <updated>2022-06-08T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out how feature flags function in principle and get started with feature flags using the Flipper gem.</summary>
    <content type="html">&lt;p&gt;Picture this scenario: you are a Rails developer and have spent the last couple of days developing that awesome feature that everyone
is waiting for. It&amp;#39;s big and complex, but it went through rigorous testing, so you are confident everything works as it
should. There are deadlines to meet, so you deploy. Immediately, all hell breaks loose.&lt;/p&gt;
&lt;p&gt;Your feature straight up breaks the entire app for some of your users. It&amp;#39;s hard to say why. No bugs showed up
during testing. You revert your change, but the damage has been done. Your customers aren&amp;#39;t happy, and you will spend
the foreseeable future doing damage control and investigating what went wrong.&lt;/p&gt;
&lt;p&gt;The problem is that releasing complex changes is inherently risky, no matter how much testing you do. It would have
been nice if you had rolled this feature out gradually, maybe only to a handful of users first. It would have
been even better if you could have switched the feature off as soon as the first problem showed up.&lt;/p&gt;
&lt;p&gt;This is precisely where feature flags come in. In this post, we&amp;#39;ll dive into the ins and outs of feature flags in Rails, including how to set them up using the Flipper gem. But first: what are they?&lt;/p&gt;
&lt;h2&gt;What Are Feature Flags in Ruby on Rails?&lt;/h2&gt;
&lt;p&gt;Feature flags are a way to enable or disable a feature in your application while it is running. If a flag is enabled,
the feature is used. Otherwise, it is not. Imagine something like this in pseudocode.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if feature_enabled?(:my_awesome_feature)
  # Run awesome feature code
else
  # Not so awesome code
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The name &amp;#39;feature flag&amp;#39; is somewhat misleading. Controlling application behavior at runtime is not limited to features,
but applies to any function within your program. You can use feature flags to try out performance tweaks and run A/B tests, for example.
A significant difference between a feature flag and any other old conditional is you can modify feature flags at runtime.&lt;/p&gt;
&lt;p&gt;For illustration purposes, let&amp;#39;s imagine a simple feature flagging mechanism using environment variables.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def feature_enabled(feature_name)
  ENV[feature_name.to_s.upcase]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By modifying the value of the environment variable, we can switch
parts of our application on or off at will. Which part of an application? Anything you want!&lt;/p&gt;
&lt;p&gt;You could hide parts of the UI if a feature is disabled. You could switch out parts of your code, which is very useful
if you aren&amp;#39;t confident in your latest refactor. One of my favorite uses of feature flags is to hide entire routes
using &lt;a href=&quot;https://guides.rubyonrails.org/routing.html#advanced-constraints&quot;&gt;routing constraints&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/constraint/feature_constraint.rb
class FeatureConstraint
  def initialize(feature)
    @feature = feature
  end

  def matches?(_request)
    feature_enabled?(@feature)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/routes.rb
Rails.application.routes.draw do
  get &amp;#39;statistics&amp;#39;, to: &amp;#39;statistics#index&amp;#39;, constraints: FeatureConstraint.new(:statistics)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can already see that there are lots of applications for feature flags.
Our naive implementation has one significant weakness, though. A feature is either on or off for every user.&lt;/p&gt;
&lt;p&gt;What if we want the ability to enable features only for specific users or groups of users, or only for some of the time?
Let&amp;#39;s take a look at Flipper.&lt;/p&gt;
&lt;h2&gt;Using the Flipper Gem for Feature Flags&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jnunemaker/flipper&quot;&gt;Flipper&lt;/a&gt; is a gem that makes feature flags and different ways to toggle them
available in Rails. It is highly modular. Apart from the main gem, you&amp;#39;ll also have to pick a storage adapter — but
more on that later. Let&amp;#39;s use the ActiveRecord adapter for now.&lt;/p&gt;
&lt;p&gt;Add the following lines to your Gemfile and run &lt;code&gt;bundle install&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;flipper&amp;#39;
gem &amp;#39;flipper-active_record&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;When we use the ActiveRecord adapter, Flipper will store your feature flag in the database and thus requires setting
up new database tables. Flipper comes with a handy generator that will create the necessary migrations.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails g flipper:active_record
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use Flipper in the same way as the naive implementation we used previously.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if Flipper.enabled?(:my_awesome_feature)
  # Run awesome feature code but with Flipper!
else
  # Not so awesome code
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The big difference is that instead of modifying environment variables, you now get to toggle features using Flipper&amp;#39;s user
interface. Add the Flipper UI gem and install it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;flipper-ui&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modify your routes file to mount Flipper UI.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.routes.draw do
  mount Flipper::UI.app(Flipper) =&amp;gt; &amp;#39;/flipper&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you start your application and navigate to the Flipper UI, you will see an overview of all available features and
their status. Go ahead and create a &lt;code&gt;my_awesome_feature&lt;/code&gt; feature.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-06/flipper-ui.png&quot; alt=&quot;The Flipper UI overview&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Feature Toggles in Flipper&lt;/h2&gt;
&lt;p&gt;You&amp;#39;ve probably noticed that you can do much more than simply enable or disable a feature in Flipper UI.
Flipper allows us to enable features for a subset of users or even for a percentage of the time. You can configure your
feature flags using one of five different mechanisms.&lt;/p&gt;
&lt;p&gt;One of them is the boolean toggle, a global on-off switch. The other four are a lot more interesting than that: groups, actors, percentage of actors, and percentage of time.&lt;/p&gt;
&lt;h3&gt;Groups&lt;/h3&gt;
&lt;p&gt;To enable a feature only for a particular group of users, use the group toggle. If it doesn&amp;#39;t exist, create the
file &lt;code&gt;config/initializers/flipper.rb&lt;/code&gt; and add the following code to register a new group — for example, paid users.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/flipper.rb
Flipper.register(:paid_users) do |actor, _context|
  actor.respond_to?(:paid_users?) &amp;amp;&amp;amp; actor.paid?
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use this new toggle, modify the Flipper invocation to pass the user — or actor, as the documentation likes to
call them — and the feature name.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if Flipper.enabled?(:my_awesome_feature, user)
  # Run awesome feature only if the user is paid
else
  # Not so awesome code
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Flipper will use the block you defined in the initializer to decide if the feature is enabled for this user. In this case,
it will invoke the &lt;code&gt;paid?&lt;/code&gt; method. You can now use this group in Flipper UI. Click the &amp;#39;Add a Group&amp;#39; button and select
your group.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-06/flipper-ui-groups.png&quot; alt=&quot;Adding a Flipper UI group feature&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Your group doesn&amp;#39;t have to be paid users. It could be members of your team or users who have signed up for a beta
program. Using the group toggle is helpful if you want to release something to a subset of users. Imagine beta
releases or internal releases.&lt;/p&gt;
&lt;h3&gt;Actors&lt;/h3&gt;
&lt;p&gt;Sometimes, you might want to release something to a single individual. In this case, the actor toggle is something to consider.&lt;/p&gt;
&lt;p&gt;You don&amp;#39;t have to change anything in your initializer to use this mechanism. However, you&amp;#39;ll need to ensure that your
actor model — which will be your user model most of the time — implements &lt;code&gt;flipper_id&lt;/code&gt;. Including &lt;code&gt;Flipper::Identifier&lt;/code&gt; takes care of that.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# models/user.rb
class User &amp;lt; ApplicationModel
  include Flipper::Identifier
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.create.flipper_id # User;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can then use this ID to enable a feature for individual users.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-06/flipper-ui-actor.png&quot; alt=&quot;Adding a Flipper UI actor feature&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This feature toggle is handy if you want to preview functionality for particular individuals. Think about enabling
specific test accounts for partners who need access to a feature before it&amp;#39;s released to a broader audience.&lt;/p&gt;
&lt;h3&gt;Percentage of Actors&lt;/h3&gt;
&lt;p&gt;If you want to ramp up feature usage slowly, the percentage of actors toggle is for you. Imagine you&amp;#39;ve implemented some
performance optimization, and you&amp;#39;re not quite sure how it will behave in production. You would roll this out to a
small percentage of users first and then slowly ramp up the usage while monitoring your application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-06/flipper-ui-percentage-of-actors.png&quot; alt=&quot;Adding a Flipper UI percentage of actors feature&quot;/&gt;&lt;/p&gt;
&lt;p&gt;If things go sideways, you can always reset the percentage to zero.&lt;/p&gt;
&lt;h3&gt;Percentage of Time&lt;/h3&gt;
&lt;p&gt;Last but not least, you may enable a feature for a percentage of requests. You do not need to provide an actor when using this toggle.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if Flipper.enabled?(:my_awesome_feature)
  # Run awesome feature code only for a percentage of requests
else
  # Not so awesome code
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I like to use this to compare the performance of two different implementations. Note that this feature toggle is random.
The same user will experience different behavior on each request.&lt;/p&gt;
&lt;h2&gt;Advanced Configuration in Flipper&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve already seen that we can use the ActiveRecord adapter to store feature flag information in our database. But depending
on your specific needs, you might want to use &lt;a href=&quot;https://www.flippercloud.io/docs/adapters&quot;&gt;a different adapter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the most part, all you need to do when swapping out storage adapters is to install the respective gem and configure
Flipper accordingly. For example, if you want to use the Redis adapter, you add the &lt;code&gt;flipper-redis&lt;/code&gt; gem to your
Gemfile and update the Flipper initializer.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;flipper-redis&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/initializers/flipper.rb
Flipper.configure do |config|
  config.adapter { Flipper::Adapters::Redis.new(Redis.new) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apart from configuring storage adapters, there are a lot of other aspects of Flipper that you can tweak.&lt;/p&gt;
&lt;p&gt;Adding feature flags incurs a slight performance penalty, so you usually want to add memoization and caching. You&amp;#39;ll
likely also want to restrict access to the Flipper UI to authorized users. These topics are out of scope for this post,
but I recommend you check out the &lt;a href=&quot;https://www.flippercloud.io/docs/optimization&quot;&gt;Flipper documentation&lt;/a&gt; if you want to dive
deeper.&lt;/p&gt;
&lt;h2&gt;Caveats to Feature Flags in Rails&lt;/h2&gt;
&lt;p&gt;Feature flags are great. But as with so many things, there are tradeoffs to consider.&lt;/p&gt;
&lt;p&gt;Adding feature flags and maintaining them adds organizational overhead. Releasing and rolling out hidden features using
flags is not as simple as deploying &amp;#39;regular&amp;#39; code — you&amp;#39;ll have to remember to turn it on, of course! That usually
isn&amp;#39;t too hard, but cleaning up feature flags that you don&amp;#39;t need anymore might be.&lt;/p&gt;
&lt;p&gt;Every time you add a feature flag to your code, you add a conditional, and if you don&amp;#39;t watch out, your code quickly
becomes littered with them. Keeping track of when and why you created certain feature flags could be challenging.
Sadly, Flipper UI itself does not offer much to manage feature flags.&lt;/p&gt;
&lt;p&gt;You should also be aware that using feature flags will incur a minor performance penalty. There are ways to mitigate
this, but it can have a noticeable impact if you misconfigure or misuse Flipper.&lt;/p&gt;
&lt;h2&gt;Wrap Up: Get Started with Feature Flags in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;We looked at how feature flags can help you release with more confidence, learned how they function in principle, and
saw how you can get started with feature flags using the Flipper gem.&lt;/p&gt;
&lt;p&gt;There are many situations where feature flags are handy. Depending on your use case, you might reach for one of the
five different toggles — boolean, group, actor, percentage of actors, or percentage of requests.&lt;/p&gt;
&lt;p&gt;Although working with feature flags has its caveats and might take some getting used to, they are nevertheless a great
tool to have at your disposal.&lt;/p&gt;
&lt;p&gt;Until next time, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Polymorphism in Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/05/25/an-introduction-to-polymorphism-in-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2022/05/25/an-introduction-to-polymorphism-in-ruby-on-rails.html</id>
    <published>2022-05-25T00:00:00+00:00</published>
    <updated>2022-05-25T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn how you can implement polymorphism in your Rails application for cleaner code.</summary>
    <content type="html">&lt;p&gt;If you have ever spent time building an Object-Oriented Program (OOP), you have likely used polymorphism in your application or, at the very least, heard the term.&lt;/p&gt;
&lt;p&gt;It’s the kind of word you’d expect to see in a science or computer science textbook. You may have spent time researching polymorphism and even implemented it in your application without clearly understanding the concept.&lt;/p&gt;
&lt;p&gt;This article will give you a greater understanding of polymorphism, specifically in Ruby on Rails. To accomplish this, we’ll dive into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The use of polymorphism in the real world&lt;/li&gt;
&lt;li&gt;Polymorphism in programming by way of OOP&lt;/li&gt;
&lt;li&gt;How you can incorporate it into your Rails application to help maintain high-quality code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;Polymorphism in the Real World&lt;/h2&gt;
&lt;p&gt;There are several ways to define polymorphism in different contexts. A useful definition, regardless of context, is &lt;em&gt;&amp;#39;an object&amp;#39;s ability to display more than one form&amp;#39;&lt;/em&gt;. In fact, if we break down the word itself, poly means ‘many’ and morph means ‘form’.&lt;/p&gt;
&lt;p&gt;In the real world, a basic example of this definition could be a woman who is also a police officer, sister, someone’s child, someone’s mother, etc. Each role determines her behavior and contributes to the person she is.&lt;/p&gt;
&lt;h3&gt;Polymorphism and Genetics&lt;/h3&gt;
&lt;p&gt;Outside computer programming, polymorphism is a term commonly associated with biology and genetics. In this context, polymorphism is more specifically defined as genetic variations that result in several distinct forms or types of individuals within a species.&lt;/p&gt;
&lt;p&gt;Think of jaguars. Jaguars can have multiple gene variations, which can affect their fur coloring. Most jaguars have tawny coloring with black circles. However, they can have lighter or darker circles due to an altered gene, and some can have black fur coloring.&lt;/p&gt;
&lt;p&gt;Different pigmentation within the same species of birds is another example of polymorphism. Consider the Gouldian finch, which has obvious distinctions in its coloring between individuals.&lt;/p&gt;
&lt;h3&gt;Monomorphism vs. Polymorphism&lt;/h3&gt;
&lt;p&gt;If we turn our attention to monomorphism, we can further understand polymorphism. Sticking with biology, monomorphism can be defined as &lt;em&gt;&amp;#39;a species with just one form&amp;#39;&lt;/em&gt;, that maintains that same form during the various phases of its development.&lt;/p&gt;
&lt;p&gt;Penguins are monomorphic. It is difficult, even for experts, to distinguish between the sexes. Gene differences in a penguin are minimal. Therefore, the physical attributes of penguins are almost indistinguishable, especially in terms of their size and black and white coloring.&lt;/p&gt;
&lt;p&gt;Behavioral cues are often the easiest way to discern between the sexes in monomorphic species.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s turn our attention to polymorphism in programming, specifically.&lt;/p&gt;
&lt;h2&gt;Polymorphism in OOP&lt;/h2&gt;
&lt;p&gt;If we consider our initial definition of polymorphism — &lt;em&gt;the ability of an object to display more than one form&lt;/em&gt; — we can seamlessly relate it to OOP.&lt;/p&gt;
&lt;p&gt;In OOP, we can use the same method to produce different results by passing in separate objects. We could use conditionals to achieve this. However, this can create chunky code and may veer us away from DRY principles. Polymorphism is essential in creating clean and logical OOP applications.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look at two examples of how polymorphism can be implemented in an OOP language like Ruby: through inheritance and duck-typing.&lt;/p&gt;
&lt;h3&gt;Polymorphism and Inheritance in Ruby&lt;/h3&gt;
&lt;p&gt;Inheritance is where a child class inherits the properties of a parent class.&lt;/p&gt;
&lt;p&gt;Below is an example of how we can implement polymorphism with inheritance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Instrument
  def instrument_example
    puts &amp;quot;Saxophone&amp;quot;
  end
end

class Stringed &amp;lt; Instrument
  def instrument_example
    puts &amp;quot;Guitar&amp;quot;
  end
end

class Percussion &amp;lt; Instrument
  def instrument_example
    puts &amp;quot;Drums&amp;quot;
  end
end

all_instruments = [Instrument.new, Stringed.new, Percussion.new]

all_instruments.each do |instrument|
  instrument.instrument_example
end

# Output

# Saxophone
# Guitar
# Drums
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code has two child classes — &lt;code&gt;Stringed&lt;/code&gt; and &lt;code&gt;Percussion&lt;/code&gt; — inherited from the parent class &lt;code&gt;Instrument&lt;/code&gt;. This example is polymorphic, as we are calling a method: &lt;code&gt;instrument_example&lt;/code&gt; — and it outputs multiple forms: &lt;code&gt;Saxophone&lt;/code&gt;, &lt;code&gt;Guitar&lt;/code&gt;, and &lt;code&gt;Drums&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This example of achieving polymorphism through inheritance is essentially overriding a method, but helps provide a clearer understanding of polymorphism in an OOP language.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Duck-Typing and Polymorphism in Ruby&lt;/h3&gt;
&lt;p&gt;A more practical example of polymorphism in OOP is through duck-typing, as referenced below.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Guitar
  def brand
    &amp;#39;Gibson&amp;#39;
  end
end

  class Drums
    def brand
      &amp;#39;Pearl&amp;#39;
    end
  end

  class Bass
    def brand
      &amp;#39;Fender&amp;#39;
    end
  end

  class Keyboard
    def brand
      &amp;#39;Casio&amp;#39;
    end
  end

  all_instruments = [Guitar.new, Drums.new, Bass.new, Keyboard.new]

  all_instruments.each do |instrument|
    puts instrument.brand
  end
  # Output

  # Gibson
  # Pearl
  # Fender
  # Casio
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Though each class method is named &lt;code&gt;brand&lt;/code&gt;, we don&amp;#39;t override the method (unlike in polymorphic inheritance). Instead of inheriting from a parent class, here we have four independent classes, each with its own method. Duck-typing is useful, as we can just iterate through the classes to get each method&amp;#39;s output (as opposed to calling each method separately).&lt;/p&gt;
&lt;p&gt;Again, duck-typing is polymorphic as we call a method — &lt;code&gt;brand&lt;/code&gt; — and generate an output that takes multiple forms: &lt;code&gt;Gibson&lt;/code&gt;, &lt;code&gt;Pearl&lt;/code&gt;, &lt;code&gt;Fender&lt;/code&gt;, and &lt;code&gt;Casio&lt;/code&gt;. Of course, duck-typing and polymorphism aren’t essential in producing this outcome. However, it’s very useful to implement clean and logical code.&lt;/p&gt;
&lt;h2&gt;Polymorphism in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Polymorphism works well in Ruby on Rails as an Active Record association. If models essentially do the same thing, we can turn them into one single model to create a polymorphic relationship.&lt;/p&gt;
&lt;p&gt;Sticking with the instrument theme, let&amp;#39;s consider an application where users can post, comment, and review instruments. Examine the Entity-Relationship Diagram (ERD) below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-05/erd-1.png&quot; alt=&quot;first erd - no Polymorphic association&quot;/&gt;&lt;/p&gt;
&lt;p&gt;In this example, we have an ERD for an application where a user can post an instrument with its details. A user can also provide a comment about that posted instrument.&lt;/p&gt;
&lt;p&gt;Other users can then provide a rating of the instrument and rate comments to determine their usefulness or validity. These Active Record associations work just fine and serve the purpose of our application.&lt;/p&gt;
&lt;p&gt;What if we wanted to add other associations to our application? We would need to add and repeat duplicate associations.&lt;/p&gt;
&lt;p&gt;For example, if we wanted to add a &lt;code&gt;user_rating&lt;/code&gt; model to rate the trustworthiness of a user, we would need to create a separate table with its own associations. This would mean adding a new relationship between the &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;user_rating&lt;/code&gt; models. The ERD would then look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-05/erd-2.png&quot; alt=&quot;second erd - example of adding another model with no Polymorphic association&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Now we have three models essentially doing the same thing: rating an object, but in different contexts. These associations are ripe for a polymorphic association.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take a look at the ERD with the rating models as polymorphic:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-05/erd-3.png&quot; alt=&quot;third erd - example of a Polymorphic association&quot;/&gt;&lt;/p&gt;
&lt;p&gt;As we have a &lt;code&gt;rating&lt;/code&gt; column, I named the model &lt;code&gt;reviews&lt;/code&gt; as opposed to &lt;code&gt;ratings&lt;/code&gt; to avoid confusion. Here, the non-review models still have associations with the other models, but the separate rating models have been merged into a single review model.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;reviewable_type&lt;/code&gt; and &lt;code&gt;reviewable_id&lt;/code&gt; now take on the same role as the separate rating models by representing which model the review is associated with.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;reviewable_type&lt;/code&gt; column stores the model class name (&lt;code&gt;user&lt;/code&gt;, &lt;code&gt;instrument_post&lt;/code&gt;, or &lt;code&gt;comment&lt;/code&gt;), and the &lt;code&gt;reviewable_id&lt;/code&gt; stores the corresponding ID of that model.&lt;/p&gt;
&lt;p&gt;We can now use these two columns to link the rating integer with a specific user, post, or comment via Active Record queries and/or conditional statements. The foreign key &lt;code&gt;user_id&lt;/code&gt; remains in the review model, as this allows us to track which user left a review.&lt;/p&gt;
&lt;p&gt;Right now, the term ‘-able’ in our polymorphic model may seem strange, but its purpose will soon be made clear when we do some Rails magic.&lt;/p&gt;
&lt;p&gt;The review model is considered polymorphic as we have one model or object that can represent and take on multiple forms: user, comment, and instrument post reviews.&lt;/p&gt;
&lt;h2&gt;Implementing Polymorphism in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Time to implement polymorphism in a Rails application! If we act as though we have already created our &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;instrument_post&lt;/code&gt;, and &lt;code&gt;comment&lt;/code&gt; models, we can get started on incorporating our polymorphic model: &lt;code&gt;reviews&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Firstly, create a table and generate the model from the terminal, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g model Review user:belongs_to reviewable:references{polymorphic}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This builds the migration file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CreateReviews &amp;lt; ActiveRecord::Migration[7.0]
  def change
    create_table :reviews do |t|
      t.belongs_to :user, null: false, foreign_key: true
      t.references :reviewable, polymorphic: true, null: false
      t.integer :rating

      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;schema.rb&lt;/code&gt; file updates after the migration is run. The polymorphic option transforms the &lt;code&gt;reviewable&lt;/code&gt; column into the &lt;code&gt;reviewable_type&lt;/code&gt; and &lt;code&gt;reviewable_id&lt;/code&gt; columns:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;  create_table &amp;quot;reviews&amp;quot;, force: :cascade do |t|
    t.integer &amp;quot;user_id&amp;quot;, null: false
    t.string &amp;quot;reviewable_type&amp;quot;, null: false
    t.integer &amp;quot;reviewable_id&amp;quot;, null: false
    t.integer &amp;quot;rating&amp;quot;
    t.datetime &amp;quot;created_at&amp;quot;, null: false
    t.datetime &amp;quot;updated_at&amp;quot;, null: false
    t.index [&amp;quot;reviewable_type&amp;quot;, &amp;quot;reviewable_id&amp;quot;], name: &amp;quot;index_reviews_on_reviewable&amp;quot;
    t.index [&amp;quot;user_id&amp;quot;], name: &amp;quot;index_reviews_on_user_id&amp;quot;
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/review.rb

class Review &amp;lt; ApplicationRecord
  belongs_to :user
  belongs_to :reviewable, polymorphic: true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember when we mentioned the term ‘-able’ above? It is a Rails naming convention for designating our polymorphic association, giving us the ability to make a user, instrument post, and comment &amp;#39;reviewable&amp;#39;.&lt;/p&gt;
&lt;p&gt;For this Rails magic to work, we need to ensure that our other models are associated correctly with our polymorphic model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/user.rb

class User &amp;lt; ApplicationRecord
  has_many :instrument_posts
  has_many :comments
  # alias association for user who submitted the review
  has_many :submitted_reviews, class_name: &amp;quot;Review&amp;quot;, foreign_key: :user_id
  # association for user, instrument_post and comment that has the review
  has_many :reviews, as: :reviewable
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/instrument_post.rb

class InstrumentPost &amp;lt; ApplicationRecord
  belongs_to :user
  has_many :reviews, as: :reviewable
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/comment.rb

class Comment &amp;lt; ApplicationRecord
  belongs_to :user
  has_many :reviews, as: :reviewable
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;instrument_post&lt;/code&gt;, and &lt;code&gt;comment&lt;/code&gt; models can now be reviewed and given ratings.&lt;/p&gt;
&lt;p&gt;If we have already created at least two users, a comment, and an instrument post, we can then create and access the reviews through various ways with Active Record Queries, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# A user (User 2) leaving a review for another user (User 1)
# The reviewable_id is the id of the user to which the review is given
# The user_id is the id of the user who created the user review
user_1 = User.first
user_1.reviews.create(user_id: 2, rating: 2)
Review.where(reviewable_type: &amp;quot;User&amp;quot;).first # id: 1, user_id: 2, reviewable_type: &amp;quot;User&amp;quot;, reviewable_id: 1, rating: 2
# or
user_1.reviews.first # id: 1, user_id: 2, reviewable_type: &amp;quot;User&amp;quot;, reviewable_id: 1, rating: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# User 2 reviewing a instrument posted by User 1
# The reviewable_id is the id of the instrument_post
# The user_id is the id of the user who created the instrument_post review
post = InstrumentPost.first
post.reviews.create(user_id: 2, rating: 4)
post.reviews.first # id: 2, user_id: 2, reviewable_type: &amp;quot;InstrumentPost&amp;quot;, reviewable_id: 1, rating: 4
post.reviews.first.reviewable_type # &amp;quot;InstrumentPost&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# User 2 reviewing a comment created by User 1
# The reviewable_id is the id of the comment
# The user_id is the id of the user who created the comment review
comment = Comment.first
comment.reviews.create(user_id: 2, rating: 5)
comment.reviews.first # id: 3, user_id: 2, reviewable_type: &amp;quot;Comment&amp;quot;, reviewable_id: 1, rating: 5
comment.reviews.first.rating # 5

# We can then find the user that created the reviewed comment by associating the value of the reviewable_id to the comment id
Comment.where(id: 1) # id: 1, user_id: 1, content: &amp;quot;Comment created by user id 1&amp;quot;
# In this scenario the comment id and user_id just happen to be the same
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;user_1 = User.first
user_2 = User.last

# Reviews User 1 has given (none)
user_1.submitted_reviews # []

# Reviews User 2 has given
user_2.submitted_reviews
# id: 1, user_id: 2, reviewable_type: &amp;quot;User&amp;quot;, reviewable_id: 1, rating: 2
# id: 2, user_id: 2, reviewable_type: &amp;quot;InstrumentPost&amp;quot;, reviewable_id: 1, rating: 4
# id: 3, user_id: 2, reviewable_type: &amp;quot;Comment&amp;quot;, reviewable_id: 1, rating: 5

# Counting the total amount of reviews User 2 has given
user_2.submitted_reviews.count # 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are many other ways to interact with the &lt;code&gt;reviews&lt;/code&gt; model, depending on what data you need to render. It is important to share how the parent models can create a review. As long as the review is associated with a user and a reviewable model, Active Record automatically links the &lt;code&gt;reviewable_id&lt;/code&gt; and &lt;code&gt;reviewable_type&lt;/code&gt; with the associated model.&lt;/p&gt;
&lt;p&gt;Without polymorphism in our Rails examples, there would be many more tables, unnecessary duplicate columns, &lt;code&gt;belongs_to&lt;/code&gt;, and &lt;code&gt;has_many&lt;/code&gt; associations in our models. Polymorphism has lessened the need to join tables, permitting easier and quicker Active Record queries and associations.&lt;/p&gt;
&lt;h2&gt;Wrap Up: Use Polymorphism for Clean and Logical Ruby Code&lt;/h2&gt;
&lt;p&gt;In this post, we explored polymorphism in two distinct environments: biology and Ruby programming. In both cases, polymorphism is the ability of an object to display more than one form.&lt;/p&gt;
&lt;p&gt;We looked at how to implement polymorphism in Ruby through inheritance and duck-typing before diving into the uses of polymorphism in Ruby on Rails specifically.&lt;/p&gt;
&lt;p&gt;Polymorphism can help you write clean and logical code. My goal is to help you add this essential OOP concept to your toolbelt for your current, future, and maybe even past applications.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Using Scientist to Refactor Critical Ruby on Rails Code</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/05/18/using-scientist-to-refactor-critical-ruby-on-rails-code.html"/>
    <id>https://blog.appsignal.com/2022/05/18/using-scientist-to-refactor-critical-ruby-on-rails-code.html</id>
    <published>2022-05-18T00:00:00+00:00</published>
    <updated>2022-05-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Migrate, refactor, and change critical Ruby production code with confidence using the Scientist gem.</summary>
    <content type="html">&lt;p&gt;Ask any software engineer to review key portions of production code, and inevitably, they will point out three things that need to be refactored. So why does so much bad, brittle, or misunderstood code remain running in production?&lt;/p&gt;
&lt;p&gt;The answer is simple: engineers are afraid to touch it. Refactoring tasks get identified and added to the backlog, but rarely make it into the current sprint.&lt;/p&gt;
&lt;p&gt;There are numerous reasons for this. The code may have been written by an engineer who left the team years ago, and no one completely understands it. In other cases, the capability is critical to the business. No one wants to be responsible for a potential outage or loss of revenue.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#39;ll examine how you can use Scientist to migrate, refactor, and change critical Ruby production code with confidence.&lt;/p&gt;
&lt;p&gt;But first, you might ask — can&amp;#39;t we use tests to dig up code issues?&lt;/p&gt;
&lt;h2&gt;This Is What Rails Testing Is For, Right?&lt;/h2&gt;
&lt;p&gt;Yes, and no. It is often difficult to gain complete confidence in code changes before deployment. The unit and system tests pass. It&amp;#39;s good to go, right?&lt;/p&gt;
&lt;p&gt;The reality is that there is no substitute for the real world, i.e. production. What if the data quality is bad or tests are missing? How can you know if the new software will perform well enough to handle production throughput?&lt;/p&gt;
&lt;p&gt;Teams with public services sometimes find that they need to deal with “bugwards compatibility” issues. When a bug has existed in production for a while, clients may code in a way that depends on consistent incorrect behavior. Customers often use software in unexpected ways.&lt;/p&gt;
&lt;h2&gt;Observe Production Changes in Ruby and Rails with Scientist&lt;/h2&gt;
&lt;p&gt;If production is the best place to gain confidence in a change, then consider observing how code behaves there. This may sound scary at first, as the idea of “testing in production” contradicts classic software engineering practices.&lt;/p&gt;
&lt;p&gt;However, the good news is that it’s easy and safe to do so in Ruby and Rails using the &lt;a href=&quot;https://github.com/github/scientist&quot;&gt;Scientist gem&lt;/a&gt;. Scientist&amp;#39;s name is based on the scientific method of conducting experiments to verify a given hypothesis. In this case, our hypothesis is that the new code does the job.&lt;/p&gt;
&lt;p&gt;The reason we can safely take this approach stems from the fact that experiments still use the result of the existing code. New code is only evaluated for observation and comparison purposes, both for accuracy and performance. We mitigate the test coverage concerns discussed earlier by evaluating performance using real-world data and parameters. Experiments typically evaluate a chosen sample rate of requests to minimize the impact on production. However, you can evaluate every request if desired.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s now take a quick look at how Scientist works in a branch by abstraction way.&lt;/p&gt;
&lt;h2&gt;The Branch by Abstraction Pattern in Ruby&amp;#39;s Scientist&lt;/h2&gt;
&lt;p&gt;Scientist&amp;#39;s approach begins with the &lt;a href=&quot;https://martinfowler.com/bliki/BranchByAbstraction.html&quot;&gt;Branch by Abstraction&lt;/a&gt; pattern described by Martin Fowler as making “a large-scale change to a software system in a gradual way.”&lt;/p&gt;
&lt;p&gt;We introduce an abstraction layer to isolate the code being updated. This layer decides which implementation to use so that the experiment is transparent to the rest of the system. The technique is related to using a feature flag that determines the code path.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.blog/2016-02-03-scientist/&quot;&gt;Scientist gem, which originated from Github&lt;/a&gt;, implements this pattern using an experiment. The existing code is referred to as the control, and the new implementation is the candidate. Both code paths are run in randomized order, but only the control result is returned to the client.&lt;/p&gt;
&lt;h2&gt;Using Scientist to Refactor a Ruby Service&lt;/h2&gt;
&lt;p&gt;Consider a &lt;a href=&quot;https://github.com/dbroemme/scientist-labtech-example/blob/master/app/helpers/prime_factor_helper.rb#L20&quot;&gt;Ruby service&lt;/a&gt; that returns the largest prime factor for a given number. Assume that we&amp;#39;ve identified optimizations to prune the required set of candidates, speeding up the service.&lt;/p&gt;
&lt;p&gt;However, service owners want to be sure no bugs were introduced. They also want to observe any performance improvements. Introduce the &lt;a href=&quot;https://github.com/dbroemme/scientist-labtech-example/blob/master/app/helpers/prime_factor_helper.rb#L3&quot;&gt;following code&lt;/a&gt;, modifying clients to call this method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;scientist&amp;#39;

def largest_prime_factor(number)
  science &amp;quot;prime-factors&amp;quot; do |experiment|
    experiment.use { find_largest_prime_factor(number) }     # old way
    experiment.try { improved_largest_prime_factor(number) } # new way
  end  # returns the control value
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, only the &lt;code&gt;use&lt;/code&gt; (control) expression is invoked. To make the experiment worthwhile, define a custom &lt;code&gt;Experiment&lt;/code&gt; class to enable it (100% of the time below) and publish the results (in this case, just logging). Scientist generates fantastic data but it doesn’t do anything with it by default. That part is left up to you.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;scientist/experiment&amp;#39;
require &amp;#39;pp&amp;#39;

class MyExperiment
  include Scientist::Experiment

  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def enabled?
    true
  end

  def raised(operation, error)
    p &amp;quot;Operation &amp;#39;#{operation}&amp;#39; failed with error &amp;#39;#{error.inspect}&amp;#39;&amp;quot;
    super # will re-raise
  end

  def publish(result)
    pp result
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The results of the experiment will be logged, and we can make improvements over time based on the feedback. Once the new code meets the requirements and confidence is high, accomplish cutover to the new implementation by simply replacing the science code with a delegation to the new implementation.&lt;/p&gt;
&lt;h2&gt;LabTech Simplifies Scientist Experiments in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;You can use the &lt;a href=&quot;https://github.com/RealGeeks/lab_tech&quot;&gt;LabTech gem&lt;/a&gt; in your Rails application to easily configure Scientist and handle the results.&lt;/p&gt;
&lt;p&gt;Applications that use AppSignal can use the &lt;code&gt;Appsignal.instrument&lt;/code&gt; &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/instrumentation.html&quot;&gt;custom instrumentation helper&lt;/a&gt; to track how long Scientist events take to complete. Wrap it around the different experiment code blocks to see events appear in the performance timeline.&lt;/p&gt;
&lt;p&gt;Now, going back to LabTech — the web page below simply accepts a number to factor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-05/prime-factor-screenshot.png&quot; alt=&quot;Image of web form to enter a number and see prime factor&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Getting started is easy provided you have access to the console. First, add the LabTech gem to your Gemfile and run a &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;lab_tech&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tables store results and experiment configuration, so run a database migration.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails lab_tech:install:migrations db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The abstraction layer is the same, except the LabTech module is used. The full code is available on GitHub.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def largest_prime_factor(number)
    LabTech.science &amp;quot;prime-factors&amp;quot; do |experiment|
      ...
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, the experiment is disabled, so use the console to enable it in all cases or for a percentage of the time.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bin/rails console
LabTech.enable &amp;quot;prime-factors&amp;quot;
LabTech.enable &amp;quot;prime-factors&amp;quot;, percent: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now run tests and the experiment will be evaluated. For a textual view of results, use either of the following commands from the Rails console.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;LabTech.summarize_results &amp;quot;prime-factors&amp;quot;
LabTech.summarize_errors &amp;quot;prime-factors&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a few successful runs and one manufactured error, here is an example of what the results summary looks like. There is an overview of successes and failures, as well as an ASCII chart showing performance differences.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;--------------------------------------------------------------------------------
Experiment: prime-factors
--------------------------------------------------------------------------------
Earliest results: 2022-04-27T02:42:45Z
Latest result:    2022-05-01T17:27:39Z (5 days)

3 of 4 (75.00%) correct
1 of 4 (25.00%) mismatched

Median time delta: +0.000s  (90% of observations between +0.000s and +0.000s)

Speedups (by percentiles):
      0%  [                         ·         █               ]    +2.4x faster
      5%  [                         ·         █               ]    +2.4x faster
     10%  [                         ·         █               ]    +2.4x faster
     15%  [                         ·         █               ]    +2.4x faster
     20%  [                         ·         █               ]    +2.4x faster
     25%  [                         ·         █               ]    +2.4x faster
     30%  [                         ·         █               ]    +2.4x faster
     35%  [                         ·         █               ]    +2.4x faster
     40%  [                         ·         █               ]    +2.4x faster
     45%  [                         ·         █               ]    +2.4x faster
     50%  [ · · · · · · · · · · · · · · · · · █ · · · · · · · ]    +2.4x faster
     55%  [                         ·         █               ]    +2.4x faster
     60%  [                         ·         █               ]    +2.4x faster
     65%  [                         ·         █               ]    +2.4x faster
     70%  [                         ·                        █]    +6.9x faster
     75%  [                         ·                        █]    +6.9x faster
     80%  [                         ·                        █]    +6.9x faster
     85%  [                         ·                        █]    +6.9x faster
     90%  [                         ·                        █]    +6.9x faster
     95%  [                         ·                        █]    +6.9x faster
    100%  [                         ·                        █]    +6.9x faster
--------------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/ankane/blazer&quot;&gt;Blazer gem&lt;/a&gt; provides a nice way to analyze the results easily. It is simple to install and allows SQL queries to run against tables. The query here shows that the candidate implementation is significantly faster than the original.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-05/blazer-query.png&quot; alt=&quot;Image of experiment observations using Blazer&quot;/&gt;&lt;/p&gt;
&lt;p&gt;In the example prime factoring service, the speedup in the improved implementation comes from a heuristic that eliminates some possible factors to consider. As we consider higher numbers and find a prime factor, we can stop searching after we get to our target number divided by that factor. The new code path only adds &lt;a href=&quot;https://github.com/dbroemme/scientist-labtech-example/blob/master/app/helpers/prime_factor_helper.rb#L38&quot;&gt;one statement&lt;/a&gt; to accomplish this.&lt;/p&gt;
&lt;p&gt;We can also see the reduction in execution time using a Blazer query against the LabTech tables.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-05/speedup.png&quot; alt=&quot;Image of bar graph showing speedup of new code&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Use Cases and Limitations of Scientist&lt;/h2&gt;
&lt;p&gt;Optimal use cases for Scientist include searches, calculations, and code that has no side effects. Code that includes transactional updates or external integrations such as email does not fit cleanly into the model because the capability is run twice (both the old and new implementations).&lt;/p&gt;
&lt;p&gt;This is not a trivial limitation, as it does eliminate several use cases. However, there are some workarounds if the experiment is critical to your success. Consider whether the side effects are relevant or if duplication is an issue. For example, it may not matter in some cases whether two emails are sent during the evaluation. Another option is to have the new code determine the result but not persist it. This would prevent any meaningful performance comparisons. However, it would allow you to verify accuracy.&lt;/p&gt;
&lt;p&gt;Other limitations stem from Scientist’s focus on return values. In some cases, valid results may exhibit differences over time, whether they simply include timestamps in the response or certain factors vary. In many cases, we can write custom comparison logic in the experiment to verify accuracy beyond basic string comparisons.&lt;/p&gt;
&lt;p&gt;Finally, a limitation of LabTech is that it has not been ported to Rails 7 as of the time of writing.&lt;/p&gt;
&lt;h2&gt;Best Practices for Effective Scientist Experiments in Rails&lt;/h2&gt;
&lt;p&gt;Consider these items when implementing your experiments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Rails projects, Scientist can either be configured in an initializer or a wrapper like the Rails LabTech gem. Most Rails applications already have a database, so LabTech leverages ActiveRecord to store results.&lt;/li&gt;
&lt;li&gt;To avoid slowing down development and testing, enable your experiment only in staging and production environments.&lt;/li&gt;
&lt;li&gt;To minimize any potential impact on production, only run the experiment on a percentage of requests. LabTech supports this out of the box as an optional parameter when you enable the experiment (it is initially disabled by default). Using pure Scientist, this logic is easy to code in the experiment’s &lt;code&gt;enabled?&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Some logic is resource-intensive, so a low sampling rate may be a good place to start. As you gain confidence with the results, ramp up the percentage of requests being evaluated.&lt;/li&gt;
&lt;li&gt;You can add context attributes to get the most from your results. The experiment context can be set to a Symbol-keyed Hash of data that is then made available in published results, e.g.:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;experiment.context :user =&amp;gt; user
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap-Up: Observe and Monitor Your Ruby App with Scientist&lt;/h2&gt;
&lt;p&gt;In this post, we explored how to use the Scientist gem to change, migrate, and refactor Ruby code in production.&lt;/p&gt;
&lt;p&gt;We examined Scientist&amp;#39;s origins in the Branch by Abstraction pattern, then dived into refactoring. Next, we saw how LabTech could help with gathering results and your Scientist configuration.&lt;/p&gt;
&lt;p&gt;We then touched on some limitations of Scientist before finally outlining a few best practices.&lt;/p&gt;
&lt;p&gt;You must observe and monitor what is happening in your system. Integrate Scientist into your development process to make critical changes in your Ruby code with greater confidence.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Bootstrapping with Ruby on Rails Generators and Templates</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/05/04/bootstrapping-with-ruby-on-rails-generators-and-templates.html"/>
    <id>https://blog.appsignal.com/2022/05/04/bootstrapping-with-ruby-on-rails-generators-and-templates.html</id>
    <published>2022-05-04T00:00:00+00:00</published>
    <updated>2022-05-04T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s explore the basics of Rails generators and templates, then examine how to customize your Rails app with templates.</summary>
    <content type="html">&lt;p&gt;Rails&amp;#39; batteries-included approach is one of its greatest assets. No other framework makes it so
effortless to get your application off the ground quickly, at least partially due to Rails&amp;#39; generators.&lt;/p&gt;
&lt;p&gt;If
you&amp;#39;ve used Rails for any amount of time, you have come across generators. Need to create a new application? Run &lt;code&gt;rails new&lt;/code&gt;. Need to scaffold a bunch of new models and views?
Run &lt;code&gt;rails generate scaffold&lt;/code&gt;. There are dozens more available to help you get
started rapidly or streamline your workflow.&lt;/p&gt;
&lt;p&gt;But sometimes, using generators is just not enough. You might want to customize the
behavior of these commands or even create your own. In this article, we&amp;#39;ll take a closer look at generators -
in particular, how to create your own custom Rails application using templates.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;What Are Rails Generators?&lt;/h2&gt;
&lt;p&gt;Not to be confused with generator functions (which you might be familiar with from Python or Javascript), Rails
generators are custom &lt;a href=&quot;https://github.com/rails/thor&quot;&gt;Thor&lt;/a&gt; commands that focus on, well, generating things.&lt;/p&gt;
&lt;p&gt;There are lots of examples. You&amp;#39;ll likely be familiar with the model generator (&lt;code&gt;rails generate model&lt;/code&gt;) for creating
new ActiveRecord models or the migration generator (&lt;code&gt;rails generate migration&lt;/code&gt;) for generating new migrations. There is
also &lt;code&gt;rails generate generator&lt;/code&gt; which — you guessed it — creates a new generator!&lt;/p&gt;
&lt;p&gt;Generators can call each other — for example, &lt;code&gt;rails scaffold&lt;/code&gt; will call numerous other generators — and provide
methods to create or modify files, install gems, run specific rake tasks, and much more. Let&amp;#39;s create a
simple model spec generator to understand how this works.&lt;/p&gt;
&lt;h3&gt;Creating Your Own Generator in Ruby on Rails&lt;/h3&gt;
&lt;p&gt;Run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;rails generate generator model_spec
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create several new files in &lt;code&gt;/lib/generators/model_spec&lt;/code&gt;. We can modify &lt;code&gt;model_spec_generator.rb&lt;/code&gt; in folder &lt;code&gt;lib/generators/model_spec/&lt;/code&gt;
to create a model spec file in the correct directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ModelSpecGenerator &amp;lt; Rails::Generators::NamedBase
  source_root File.expand_path(&amp;#39;templates&amp;#39;, __dir__)

  def create_model_spec
    template_file = File.join(&amp;#39;spec/models&amp;#39;, class_path, &amp;quot;#{file_name}_spec.rb&amp;quot;)
    template &amp;#39;model_spec.rb.erb&amp;#39;, template_file
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;template&lt;/code&gt; command will look for a template file in the &lt;code&gt;lib/generators/model_spec/templates&lt;/code&gt; directory and render
it to the specified location — the &lt;code&gt;spec/models&lt;/code&gt; directory. The command will replace ERB-style variables
found in the template file.&lt;/p&gt;
&lt;p&gt;By setting the &lt;code&gt;source_root&lt;/code&gt;, we let our generator know where it can find referenced template files. Template
&lt;code&gt;model_spec.rb&lt;/code&gt; in folder &lt;code&gt;lib/generators/model_spec/templates/&lt;/code&gt; could look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;rails_helper&amp;#39;

RSpec.describe &amp;lt;%= class_name %&amp;gt;, :model
  pending &amp;quot;add some examples to (or delete) #{__FILE__}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have created that file, you can run the generator to create a new spec file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails generate model_spec mymodel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Many gems ship with generators such as this one. In fact, we created a simplified version of the generator &lt;a href=&quot;https://relishapp.com/rspec/rspec-rails/docs/generators&quot;&gt;Rspec ships with&lt;/a&gt;. &lt;a href=&quot;https://github.com/thoughtbot/factory_bot_rails#generators&quot;&gt;FactoryBot has a generator&lt;/a&gt; for factories.
There are many more examples.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The generators in various gems are more sophisticated than the one we created. We could
make our generator take arguments or even hook it into existing generators such as &lt;code&gt;rails scaffold&lt;/code&gt;. Refer to the &lt;a href=&quot;https://guides.rubyonrails.org/generators.html#customizing-your-workflow&quot;&gt;Rails generators documentation&lt;/a&gt; if you want to learn more.&lt;/p&gt;
&lt;p&gt;So generators have the potential to simplify your workflow in an existing application. But can we also use generators to customize setting up a new application?&lt;/p&gt;
&lt;p&gt;Enter templates!&lt;/p&gt;
&lt;h2&gt;Templates in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;As the name suggests, templates are files for customizing your application setup. Don&amp;#39;t confuse these with the template files that we previously discussed!
Under the hood, they are just generators with a specific purpose, as evidenced by the &lt;a href=&quot;https://guides.rubyonrails.org/rails_application_templates.html#template-api&quot;&gt;template API&lt;/a&gt;. While not exactly identical to generators, they are very similar.&lt;/p&gt;
&lt;p&gt;If you have an existing template file, you can use it like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails new myapp -m mytemplate.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rather than specifying a local file, you may also specify a URL. This is especially useful as it allows you to share
application templates.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails new myapp -m https://gist.github.com/appsignal/12345/raw/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are not limited to using templates when running &lt;code&gt;rails new&lt;/code&gt; either. If you&amp;#39;ve already set up an app, you can apply
templates afterward by executing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails app:template LOCATION=http://example.com/template.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Templates can be extremely useful. Who doesn&amp;#39;t want to automate adding the same couple of gems and making the same
configuration changes every time they create a new app? Creating your own application template is great fun — even if
it doesn&amp;#39;t save a lot of time in the long run.&lt;/p&gt;
&lt;h2&gt;Creating Your Own Template in Rails&lt;/h2&gt;
&lt;p&gt;We now know about generators and how to use templates. Let&amp;#39;s create a simple application template to automate some setup
steps.&lt;/p&gt;
&lt;p&gt;How you set up your Rails app is very much down to personal preference, but here&amp;#39;s an example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install the &lt;a href=&quot;https://github.com/bkeepers/dotenv&quot;&gt;dotenv&lt;/a&gt; gem.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;.env.development&lt;/code&gt; file for the development environment.&lt;/li&gt;
&lt;li&gt;Adapt the database configuration file to use environment variables.&lt;/li&gt;
&lt;li&gt;Optionally &lt;a href=&quot;https://github.com/rspec/rspec-rails&quot;&gt;install and set up Rspec&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;#39;s create a local file — &lt;code&gt;mytemplate.rb&lt;/code&gt; — and add &lt;code&gt;dotenv&lt;/code&gt; using the &lt;code&gt;gem&lt;/code&gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;dotenv-rails&amp;#39;, groups: [:development, :test]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we&amp;#39;ve just added the dotenv gem, let&amp;#39;s also create a &lt;code&gt;.env.development&lt;/code&gt; file to contain our database
configuration.&lt;/p&gt;
&lt;p&gt;You can create a new file with specific content by using &lt;code&gt;create_file&lt;/code&gt;. You won&amp;#39;t find this in the template or generator
documentation, as the method is supplied by Thor. You might also come across the alias &lt;code&gt;file&lt;/code&gt;. Application templates are
evaluated in the context of &lt;code&gt;Rails::Generators::AppGenerator&lt;/code&gt;, and that&amp;#39;s exactly &lt;a href=&quot;https://github.com/rails/rails/blob/8f39fbe18a57ae74513edc8561c00a369fe10f08/railties/lib/rails/generators/rails/app/app_generator.rb#L522&quot;&gt;where the &lt;code&gt;file&lt;/code&gt; alias is defined&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;create_file &amp;#39;.env.development&amp;#39;, &amp;lt;&amp;lt;~TXT
  DATABASE_HOST=localhost
  DATABASE_USERNAME=#{app_name}
  DATABASE_PASSWORD=#{app_name}
TXT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;app_name&lt;/code&gt; variable contains the first argument of &lt;code&gt;rails new&lt;/code&gt;. Using this variable, we can ensure our config file
matches the generated application.&lt;/p&gt;
&lt;p&gt;Next, let&amp;#39;s use our environment variables to connect to the database. We could overwrite the entire &lt;code&gt;config/database.yml&lt;/code&gt;
using the &lt;code&gt;create_file&lt;/code&gt; command, but let&amp;#39;s modify it instead using &lt;a href=&quot;https://guides.rubyonrails.org/generators.html#inject-into-file&quot;&gt;&lt;code&gt;inject_into_file&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;inject_into_file &amp;#39;config/database.yml&amp;#39;, after: /database: #{app_name}_development\n/ do &amp;lt;&amp;lt;-RUBY
  host: &amp;lt;%= ENV[&amp;#39;DATABASE_HOST&amp;#39;] %&amp;gt;
  username: &amp;lt;%= ENV[&amp;#39;DATABASE_USERNAME&amp;#39;] %&amp;gt;
  password: &amp;lt;%= ENV[&amp;#39;DATABASE_PASSWORD&amp;#39;] %&amp;gt;
  RUBY
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use both strings or regex with the &lt;code&gt;after&lt;/code&gt; argument to specify where to inject content.&lt;/p&gt;
&lt;p&gt;Of course, using this kind of configuration only makes sense if a user isn&amp;#39;t creating an application with SQLite.
You can check for the presence of certain arguments by using the &lt;code&gt;options&lt;/code&gt; variable. It&amp;#39;s best you read the &lt;a href=&quot;https://github.com/rails/rails/blob/8f39fbe18a57ae74513edc8561c00a369fe10f08/railties/lib/rails/generators/database.rb#L14&quot;&gt;source code of Rails&amp;#39; app generator&lt;/a&gt; to see which options are available.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if options[:database] != &amp;#39;sqlite3&amp;#39;
  # Set up env vars and db configuration
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Last but not least, let&amp;#39;s also allow users to install Rspec if they want to. There are various
methods for taking user input and creating interactive templates. The &lt;code&gt;yes?&lt;/code&gt; method asks a user for
confirmation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if yes?(&amp;#39;Would you like to install Rspec?&amp;#39;)
  gem &amp;#39;rspec-rails&amp;#39;, group: :test
  after_bundle { generate &amp;#39;rspec:install&amp;#39; }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We already know the &lt;code&gt;gem&lt;/code&gt; method, but &lt;code&gt;generate&lt;/code&gt; and &lt;code&gt;after_bundle&lt;/code&gt; are new.&lt;/p&gt;
&lt;p&gt;As mentioned before, Rspec adds its own generators, and you can call these generators (or any other ones, for that
matter) directly from your template. But there is a catch — gems specified with the &lt;code&gt;gem&lt;/code&gt; method are only installed at
the end of the template. Calling &lt;code&gt;generate&lt;/code&gt; with a generator supplied by such a gem would fail — which is why you should register
the command as a callback with &lt;code&gt;after_bundle&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Before we wrap up, a quick word about creating or modifying files. We used &lt;code&gt;create_file&lt;/code&gt; and &lt;code&gt;inject_into_file&lt;/code&gt;,
but there are many other options. You may come across &lt;code&gt;copy_file&lt;/code&gt; or &lt;code&gt;template&lt;/code&gt; when reading different templates. I did not
mention them here to keep things simple. If you want to create more advanced templates, you
should know that these other methods for dealing with files exist.&lt;/p&gt;
&lt;h2&gt;The Result: The Final Template in Rails&lt;/h2&gt;
&lt;p&gt;The final template should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Install dotenv
gem &amp;#39;dotenv-rails&amp;#39;, groups: [:development, :test]

if options[:database] != &amp;#39;sqlite3&amp;#39;
  # Set up .env.development
  create_file &amp;#39;.env.development&amp;#39;, &amp;lt;&amp;lt;~TXT
    DATABASE_HOST=localhost
    DATABASE_USERNAME=#{app_name}
    DATABASE_PASSWORD=#{app_name}
  TXT

  # Modify database.yml
  inject_into_file &amp;#39;config/database.yml&amp;#39;, after: /database: #{app_name}_development\n/ do &amp;lt;&amp;lt;-RUBY
  host: &amp;lt;%= ENV[&amp;#39;DATABASE_HOST&amp;#39;] %&amp;gt;
  username: &amp;lt;%= ENV[&amp;#39;DATABASE_USERNAME&amp;#39;] %&amp;gt;
  password: &amp;lt;%= ENV[&amp;#39;DATABASE_PASSWORD&amp;#39;] %&amp;gt;
  RUBY
  end
end

# Optionally install Rspec
if yes?(&amp;#39;Would you like to install Rspec?&amp;#39;)
  gem &amp;#39;rspec-rails&amp;#39;, group: :test
  after_bundle { generate &amp;#39;rspec:install&amp;#39; }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can test this particular template by running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails new myapp -m mytemplate.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails new myapp --database=postgresql mytemplate.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To take advantage of the custom database configuration.&lt;/p&gt;
&lt;p&gt;As mentioned, this file is perfectly suited to share as a GitHub gist. Adapt it to suit your needs,
upload it, and then share it with your colleagues, friends, and anyone else interested in your custom
app template 😉.&lt;/p&gt;
&lt;h3&gt;Learn More About Rails Generators and Templates&lt;/h3&gt;
&lt;p&gt;Needless to say, we&amp;#39;ve just scratched the surface here.&lt;/p&gt;
&lt;p&gt;Everyone has different preferences for writing and developing Rails apps, so there are numerous
generators and application templates out there. You can learn a lot about doing specific customizations by reading about them. I recommend &lt;a href=&quot;https://github.com/excid3/jumpstart&quot;&gt;Chris
Oliver&amp;#39;s Jumpstart&lt;/a&gt; and &lt;a href=&quot;https://railsbytes.com/&quot;&gt;RailsBytes&lt;/a&gt;, the latter
of which is a community-curated collection of templates.&lt;/p&gt;
&lt;p&gt;There is also &lt;a href=&quot;https://github.com/thoughtbot/suspenders&quot;&gt;Thoughtbot&amp;#39;s Suspenders&lt;/a&gt;, which inspired me to dig deeper into
Rails generators and templates. I even wrote my own application template — &lt;a href=&quot;https://github.com/hschne/schienenzeppelin&quot;&gt;Schienenzeppelin&lt;/a&gt; —
which, while not up-to-date, might still provide some inspiration.&lt;/p&gt;
&lt;h2&gt;Wrap Up: Get Started with Ruby on Rails Generators and Templates&lt;/h2&gt;
&lt;p&gt;In this post, we looked into the basics of Rails generators and how they are used. We created our own generators
to simplify writing new model specs.&lt;/p&gt;
&lt;p&gt;We then delved into templates and learned how you could create a simple template to customize your application
setup. This can be a bit of work, but also quite rewarding. If writing your own templates is not for you, there are
many existing ones online to choose from!&lt;/p&gt;
&lt;p&gt;Happy templating!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>5 Tips to Design Ruby on Rails Transactions the Right Way</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/03/30/5-tips-to-design-ruby-on-rails-transactions-the-right-way.html"/>
    <id>https://blog.appsignal.com/2022/03/30/5-tips-to-design-ruby-on-rails-transactions-the-right-way.html</id>
    <published>2022-03-30T00:00:00+00:00</published>
    <updated>2022-03-30T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Check out these 5 tips to ensure your transactions are readable and well-designed in your Ruby on Rails app.</summary>
    <content type="html">&lt;p&gt;Data integrity problems are among the most common database issues Rails developers face. Besides allowing for proper validation, correctly designed transaction blocks ensure that your data isn&amp;#39;t partially created or updated.&lt;/p&gt;
&lt;p&gt;However, transactions can also harm your application — or even take down your whole database — when not properly designed.&lt;/p&gt;
&lt;p&gt;This article offers a set of good practices for working with transactions. The tips are pretty simple, but they will help make your transactions bulletproof, readable, and relatively safe.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive in!&lt;/p&gt;
&lt;h2&gt;1. Use Bang Methods in Rails When Possible&lt;/h2&gt;
&lt;p&gt;In Rails, method versions with &lt;code&gt;!&lt;/code&gt; can give you confidence that an error will be raised when something goes wrong.&lt;/p&gt;
&lt;p&gt;For example, the &lt;code&gt;#save&lt;/code&gt; method also exists in the &lt;code&gt;save!&lt;/code&gt; version. You might want to use this version in the controller if you don&amp;#39;t want to raise any errors:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def create
  user = User.new(user_params)

  if user.save
    # redirect
  else
    render :new
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above approach won&amp;#39;t work well in transactions. By using &lt;code&gt;save&lt;/code&gt;, we can&amp;#39;t roll back the process when the error is raised. That&amp;#39;s why it is so important to use the &lt;code&gt;!&lt;/code&gt; version of the methods:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Base.transaction do
  user = User.create(user_attributes)
  user.memberships.create(membership_attributes)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above, the transaction succeeds even if the membership record isn&amp;#39;t created, and we end up with a messed-up data structure in the database.&lt;/p&gt;
&lt;p&gt;If you use the following version, the transaction reverts due to an &lt;code&gt;ActiveRecord::RecordNotSaved&lt;/code&gt; error:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Base.transaction do
  user = User.create!(user_attributes)
  user.memberships.create!(membership_attributes)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. Handle Errors in Rails Transactions Properly&lt;/h2&gt;
&lt;p&gt;When it comes to errors in transactions, there are a few rules that you should respect. By following these rules, you&amp;#39;ll have readable and well-working code that will not create confusion among other developers or weird behavior that is hard to debug.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Do Not Rescue from &lt;code&gt;ActiveRecord::StatementInvalid&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ActiveRecord::StatementInvalid&lt;/code&gt; is a special error raised when something on the database level goes wrong. Never rescue from this error. You should always be explicitly notified when something goes wrong with the database query.&lt;/p&gt;
&lt;p&gt;Avoid the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def perform_action(...)
  User.transaction do
    # perform transaction
  end
rescue ActiveRecord::StatementInvalid
  # do something
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Use the Rescue on the Right Level&lt;/h3&gt;
&lt;p&gt;If you use rescue on the following level, you&amp;#39;ll catch the error:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.transaction do
  user.perform_action!
  user.perform_another_action!
rescue SomeError
  # rescue
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The transaction does not roll back because you caught the error. Let the error be raised and catch it outside the transaction block:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def some_method
  User.transaction do
    user.perform_action!
    user.perform_another_action!
  end
rescue SomeError
  # rescue
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above approach, the transaction rolls back in case of an error, and you catch the error. This is the right approach to catch errors raised inside transactions without overwriting transaction behavior.&lt;/p&gt;
&lt;h3&gt;Do Not Catch Generic Errors&lt;/h3&gt;
&lt;p&gt;You should avoid catching generic errors like &lt;code&gt;StandardError&lt;/code&gt; or &lt;code&gt;ArgumentError&lt;/code&gt;. This is more like a general rule for readable and easily testable code, but it&amp;#39;s worth mentioning.&lt;/p&gt;
&lt;p&gt;Catching these errors can make debugging harder, as other places in the code may raise the errors. This could silence some serious issues in your app that are not necessarily related to the place you rescue them.&lt;/p&gt;
&lt;h3&gt;Use ActiveRecord&amp;#39;s Default Rollback Error Wisely&lt;/h3&gt;
&lt;p&gt;ActiveRecord provides a particular error class that you can use inside a transaction to make a silent rollback. You roll back the transaction by raising the &lt;code&gt;ActiveRecord::Rollback&lt;/code&gt; error, but the error isn&amp;#39;t raised outside, as happens with other errors. Keep this behavior in mind and use it wisely.&lt;/p&gt;
&lt;h2&gt;3. Know When to Avoid Using Transactions in Rails&lt;/h2&gt;
&lt;p&gt;As with anything, you should not overuse transactions in your code. For example, a common mistake is to wrap only one query into your transaction. This does not make sense because, if the query doesn&amp;#39;t succeed, there is no need to roll back anything.&lt;/p&gt;
&lt;p&gt;Another common mistake is to wrap code unrelated to your database call into a transaction. You should avoid such an approach, as the transaction will hold the connection unless the code inside the block executes. Limit the code inside the block to call only your database, if possible.&lt;/p&gt;
&lt;h2&gt;4. Understand the Disadvantages of Transactions&lt;/h2&gt;
&lt;p&gt;Transactions help maintain data integrity inside a database, but you should also be aware of their disadvantages. For example, queries wrapped in a transaction block take more DB resources than single queries.&lt;/p&gt;
&lt;p&gt;Another drawback of using transactions is that it leads to more complex code. Transactions can make your code less readable when used incorrectly.&lt;/p&gt;
&lt;h2&gt;5. Use the Transaction Block in the Right Context&lt;/h2&gt;
&lt;p&gt;You can use the transaction method when a class inherits from the &lt;code&gt;ActiveRecord&lt;/code&gt; class. This does not mean that the version you use does not matter. Although it might not matter from a functional perspective, it matters in terms of ensuring your code is readable.&lt;/p&gt;
&lt;p&gt;Three common versions use the transaction method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Base.transaction
Model.transaction
Model.new.transaction
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you use many models and mix instance method invocations with classes inside a block, you should use &lt;code&gt;ActiveRecord::Base.transaction&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Base.transaction do
  attributes = user.prepare_attributes(account)
  membership = Membership.create(attributes)
  LogService.log_creation(user, membership)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you deal mostly with code that belongs to a given model, invoke the transaction method on a class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.transaction do
  user = User.create!(attributes)
  user.log_activity(‘creation’)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you operate on a model instance, it makes sense to invoke the transaction method on the instance level:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;user.transaction do
  user.make_transaction(attributes)
  user.log_activity(‘transaction’)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, these rules are not official. They are just suggestions to make code more readable.&lt;/p&gt;
&lt;h2&gt;Next Steps: Review Transactions in Your Ruby on Rails Project&lt;/h2&gt;
&lt;p&gt;I hope you&amp;#39;ve found these tips for working with transactions in Ruby on Rails useful.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve covered the importance of designing Rails transactions properly to improve data integrity and ensure that your processes perform without surprising side effects.&lt;/p&gt;
&lt;p&gt;However, a proper error handling policy isn&amp;#39;t only beneficial when using transactions — it will also improve your whole codebase. Keep that in mind the next time you expect your code to throw some errors.&lt;/p&gt;
&lt;p&gt;Now is an excellent time to review transactions in your Ruby on Rails project design to avoid errors. Design for efficient and reliable communication with your database to make your application more stable.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The Perils of Parallel Testing in Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/03/16/the-perils-of-parallel-testing-in-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2022/03/16/the-perils-of-parallel-testing-in-ruby-on-rails.html</id>
    <published>2022-03-16T00:00:00+00:00</published>
    <updated>2022-03-16T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Parallel tests can help speed up your tests in Rails, but be aware of the possible pitfalls.</summary>
    <content type="html">&lt;p&gt;Have you ever heard someone complain about their tests being too fast? Me neither.&lt;/p&gt;
&lt;p&gt;Fast tests mean fast feedback. Whether you run them locally or in a continuous integration pipeline,
the earlier your tests finish, the earlier you can react to failures and improve your code. Besides the productivity
gains, it is well known that slow tests make developers grumpy. Nobody likes their developers grumpy.&lt;/p&gt;
&lt;p&gt;With all that said, creating a lightning-fast test suite isn&amp;#39;t always as easy as you&amp;#39;d hope. Luckily, Rails 6 introduced
an exciting feature called &lt;strong&gt;parallel testing&lt;/strong&gt;. It is effortless to get started with and can speed up your tests by a
lot. However, there are some pitfalls to watch out for.&lt;/p&gt;
&lt;h2&gt;What Is Parallel Testing?&lt;/h2&gt;
&lt;p&gt;What does parallel testing even mean?&lt;/p&gt;
&lt;p&gt;When you run a test suite, your test runner will typically spawn a single process to execute your tests one
after the other — or &lt;em&gt;serially&lt;/em&gt;. A single test process uses a single CPU core.
As you can probably imagine, this approach doesn&amp;#39;t take full advantage of modern hardware, which often sports dozens
of CPU cores.&lt;/p&gt;
&lt;p&gt;You may have a fancy MacBook with 10 CPU cores, but sadly, that won&amp;#39;t make your tests go any faster!&lt;/p&gt;
&lt;p&gt;We can change this by distributing individual tests to multiple worker processes. Tests will no longer run after
each other, but next to each other — in &lt;em&gt;parallel&lt;/em&gt;. Running your test suite on two workers is twice as fast
as running the same test suite on a single worker.&lt;/p&gt;
&lt;p&gt;The more cores your machine has, the more worker processes are feasible, and thus, the faster your test suite will
finish. Say your tests usually take eight minutes to run. Running those same tests using four worker processes will
take the runtime down to around two minutes!&lt;/p&gt;
&lt;p&gt;Imagine doing the same on a nice, fat 16 core machine, where we can spawn sixteen workers. Very nice indeed!&lt;/p&gt;
&lt;h2&gt;Configuring Parallel Testing in Rails&lt;/h2&gt;
&lt;p&gt;So how do we get there? Until recently, you could use third-party gems to parallelize your test suite, but starting from
Rails 6, parallel tests come standard. It&amp;#39;s as easy as adding &lt;code&gt;parallelize&lt;/code&gt; to your tests:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ActiveSupport::TestCase
  parallelize(workers: :number_of_processors)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using this configuration, Rails will automatically spawn worker processes based on the number of processors in your
machine. Rails will also create namespaced databases (e.g. &lt;code&gt;database-test-0&lt;/code&gt;, &lt;code&gt;database-test-1&lt;/code&gt;, etc.)
to run your tests against.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s all it takes to get started! Of course, there are some additional configuration options if you need them.&lt;/p&gt;
&lt;p&gt;Sometimes, you may have to perform a specific setup or cleanup for parallel tests. Rails provides two hooks for you
to use — &lt;code&gt;parallelize_setup&lt;/code&gt; and &lt;code&gt;parallelize_teardown&lt;/code&gt;. These are called before and after new worker processes spawn:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ActiveSupport::TestCase
  parallelize_setup do |worker|
    # setup
  end

  parallelize_teardown do |worker|
    # cleanup
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also manually set the number of workers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ActiveSupport::TestCase
  parallelize(workers: 4) # Use 4 worker processes
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Alternatively, use the &lt;code&gt;PARALLEL_WORKERS&lt;/code&gt; environment variable to override an existing configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;PARALLEL_WORKERS=4 rails test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is also the option to use threads instead of workers to parallelize your test suite.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ActiveSupport::TestCase
  parallelize(workers: :number_of_processors, with: :threads)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;with: :threads&lt;/code&gt; is the default option when using JRuby or TruffleRuby. Using threads, in theory, provides slightly better
performance. Threads require less overhead than processes, after all. In practice, however, I never found using threads
all too useful, and you should be fine just sticking to process-based parallelization for the most part.&lt;/p&gt;
&lt;h2&gt;Beware the Pitfalls&lt;/h2&gt;
&lt;p&gt;So all you need to do is add &lt;code&gt;parallelize&lt;/code&gt; to your existing tests to experience incredible speedup? It&amp;#39;s that easy!&lt;/p&gt;
&lt;p&gt;If you are lucky, that really is true. It&amp;#39;s more likely that you will hit some unexpected snags when first adding parallelization to your existing test suite. This certainly was the case for me!&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get one thing out of the way. If you use RSpec rather than Minitest, you are out of luck. RSpec does not
support Rails 6 built-in parallel testing. There is an &lt;a href=&quot;https://github.com/rspec/rspec-rails/issues/2104&quot;&gt;ongoing discussion&lt;/a&gt;
about changing that, but there hasn&amp;#39;t been any significant progress for a while. If you want parallel tests with RSpec,
your best bet is still using third-party gems such as &lt;a href=&quot;https://github.com/grosser/parallel_tests&quot;&gt;grosser/parallel_tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another unexpected issue that you might face is that running a small number of tests in parallel ends up being &lt;em&gt;slower&lt;/em&gt;
then running them serially. Setting up parallel tests comes with a significant overhead — such as creating multiple
databases — which can eliminate any gains you might get from parallelization.&lt;/p&gt;
&lt;p&gt;You might be better off disabling parallel tests for a small number of tests. You can do so by using the
&lt;code&gt;PARALLEL_WORKERS&lt;/code&gt; environment variable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;PARALLEL_WORKERS=0 rails test test/controllers/my_controller_test.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails 7 addressed this by enabling parallel execution only when you execute many tests. So if you&amp;#39;ve already
upgraded, you won&amp;#39;t experience this problem. Per default, the parallelization threshold is set to 50, but you can override
it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;config.active_support.test_parallelization_threshold = 123
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last problem I want to highlight is the one you are most likely to face, and it is also the most insidious and hardest
to deal with. You may start seeing random failures when enabling parallel tests for your test suite. To understand how
parallelization can cause this, let&amp;#39;s look at a simple test case:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class FileTest
  def teardown
    File.delete(&amp;#39;test.txt&amp;#39;)
  end

  test &amp;#39;create file&amp;#39; do
    file = File.write(&amp;#39;test.txt&amp;#39;, &amp;#39;created&amp;#39;)

    assert_path_exists(&amp;#39;test.txt&amp;#39;)
  end

  test &amp;#39;delete file&amp;#39; do
    file = File.write(&amp;#39;test.txt&amp;#39;, &amp;#39;deleted&amp;#39;)

    File.delete(&amp;#39;test.txt&amp;#39;)

    assert_not(File.exist?(&amp;#39;test.txt&amp;#39;))
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides being a bit useless, this test is perfectly fine. It will pass 100% of the time as long as it&amp;#39;s run serially.
Each test is isolated, and executing these tests in random order does not cause them to fail. That changes when you
add multiple processes or threads to the mix.&lt;/p&gt;
&lt;p&gt;When running tests in parallel, the execution order of individual statements in your tests can get changed up due to
CPU scheduling. Looking at the example, you&amp;#39;ll sometimes see execution orders such as this one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Worker 1 executes
file = File.write(&amp;#39;test.txt&amp;#39;, &amp;#39;created&amp;#39;)

# Worker 2 executes
file = File.write(&amp;#39;test.txt&amp;#39;, &amp;#39;deleted&amp;#39;)
File.delete(&amp;#39;test.txt&amp;#39;)
assert_not(File.exist?(&amp;#39;test.txt&amp;#39;))

# Worker 1 executes
assert_path_exists(&amp;#39;test.txt&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since Worker 2 deleted the file before Worker 1&amp;#39;s assertion was executed, the first test will fail — sometimes. To add
insult to injury, a different execution order would sometimes make the second test fail and the first one pass.&lt;/p&gt;
&lt;p&gt;This simple example illustrates an issue that extends not only to files but to any singleton resource that your tests
access in a non-thread-safe way. Suppose you write to a Redis database or an Elasticsearch index. In that case, you&amp;#39;ll likely experience
similar complications. What&amp;#39;s worse, it may take you a while to uncover all tests that cause random failures — and
even more time to fix all of them.&lt;/p&gt;
&lt;p&gt;There is no silver bullet to address flaky parallel tests. In general, you will need to ensure that multiple test processes
do not share resources. For files, use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.5.3/libdoc/tempfile/rdoc/Tempfile.html&quot;&gt;Tempfiles&lt;/a&gt;.
Use &lt;code&gt;parallelize_setup&lt;/code&gt; to create namespaced resources (e.g. Redis databases). And so on.&lt;/p&gt;
&lt;h2&gt;Adding Parallel Testing to Existing Rails Tests&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s say you struggle with random test failures due to parallelization and don&amp;#39;t have the time to fix them. However, you still
want to reap the benefits of parallel testing. You may prefer to enable it only for a subset of your tests.&lt;/p&gt;
&lt;p&gt;Only tests
that call &lt;code&gt;parallelize&lt;/code&gt; will be parallelized after all, and by using concerns or parent classes, you can add parallel
testing to your test suite one test class at a time. You could create a module like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Parallelize
  def self.included(base)
   base.class_eval do
      parallelize(workers: :number_of_processors)

      # ...
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any test class that includes this module will now run in parallel:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyTest &amp;lt; ActiveSupport::TestCase
  include Parallelize
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, you could create a new test class like &lt;code&gt;ParallelTest&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ParallelTest &amp;lt; ActiveSupport::TestCase
  parallelize(workers: :number_of_processors)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, inherit from that for tests that should run in parallel — and leave out those that prove problematic.&lt;/p&gt;
&lt;h2&gt;Parallel Testing as a Bandaid&lt;/h2&gt;
&lt;p&gt;Parallel testing provides impressive speed gains for little effort. Don&amp;#39;t be fooled, though: it is no substitute for other
approaches to improve your test suites&amp;#39; speed, but rather an addition.&lt;/p&gt;
&lt;p&gt;If you find your test suite is slow and can spare the effort, spend some time profiling it and addressing
the root cause/s for the slowness. A slow test suite with parallel testing added to it will get faster, but never as fast as an already
fast test suite that also runs in parallel!&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we looked at what
parallel testing is, how you can set it up and how to configure it. If you need a way to make your tests go faster, parallel testing provides just that.&lt;/p&gt;
&lt;p&gt;You might face some obstacles when adding parallel testing to your test suite. Don&amp;#39;t be surprised when tests that ran
stable for years suddenly start failing. You can work around them by parallelizing only a subset of your test suite.&lt;/p&gt;
&lt;p&gt;No matter which approach you choose, parallel testing is a fantastic tool to speed up your tests!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Import Maps Under the Hood in Rails 7</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/03/02/import-maps-under-the-hood-in-rails-7.html"/>
    <id>https://blog.appsignal.com/2022/03/02/import-maps-under-the-hood-in-rails-7.html</id>
    <published>2022-03-02T00:00:00+00:00</published>
    <updated>2022-03-02T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn how to install JavaScript libraries with import maps and how import maps work under the hood in Rails 7.</summary>
    <content type="html">&lt;p&gt;Import maps is the new feature in Rails 7 that allows us to say goodbye to Node.js and tools like Webpack. There&amp;#39;s no need for bundling anymore. With this new mechanism, you can still manage your JavaScript libraries with a specific version. Instead of one big file, though, your application serves many small JavaScript files.&lt;/p&gt;
&lt;p&gt;It’s essential you know how import maps work to benefit from the newest version of Rails (but don’t worry, you can still use tools like Webpack instead). However, it’s also worth discovering what is happening under the hood. This way, you can better understand the journey from installing a JavaScript library in your project to serving its content to your users.&lt;/p&gt;
&lt;p&gt;This article will show you how to install, serve, and uninstall JavaScript libraries with import maps and what happens under the hood during each phase.&lt;/p&gt;
&lt;h2&gt;The Core of Import Maps&lt;/h2&gt;
&lt;p&gt;The feature itself is not complicated. However, before I show you what happens inside the library, I would like to introduce the JSPM tool. JSPM is a shortcut for JavaScript Package Management. Thanks to this tool, you can load any NPM package inside the browser without extra tooling, and it will be fully optimized.&lt;/p&gt;
&lt;p&gt;Rails uses JSPM to serve JavaScript libraries in your application. You can either download the source files to the vendor directory or serve the code directly from the URL.&lt;/p&gt;
&lt;p&gt;For example, to access the jQuery library, you can call &lt;a href=&quot;https://api.jspm.io/generate?install=jquery&quot;&gt;https://api.jspm.io/generate?install=jquery&lt;/a&gt; URL in your browser, and you will receive the URL to the minified source code in the JSON response. Of course, the service provides some more options for requests, but this knowledge is enough to understand how Rails cooperates with JSPM in the import maps library.&lt;/p&gt;
&lt;h2&gt;Install Import Maps in Your Rails Project&lt;/h2&gt;
&lt;p&gt;The import maps feature is available in Rails as a Ruby gem. If you generate a new project with Rails 7, it’s included in the Gemfile by default. You can add it to existing projects by executing the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle add importmap-rails
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have the gem installed, you have to generate the required files by using the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;./bin/rails importmap:install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What does this command do, exactly? First, it calls the install rake task included in the importmap namespace. The gem delivers the rake task. Inside the task, the standard rails rake task is executed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;system &amp;quot;#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path(&amp;quot;../install/install.rb&amp;quot;,  __dir__)}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;install.rb&lt;/code&gt; file does a few things in the following order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Adds helper to the layout in the head section&lt;/strong&gt; - if the &lt;code&gt;application.html.erb&lt;/code&gt; layout exists in your project, it adds the &lt;code&gt;javascript_importmap_tags&lt;/code&gt; line before the closing &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt; tag. This helper includes JavaScript libraries pinned by import maps. If your project does not include the standard layout file, it will render the information about the helper so you can add it by yourself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Creates app/javascript/application.js file&lt;/strong&gt; - it adds the comment about import maps to let you know that there is a new place where you should define dependencies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Creates vendor/javascript directory&lt;/strong&gt; - this is where JavaScript libraries will be stored if they are not served directly via a URL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Updates sprockets manifest, if it exists&lt;/strong&gt; - sprockets needs to know about the JavaScript files placed inside the &lt;code&gt;vendor/javascript&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Creates config/importmap.rb file&lt;/strong&gt; - this will contain the list of libraries used by Rails (something like package.json file).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copies bin/importmap file and sets the correct permission to execute it&lt;/strong&gt; - this file is used to pin and unpin specific libraries.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As soon as this process finishes, you are ready to install JavaScript libraries using the &lt;code&gt;./bin/importmap&lt;/code&gt; file, and Rails is prepared to serve those files to your visitors.&lt;/p&gt;
&lt;h2&gt;Adding Libraries to Your Rails Project&lt;/h2&gt;
&lt;p&gt;When installing a library with import maps, you have two options: you can either use the code directly from the CDN URL or download the file and serve it directly from your server.&lt;/p&gt;
&lt;p&gt;Let’s explore the CDN option first.&lt;/p&gt;
&lt;h3&gt;Using NPM Packages via JavaScript CDN’s&lt;/h3&gt;
&lt;p&gt;It all starts with the pin command and the library name:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;./bin/importmap pin jquery
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You pass two arguments to the program located inside the &lt;code&gt;./bin/importmap&lt;/code&gt; file. &lt;code&gt;importmap&lt;/code&gt; is an executable file that loads a &lt;code&gt;config/application.rb&lt;/code&gt; file from your project and the import maps commands file.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Import maps use the Thor gem to handle command line programs gently. The first argument, pin, is the command name. When you invoke it, the following things happen under the hood:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Request to JSPM API is executed&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I mentioned before, the gem uses JSPM to get the contents of the package into our application. Therefore, the very first step is the request URL formation. Without any extra parameters passed to the pin command, the request URL is &lt;a href=&quot;https://api.jspm.io/generate&quot;&gt;https://api.jspm.io/generate&lt;/a&gt; with the following parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;install&lt;/code&gt; - [&amp;quot;jquery&amp;quot;] - the param is an array because you can install multiple packages simultaneously.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flattenScope&lt;/code&gt; - true - with this param set to &lt;code&gt;true&lt;/code&gt;, the returned import map format will be more straightforward without scopes, if possible.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;env&lt;/code&gt; - [&amp;quot;browser&amp;quot;, &amp;quot;module&amp;quot;, &amp;quot;production&amp;quot;] - this is the list of environment condition strings.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;provider&lt;/code&gt; - &amp;quot;jspm&amp;quot; - besides JSPM, Skypack, JSdelivr, and Unpkg providers are available to use as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Try accessing &lt;a href=&quot;https://api.jspm.io/generate?install=jquery&amp;flattenScope=true&quot;&gt;https://api.jspm.io/generate?install=jquery&amp;amp;flattenScope=true&lt;/a&gt; in your browser, and you will get a simple JSON response with some simple attributes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Response from JSPM is parsed&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Parsing is a very straightforward phase. As you saw, the response is simple, and all we need is the library name and CDN URL. The &lt;code&gt;Packager&lt;/code&gt; class is responsible for parsing the response in the import map library. Instead of returning the attributes, it returns a complete line that you can use directly in the config:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;%(pin &amp;quot;#{package}&amp;quot;, to: &amp;quot;#{url}&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are not yet familiar with &lt;code&gt;%()&lt;/code&gt;, don’t worry, as it works almost the same as the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;pin \&amp;quot;#{package}\&amp;quot;, to: \&amp;quot;#{url}\&amp;quot;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only difference is that the &lt;code&gt;%()&lt;/code&gt; notation escapes the double quotes automatically. The generated config line passes to another function that handles the config file update process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Import map config is updated&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The last step is to update the config file with the pin definition. Because the previous pin definition can be present, the gem first verifies the config file and searches for the existing library definition. It’s simple to do with the regex:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;/^pin &amp;quot;#{package}&amp;quot;.*$/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the pin definition for the given package is present, the gem replaces the line with the new definition. If the previous definition is not present, the gem adds a new line at the end of the &lt;code&gt;config/importmap.rb&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: When you use this method, keep in mind that this option is free, but one person supported by the community manages the JSPM servers. This situation could change in the future, so I advise you to store libraries locally if you plan to use import maps with production-ready applications.&lt;/p&gt;
&lt;h3&gt;Downloading NPM Packages&lt;/h3&gt;
&lt;p&gt;If you don’t want to use source code from a CDN URL, you can download the library to the &lt;code&gt;vendor&lt;/code&gt; directory inside your application. In this case, the process is very similar to the case with CDN URLs. What&amp;#39;s different is that the code is downloaded into a .js file before the config line is returned for an update.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The download process&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The download process consists of a few small steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The gem calls &lt;code&gt;FileUtils.mkdir_p&lt;/code&gt;, which creates the &lt;code&gt;vendor&lt;/code&gt; directory if it does not already exist.&lt;/li&gt;
&lt;li&gt;The gem calls &lt;code&gt;FileUtils.rm_rf&lt;/code&gt; to remove the previous package file if it exists.&lt;/li&gt;
&lt;li&gt;The gem saves JS code located under the JSPM-provided URL into the .js file and places it into the &lt;code&gt;vendor&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the gem cannot download the package’s contents, you will be notified by a proper error raised in your command line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Config line generation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If the package is named the same as the file with the package source, the config line is straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;%(pin &amp;quot;#{package}&amp;quot; # #{version})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Otherwise, the import map needs to map the package name to the file directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;%(pin &amp;quot;#{package}&amp;quot;, to: &amp;quot;#{filename}&amp;quot; # #{version})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the config line is formatted and returned, the config is updated with the package definition (as described in the CDN URL version above).&lt;/p&gt;
&lt;h2&gt;Using Pinned Packages in Your Rails App&lt;/h2&gt;
&lt;p&gt;The gem extensively uses the Rails engine mechanism to deliver features. The &lt;code&gt;Importmap::Engine&lt;/code&gt; defines a bunch of initializers that perform the configuration.&lt;/p&gt;
&lt;h3&gt;Import Pinned Packages from config/importmap.rb&lt;/h3&gt;
&lt;p&gt;The configuration lines inside the config/importmap.rb file are replaced with references inside the application. First, the gem collects all definitions into a hash where the &lt;code&gt;Struct&lt;/code&gt; object represents each definition to make it easier to access specific attributes.&lt;/p&gt;
&lt;p&gt;You can call &lt;code&gt;Rails.application.importmap.packages&lt;/code&gt; to access the hash with all definitions inside the application.&lt;/p&gt;
&lt;h3&gt;Including References to Packages inside Views&lt;/h3&gt;
&lt;p&gt;When pinned packages are imported, you can include them in your views to provide the code. The gem provides a &lt;code&gt;javascript_importmap_tags&lt;/code&gt; helper, which you can simply render in your layout. It uses &lt;code&gt;Rails.application.importmap&lt;/code&gt; to generate JSON output for all pinned libraries, and with the usage of &lt;code&gt;asset_helper&lt;/code&gt; in Rails, it provides the correct paths to the libraries.&lt;/p&gt;
&lt;p&gt;After adding the following line to your &lt;code&gt;head&lt;/code&gt; section of the layout:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;%= javascript_importmap_tags %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will get the following output (assuming that you are only using jQuery):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script type=&amp;quot;importmap&amp;quot; data-turbo-track=&amp;quot;reload&amp;quot;&amp;gt;
  {
    &amp;quot;imports&amp;quot;: {
      &amp;quot;application&amp;quot;: &amp;quot;/assets/application-37f365cbecf1fa2810a8303f4b6571676fa1f9c56c248528bc14ddb857531b95.js&amp;quot;,
      &amp;quot;jquery&amp;quot;: &amp;quot;https://ga.jspm.io/npm:jquery@3.6.0/dist/jquery.js&amp;quot;
    }
  }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Handling Import Maps by Browser&lt;/h3&gt;
&lt;p&gt;From that moment on, the browser handles the rest. If you are not familiar with the &lt;code&gt;importmap&lt;/code&gt; script type, let me explain it quickly.&lt;/p&gt;
&lt;p&gt;Import map simply controls what is fetched when you use the following statement inside your JavaScript code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import &amp;quot;jquery&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Removing Pinned Packages&lt;/h2&gt;
&lt;p&gt;To remove a pinned package, you have to execute the unpin command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;./bin/importmap unpin react
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You don’t have to pass the &lt;code&gt;--download&lt;/code&gt; parameter because the gem will automatically delete any files related to the package. The removal process consists of three steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Request to the JSPM API&lt;/strong&gt; - the same request is executed when the package is added. The gem does this to get up-to-date package information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Removal of the existing package files&lt;/strong&gt; - the gem uses &lt;code&gt;FileUtils.rm_rf&lt;/code&gt; to remove all related files even if they are placed in directories.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove the config lines related to the package&lt;/strong&gt; - with the usage of &lt;code&gt;File.readlines&lt;/code&gt;, the gem loads all lines from the config file, selects those that do not contain anything related to the removed package and saves them again in the config file. Simple as that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also unpin multiple packages at once; just add their names after the &lt;code&gt;unpin&lt;/code&gt; argument.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve reached the end of this short, yet valuable, journey. You now know that import maps is just a different way of loading JavaScript libraries in your web application. Instead of one big bundled file, you serve multiple smaller files that are easy to cache and control.&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;importmap-rails&lt;/code&gt; gem, it’s easy to adapt import maps in your project. The gem ships with a simple configuration file and command-line interface to install and remove packages using the JSPM.&lt;/p&gt;
&lt;p&gt;Should you use import maps in your next Rails project? As always, it depends. The good thing is that you are not limited to import maps — you can always switch between it, Webpack, and similar, more complex tools.&lt;/p&gt;
&lt;p&gt;Thanks for reading and happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Delayed Job vs. Sidekiq: Which Is Better?</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/02/15/delayed-job-vs-sidekiq-which-is-better.html"/>
    <id>https://blog.appsignal.com/2022/02/15/delayed-job-vs-sidekiq-which-is-better.html</id>
    <published>2022-02-15T00:00:00+00:00</published>
    <updated>2022-02-15T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s take a look at the pros and cons of Delayed Job and Sidekiq, two major job processing systems for Rails apps.</summary>
    <content type="html">&lt;p&gt;Most applications need background jobs for mailers, regular clean-ups, or any other time-consuming operation that doesn&amp;#39;t require a user to be present.&lt;/p&gt;
&lt;p&gt;Several gems support job queues and background processing in the Rails world — &lt;a href=&quot;https://github.com/collectiveidea/delayed_job&quot;&gt;Delayed Job&lt;/a&gt; and &lt;a href=&quot;https://github.com/mperham/sidekiq&quot;&gt;Sidekiq&lt;/a&gt; being the two most popular ones.&lt;/p&gt;
&lt;p&gt;In this post, we will take a detailed look at Delayed Job and Sidekiq, including how they fare against each other.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s go!&lt;/p&gt;
&lt;h2&gt;A Quick Introduction to Delayed Job&lt;/h2&gt;
&lt;p&gt;Delayed Job is a direct extraction from Shopify and uses a table to maintain all background jobs.
It follows a very simple pattern. Any Ruby object that responds to a &lt;code&gt;perform&lt;/code&gt; method can be enqueued in the jobs table.&lt;/p&gt;
&lt;p&gt;In addition, if you don&amp;#39;t need to maintain special job objects (although this is highly recommended for testability and clear separation of long-running operations), it also allows you to call &lt;code&gt;.delay.method(params)&lt;/code&gt; on any Ruby object. It will process the method in the background.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/collectiveidea/delayed_job#queuing-jobs&quot;&gt;Delayed Job README&lt;/a&gt; does a great job of explaining all common usage patterns.&lt;/p&gt;
&lt;p&gt;Many teams choose Delayed Job because it is simple and uses their already existing database. They don&amp;#39;t need to spend/maintain other resources.&lt;/p&gt;
&lt;p&gt;However, it will still take up space in your database table. If you have too many jobs queued at the same time, you might need more disk space to accommodate them all.&lt;/p&gt;
&lt;h2&gt;A Quick Introduction to Sidekiq&lt;/h2&gt;
&lt;p&gt;Sidekiq, on the other hand, uses Redis as its data store to maintain all job metadata.
This comes with the obvious benefit of being much faster than the regular database systems Delayed Jobs uses.
In addition to this, each Sidekiq process spawns multiple threads to process the jobs even faster.&lt;/p&gt;
&lt;p&gt;For each background job in Sidekiq, we need a specialized class that includes the &lt;code&gt;Sidekiq::Worker&lt;/code&gt; concern and responds to the &lt;code&gt;perform&lt;/code&gt; method.
To enqueue the job, we need to call &lt;code&gt;perform_async(arg1, arg2)&lt;/code&gt; on the worker with the arguments.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/mperham/sidekiq/wiki/Getting-Started&quot;&gt;Sidekiq &amp;#39;Getting Started&amp;#39; guide&lt;/a&gt; explains this and other usage patterns in good detail.&lt;/p&gt;
&lt;h2&gt;Using Active Job with Delayed Job or Sidekiq&lt;/h2&gt;
&lt;p&gt;Rails already provides a mature job framework for top-level declaration and handling of jobs.
Both Delayed Job and Sidekiq support running jobs through ActiveJob&amp;#39;s unified API.
Just inherit from &lt;code&gt;ApplicationJob&lt;/code&gt; and call &lt;code&gt;perform_later&lt;/code&gt; on your job class to enqueue the job to the configured queuing backend.&lt;/p&gt;
&lt;p&gt;The advantage of running jobs with Active Job is that your application code becomes framework agnostic, and switching from Delayed Job to Sidekiq (or vice versa) becomes pretty easy. The &lt;a href=&quot;https://edgeapi.rubyonrails.org/classes/ActiveJob/TestHelper.html&quot;&gt;&lt;code&gt;ActiveJob::TestHelper&lt;/code&gt;&lt;/a&gt; also makes testing enqueued jobs a breeze.&lt;/p&gt;
&lt;p&gt;But the abstraction provided by Active Job also comes with a performance overhead, as job data has to be wrapped before it&amp;#39;s pushed to the store.
Sidekiq claims that ActiveJob is about 2-20x slower when pushing to Redis, with ~3x the processing overhead.&lt;/p&gt;
&lt;h2&gt;Delayed Jobs vs. Sidekiq&lt;/h2&gt;
&lt;p&gt;Now that we know the basics of Delayed Jobs and Sidekiq, let&amp;#39;s dive deeper into their differences and what each brings to the table.&lt;/p&gt;
&lt;h3&gt;The Features&lt;/h3&gt;
&lt;p&gt;For basic applications, both Sidekiq and Delayed Job provide a good set of features out of the box.
These include assigning job priorities, named queues, and auto-retry on failures.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Delayed Job also provides a way to configure max run time out of the box (Sidekiq does not).&lt;/p&gt;
&lt;p&gt;Sidekiq, on the other hand, provides support for &lt;a href=&quot;https://github.com/mperham/sidekiq/wiki/Middleware&quot;&gt;Middleware&lt;/a&gt; to update job metadata, skip queuing a job, or execute a job.
Sidekiq supports more callbacks, though &lt;a href=&quot;https://github.com/collectiveidea/delayed_job/#hooks&quot;&gt;some hooks are available for Delayed Job apps&lt;/a&gt;. Instead of callbacks, you can use Delayed Job with Active Job (namely, the &lt;code&gt;before_enqueue&lt;/code&gt; and &lt;code&gt;around_perform&lt;/code&gt; callbacks inbuilt into Rails).&lt;/p&gt;
&lt;p&gt;Web UI is another feature that comes out of the box with Sidekiq.
This provides historical statistics about jobs and information about workers, currently enqueued and dead jobs.
You can perform operations like deleting or running jobs immediately without going through the console.&lt;/p&gt;
&lt;p&gt;Delayed Job does not have an inbuilt Web UI, but &lt;a href=&quot;https://github.com/ejschmitt/delayed_job_web&quot;&gt;&lt;code&gt;delayed_job_web&lt;/code&gt;&lt;/a&gt; gives access to a basic Web UI with similar features to Sidekiq&amp;#39;s.&lt;/p&gt;
&lt;h3&gt;Sidekiq Wins at Performance&lt;/h3&gt;
&lt;p&gt;Performance-wise, Sidekiq beats Delayed Job quite convincingly.
According to &lt;a href=&quot;https://github.com/mperham/sidekiq#performance&quot;&gt;Sidekiq&amp;#39;s open-source benchmark&lt;/a&gt;, it is approximately 30x faster than Delayed Job.
There are two major reasons for this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Redis is much faster at querying data than traditional databases like Postgres because it stores data in memory as opposed to the disk.&lt;/li&gt;
&lt;li&gt;Delayed Job runs a single thread to process jobs, compared to Sidekiq, which uses multiple threads.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While all of this looks great on paper, the differences do not matter much unless you work on a big scale (something like 10k jobs per minute).
The exact number also depends on the average run time of a job. The longer the run time, the less the performance overhead of Delayed Job matters.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re worried about the performance of Delayed Job, you can make some performance optimizations.
The exact indexes to use will depend on the statistics of your job system.
For example, if you use multiple queues and only one gets a major chunk of jobs, a simple index on the queue column (&lt;code&gt;add_index :delayed_jobs, :queue&lt;/code&gt;) can significantly improve performance.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In AppSignal, &lt;a href=&quot;https://www.appsignal.com/ruby/sidekiq-monitoring&quot;&gt;Sidekiq magic dashboard&lt;/a&gt; gets automatically generated and it enables you to monitor queue length, queue latency, job duration, job statuses, memory usage, etc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-02/sidekiq-dashboard.png&quot; alt=&quot;Sidekiq&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Deployment&lt;/h3&gt;
&lt;p&gt;Both Delayed Job and Sidekiq have a similar deployment strategy for workers.
Using Heroku, you just need to add entries inside your &lt;code&gt;Procfile&lt;/code&gt; to start the job processor and run the workers.&lt;/p&gt;
&lt;p&gt;For Sidekiq:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;worker: bundle exec sidekiq -t 25 -c ${SIDEKIQ_CONCURRENCY:-5} [-q name,priority [-q another_queue,another_priority]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Delayed Job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;worker: [QUEUE=x,y,z] bundle exec rails jobs:work
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Memory&lt;/h3&gt;
&lt;p&gt;Here&amp;#39;s where things start to get a bit more interesting. Sidekiq has a concurrency option to control how many threads it runs.
Most of the Sidekiq vs. Delayed Job benchmarks mention Sidekiq&amp;#39;s very high concurrency of up to 25 threads, which contributes to its super-fast performance.&lt;/p&gt;
&lt;p&gt;But in a real setting, you have to limit the threads to something more conservative.
The actual number depends on how heavy your application is and what kinds of jobs you perform.
What I have seen in practice is that if you run a worker on 512MB memory (equivalent to &lt;code&gt;standard-1x&lt;/code&gt; on Heroku), the number of threads is somewhere between 2 and 5 instead of 25.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mikeperham.com/2018/04/25/taming-rails-memory-bloat/&quot;&gt;&amp;#39;Taming Rails memory bloat&amp;#39; by Mike Perham&lt;/a&gt;, the creator of Sidekiq, discusses memory issues in more detail and is well worth a read.
I won&amp;#39;t jump into the full discussion, but he recommends that you set &lt;code&gt;MALLOC_ARENA_MAX=2&lt;/code&gt; on all workers that run Sidekiq.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;jemalloc&lt;/code&gt; instead of regular &lt;code&gt;malloc&lt;/code&gt; helps too. The exact way to do this depends on the platform you use, but it is pretty simple on Heroku. Just set &lt;a href=&quot;https://github.com/gaffneyc/heroku-buildpack-jemalloc.git&quot;&gt;heroku-buildpack-jemalloc&lt;/a&gt; as the first buildpack (ahead of the &lt;code&gt;heroku/ruby&lt;/code&gt; buildpack).&lt;/p&gt;
&lt;h3&gt;Delayed Job Uses Simpler Resources&lt;/h3&gt;
&lt;p&gt;As we discussed, Delayed Job runs on your existing database instance.
You might need to increase:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the available memory&lt;/li&gt;
&lt;li&gt;disk space&lt;/li&gt;
&lt;li&gt;max connections&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;depending on the job load or the number of workers you run. But the only resource you need is the job processor.&lt;/p&gt;
&lt;p&gt;On the other hand, Sidekiq requires a Redis instance to handle jobs.
If you also use Redis as a cache store, it is recommended that you use a separate instance configured as a &amp;quot;persistent store&amp;quot; for Sidekiq jobs.&lt;/p&gt;
&lt;p&gt;Since Redis works best when everything fits in memory, if you have too many jobs (for example, if Sidekiq stops processing them for some time due to an issue in the app), it might take some downtime to clear everything up.
This is especially troublesome if you have Redis on the same server as your app.
They will start competing for memory, leading to swapping and eventually destroying your app&amp;#39;s performance.&lt;/p&gt;
&lt;p&gt;One important point to note about Redis is that it has to be configured with &lt;code&gt;maxmemory-policy noeviction&lt;/code&gt; to avoid silent drops of Sidekiq&amp;#39;s data.
Otherwise, you will find yourself missing jobs that need to be performed, without any trace.&lt;/p&gt;
&lt;h4&gt;A Side-Note: Paid Upgrades in Sidekiq&lt;/h4&gt;
&lt;p&gt;If you need extra features, Sidekiq comes with &lt;code&gt;Pro&lt;/code&gt; and &lt;code&gt;Enterprise&lt;/code&gt; versions.&lt;/p&gt;
&lt;p&gt;The most notable addition to Pro is &lt;code&gt;Batch Jobs&lt;/code&gt; that can run in parallel, be monitored, and interact as a group, invoking a callback when all jobs are done. Pro also has improved reliability features to ensure that no jobs are dropped silently, even during network problems.&lt;/p&gt;
&lt;p&gt;The Enterprise version comes with yet more features. If you are looking for something that a regular Sidekiq installation can&amp;#39;t solve, &lt;a href=&quot;https://sidekiq.org/&quot;&gt;explore the paid Sidekiq features&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In practice, the free version of Sidekiq still works great.
But it is good to know that there are paid options you can upgrade to as needed, instead of switching to a different solution.&lt;/p&gt;
&lt;h3&gt;Community and Development Status: Sidekiq Has the Edge&lt;/h3&gt;
&lt;p&gt;There is a huge community behind both Sidekiq and Delayed Job. However, it is not always easy to find quick answers to your questions in StackOverflow or the official documentation.&lt;/p&gt;
&lt;p&gt;On the development side, things are not looking very bright for Delayed Job.
There was some minor work done on Delayed Job in December 2021 and January 2022, but it doesn&amp;#39;t seem like it is getting any major developments going forward. It appears to be in maintenance-only mode, and there are a lot of open issues on Github.&lt;/p&gt;
&lt;p&gt;In contrast, Sidekiq is still under active development, and its creator is working full time on it.
There are very few open issues, and they get addressed regularly.&lt;/p&gt;
&lt;h2&gt;Wrap-up: Sidekiq or Delayed Job? It Depends on Your Needs&lt;/h2&gt;
&lt;p&gt;In this post, we covered two major job processing systems for Rails applications — Sidekiq and Delayed Job — taking a look at some of their pros and cons.&lt;/p&gt;
&lt;p&gt;There are different use cases for each. It all depends on the budget and scale of your operation.&lt;/p&gt;
&lt;p&gt;If performance and long-term maintainability are of importance, Sidekiq is a no-brainer.
On the other hand, if running costs are a concern, Delayed Job can help you there.&lt;/p&gt;
&lt;p&gt;Whether you choose Delayed Job or Sidekiq, good luck with your project and happy coding!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;👋 If you liked this article, take a look at other Ruby (on Rails) performance articles in our &lt;a href=&quot;https://www.appsignal.com/ruby#ruby-monitoring-checklist&quot;&gt;Ruby performance monitoring checklist&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Test and Optimize Your Ruby on Rails Database Performance</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/01/26/test-and-optimize-your-ruby-on-rails-database-performance.html"/>
    <id>https://blog.appsignal.com/2022/01/26/test-and-optimize-your-ruby-on-rails-database-performance.html</id>
    <published>2022-01-26T00:00:00+00:00</published>
    <updated>2022-01-26T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Explore three common ways to discover and test database performance problems in Rails, and seven ways to optimize performance.</summary>
    <content type="html">&lt;p&gt;In this article, you will learn how to test database performance in Rails and solve some of the most common database performance issues.&lt;/p&gt;
&lt;p&gt;When you develop a Rails application, ActiveRecord is the default tool that manages your database. ActiveRecord provides an easy and fast interface to query and insert data using commands like &lt;code&gt;.where&lt;/code&gt;, &lt;code&gt;.save&lt;/code&gt;, &lt;code&gt;.create&lt;/code&gt;, and &lt;code&gt;.update&lt;/code&gt;. Rails does the work of converting these commands to SQL queries, which is a good thing, but sometimes can cause performance issues. It is important that you understand some of the common issues and how to optimize performance.&lt;/p&gt;
&lt;h2&gt;A Quick Note on ActiveRecord in Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Rails ActiveRecord is a layer in Model-View-Controller (MVC) that manages your database by representing it as a business object. The ActiveRecord pattern uses the ORM technique to connect objects of an application to the relational database table management system.&lt;/p&gt;
&lt;p&gt;Now, let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;3 Ways to Identify and Test Database Performance Issues in Rails&lt;/h2&gt;
&lt;h3&gt;1. Run Explain on ActiveRecord Queries&lt;/h3&gt;
&lt;p&gt;An explain statement displays information about the execution plan of an SQL query - how a query will be executed, including how many rows will be scanned, what index will be used, and how tables are joined.&lt;/p&gt;
&lt;p&gt;The execution plan helps us figure out what slows down query execution by looking at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What index you should add to improve the performance of a query.&lt;/li&gt;
&lt;li&gt;If tables are joined in an optimal order. You can use &lt;code&gt;STRAIGHT_JOIN&lt;/code&gt; to force table order in a join statement for better performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Explain works on SELECT, DELETE, INSERT, REPLACE and UPDATE statements as well.&lt;/p&gt;
&lt;p&gt;Using explain with ActiveRecord is very straightforward. Enter the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;2.4.2 :004 &amp;gt; User.where(id: 1).explain

 =&amp;gt; EXPLAIN for: SELECT &amp;quot;users&amp;quot;.* FROM &amp;quot;users&amp;quot; WHERE &amp;quot;users&amp;quot;.&amp;quot;id&amp;quot; = $1 [[&amp;quot;id&amp;quot;, 1]]
                                QUERY PLAN
--------------------------------------------------------------------------
 Index Scan using users_pkey on users  (cost=0.14..8.16 rows=1 width=792)
   Index Cond: (id = 1)
(2 rows)

2.4.2 :005 &amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding &lt;code&gt;.explain&lt;/code&gt; at the end of the command provides the query plan for the ActiveRecord commands. The above example is a very simple query that uses the &lt;code&gt;id&lt;/code&gt; (primary key) to query the table. The output of the &lt;code&gt;explain&lt;/code&gt; statement shows that it is using the &lt;code&gt;pkey&lt;/code&gt;. From this, you can be sure that the statement is optimal and fast.&lt;/p&gt;
&lt;p&gt;You can try adding an &lt;code&gt;.explain&lt;/code&gt; command to your slow queries to uncover the order of execution, along with the index used. If the query plan shows &lt;code&gt;Seq Scan&lt;/code&gt;, the index is not being used and requires a query change or a new index to be added.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s another example with join:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;2.4.2 :062 &amp;gt; User.where(id: 1).joins(:collaborations).explain

 =&amp;gt; EXPLAIN for: SELECT &amp;quot;users&amp;quot;.* FROM &amp;quot;users&amp;quot; INNER JOIN &amp;quot;collaborations&amp;quot; ON &amp;quot;collaborations&amp;quot;.&amp;quot;user_id&amp;quot; = &amp;quot;users&amp;quot;.&amp;quot;uid&amp;quot; WHERE &amp;quot;users&amp;quot;.&amp;quot;id&amp;quot; = $1 [[&amp;quot;id&amp;quot;, 1]]
                                      QUERY PLAN
--------------------------------------------------------------------------------------
 Hash Join  (cost=8.17..27.31 rows=4 width=792)
   Hash Cond: ((collaborations.user_id)::text = (users.uid)::text)
   -&amp;gt;  Seq Scan on collaborations  (cost=0.00..17.20 rows=720 width=32)
   -&amp;gt;  Hash  (cost=8.16..8.16 rows=1 width=792)
         -&amp;gt;  Index Scan using users_pkey on users  (cost=0.14..8.16 rows=1 width=792)
               Index Cond: (id = 1)
(6 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the user table is joined with the collaborations table. When you look into the query plan, the collaboration table is using &lt;code&gt;seq scan&lt;/code&gt; and is executing first — whereas the user table is using the &lt;code&gt;pkey&lt;/code&gt; index and is executing later. You can add an index on the &lt;code&gt;user_id&lt;/code&gt; column in the collaborations table to optimize this query. Explain helps in breaking down the query, so you can figure out where optimization is needed.&lt;/p&gt;
&lt;h3&gt;2. Measure Key Database Metrics&lt;/h3&gt;
&lt;p&gt;Query time is not the only metric to measure to see if a query is performant — look at several other database metrics, including:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CPU usage&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Disk queue for waiting IO&lt;/li&gt;
&lt;li&gt;Network bandwidth for inbound and outbound traffic&lt;/li&gt;
&lt;li&gt;Available disk space&lt;/li&gt;
&lt;li&gt;Throughput&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The query may slow down when these metrics go over a certain threshold. You must look at a data point across a time range to understand performance issues.&lt;/p&gt;
&lt;p&gt;The data point that needs to be measured depends on lots of factors, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The database type:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relational&lt;/li&gt;
&lt;li&gt;In-memory&lt;/li&gt;
&lt;li&gt;No-SQL&lt;/li&gt;
&lt;li&gt;Data-Warehouse&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How the server is hosted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On-premises&lt;/li&gt;
&lt;li&gt;On the cloud&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is no single way to monitor database metrics — it depends on different factors.&lt;/p&gt;
&lt;h3&gt;3. Measure Rails App Performance using AppSignal&lt;/h3&gt;
&lt;p&gt;It can get difficult to manage all your performance metrics without a central place that gives you visibility over all queries. Adding performance code to every code block can be cumbersome and unmanageable.&lt;/p&gt;
&lt;p&gt;With tools like AppSignal, you can easily integrate performance measurement into your application. AppSignal supports Rails out of the box. Learn about the simple AppSignal installation process from the &lt;a href=&quot;https://docs.appsignal.com/ruby/&quot;&gt;&amp;#39;AppSignal for Ruby&amp;#39; documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some of the critical metrics to keep an eye on are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Slow queries&lt;/li&gt;
&lt;li&gt;Database performance based on throughput&lt;/li&gt;
&lt;li&gt;N+1 queries&lt;/li&gt;
&lt;li&gt;Database latency&lt;/li&gt;
&lt;li&gt;Number of active connections&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;#39;s how an AppSignal dashboard might look:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-01/dashboard-demo.png&quot; alt=&quot;demo&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;7 Ways to Optimize Ruby on Rails Database Performance&lt;/h2&gt;
&lt;h3&gt;1. Eager Loading for N+1 Queries&lt;/h3&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;N+1 queries are the most common database performance problem. Let us see an example of an &lt;code&gt;N+1&lt;/code&gt; query where you have two models — user and project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ActiveRecord::Base
  has_many :projects
end

class Project &amp;lt; ActiveRecord::Base
  belongs_to :user
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if you want to find the user and project names, run the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;users = User.where(country: &amp;quot;Germany&amp;quot;)

users.each do |user|
  puts &amp;quot;#{user.name} | #{user.project.name}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above will query the database with each loop and cause performance issues. The total number of queries executed will be the number of users + 1.&lt;/p&gt;
&lt;p&gt;Eliminating this issue is very simple: eager load the association. Simply add &lt;code&gt;.includes(:projects)&lt;/code&gt; at the end of the query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;users = User.where(country: &amp;quot;Germany&amp;quot;).includes(:projects)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, executing the loop will not query the database, as the query above eager loads the projects:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;users.each do |user|
  puts &amp;quot;#{user.name} | #{user.project.name}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails 6.1 provides &lt;a href=&quot;https://github.com/rails/rails/pull/37400&quot;&gt;strict loading&lt;/a&gt; to ensure that the association is eager loaded before it is accessed. To enable strict loading, add the following line in the model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  has_many :projects, strict_loading: true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when you try to access the projects without eager loading, Rails will throw an &lt;code&gt;ActiveRecord::StrictLoadingViolationError&lt;/code&gt; exception.&lt;/p&gt;
&lt;p&gt;If you don&amp;#39;t have Rails 6.1, you can use gems like &lt;a href=&quot;https://github.com/flyerhzm/bullet&quot;&gt;Bullet&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;2. Use a Database Index&lt;/h3&gt;
&lt;p&gt;Databases provide indexes to help retrieve data faster. Using the explain command that we covered earlier, you will be able to figure out if a query is using a proper index.&lt;/p&gt;
&lt;p&gt;You can change a slow query to use an already existing index or an added index that helps improve performance.&lt;/p&gt;
&lt;p&gt;There are four different types of indexes in MySQL:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Primary key - Index is automatically added to the primary key, which also ensures that it is unique&lt;/li&gt;
&lt;li&gt;Unique - Unique key index ensures that the items added in an attribute are always unique&lt;/li&gt;
&lt;li&gt;Index - Added to attributes other than the primary key&lt;/li&gt;
&lt;li&gt;Full text - Helps to query against character-based data&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;An index is stored in a &lt;a href=&quot;https://www.geeksforgeeks.org/introduction-of-b-tree-2/&quot;&gt;B-Tree&lt;/a&gt; or a Hash format.&lt;/p&gt;
&lt;p&gt;Indexes can be added to a single field or created as a composition of multiple fields. A composite index is useful to optimize queries that include multiple fields. When only one index is used, it requires a large dataset scan.&lt;/p&gt;
&lt;p&gt;For example, in the following query, there are two fields:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.where(project: &amp;quot;abc&amp;quot;, country: &amp;quot;Germany&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There could be a lot of users in the project &lt;code&gt;&amp;quot;abc&amp;quot;&lt;/code&gt; and the country field will be scanned. This can be a slow process because of the large result dataset. In this case, you can add a composite index to both the project and country fields to improve performance.&lt;/p&gt;
&lt;p&gt;You can add the index to Rails with the following ActiveRecord migration commands:&lt;/p&gt;
&lt;p&gt;Single index:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AddIndexOnProjectToUsers &amp;lt; ActiveRecord::Migration[6.0]
  def change
    add_index :users, :project
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Composite index:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AddIndexOnProjectAndCountryToUsers &amp;lt; ActiveRecord::Migration[6.0]
  def change
    add_index :users, [:project, :country]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. Use Limits&lt;/h3&gt;
&lt;p&gt;The more records that are returned, the slower performance can get. It is better to do multiple queries than a single query that returns a large data set.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.where(country: &amp;quot;Germany&amp;quot;).limit(100)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To fetch the next 100 batches, you can use the offset:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.where(country: &amp;quot;Germany&amp;quot;).limit(100).offset(100)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will significantly improve performance. One thing to keep in mind is that with a higher offset, a query gets slower. Add a limit to the offset.&lt;/p&gt;
&lt;h3&gt;4. Use &lt;code&gt;find_each&lt;/code&gt; To Load a Large Number of Items&lt;/h3&gt;
&lt;p&gt;When iterating over records, batch them in Rails for better performance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.where(country: &amp;quot;Germany&amp;quot;).each do |user|
  puts user
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will query all the records in the database once and cause memory and database performance issues.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;find_each&lt;/code&gt; or &lt;code&gt;find_in_batches&lt;/code&gt; will help improve performance by doing the same operation in a batch:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.where(country: &amp;quot;Germany&amp;quot;).find_each do |user|
  puts user
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, &lt;code&gt;find_each&lt;/code&gt; queries result in a batch of 1,000. You can change the batch size by defining it as an argument:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.where(country: &amp;quot;Germany&amp;quot;).find_each(:batch_size: 5000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use &lt;code&gt;find_in_batches&lt;/code&gt; based on the operation you need to perform. The difference between &lt;code&gt;find_in_batches&lt;/code&gt; and &lt;code&gt;find_each&lt;/code&gt; is that &lt;code&gt;find_in_batches&lt;/code&gt; yields the result as an array of models instead of individual records.&lt;/p&gt;
&lt;h3&gt;5. Select Your Required Field Using Pluck&lt;/h3&gt;
&lt;p&gt;The Pluck command directly converts a query&amp;#39;s result to an array instead of an ActiveRecord object.&lt;/p&gt;
&lt;p&gt;If a query returns large results, using Pluck will improve code performance. Pluck will only select a required field from a database:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.pluck(:id)
# SELECT &amp;quot;users&amp;quot;.&amp;quot;id&amp;quot; FROM &amp;quot;users&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result is fetched from an index, not the main table, and is more effective with queries involving sorts.&lt;/p&gt;
&lt;h3&gt;6. Use Bulk Operations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Bulk Delete&lt;/strong&gt;
A delete operation looping over the ActiveRecord object will delete records one at a time:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;users = User.where(country: &amp;quot;Germany&amp;quot;)

users.each do |user|
  user.delete
end

# &amp;gt;
# DELETE FROM users WHERE id = 1;
# DELETE FROM users WHERE id = 5;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deleting each record requires many queries to be made to the database. Instead, it&amp;#39;s optimal to use a single bulk &lt;code&gt;delete_all&lt;/code&gt; query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;users = User.where(country: &amp;quot;Germany&amp;quot;)

users.delete_all

# &amp;gt;
# DELETE FROM users WHERE users.country = &amp;#39;Germany&amp;#39;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Bulk Create&lt;/strong&gt;
Some people don&amp;#39;t realize that similar to bulk delete, you can also perform a bulk insert with ActiveRecord. This can reduce the &lt;code&gt;n&lt;/code&gt; number of queries to only one. The &lt;code&gt;ActiveRecord::Base&lt;/code&gt; &lt;code&gt;create&lt;/code&gt; method accepts an array of hashes as input:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;users = [
  {name: &amp;quot;Milap&amp;quot;, email: &amp;quot;milap@country.com&amp;quot;, country: &amp;quot;Germany&amp;quot;},
  {name: &amp;quot;Aastha&amp;quot;, email: &amp;quot;aastha@country.com&amp;quot;, country: &amp;quot;Germany&amp;quot;}
]

User.create(users)

# INSERT INTO users (name, email)
# VALUES
#   (&amp;#39;Milap&amp;#39;, &amp;#39;milap@country.com&amp;#39;, &amp;#39;Germany&amp;#39;),
#   (&amp;#39;Aastha&amp;#39;, &amp;#39;aastha@country.com&amp;#39;, &amp;#39;Germany&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7. Use In-Memory Calculation if Needed&lt;/h3&gt;
&lt;p&gt;In some instances, an in-memory calculation is preferable to querying. Suppose we want to find countries in our database that do not have a record of users:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;countries = [
  &amp;quot;Germany&amp;quot;,
  &amp;quot;UK&amp;quot;,
  &amp;quot;Norway&amp;quot;,
  &amp;quot;Netherlands&amp;quot;
]

countries.each do |country|
  unless User.where(country: country).exists?
    puts country
  end
end
# SELECT 1 AS one FROM `users` WHERE users`.`country` = &amp;#39;Germany&amp;#39; LIMIT 1
# SELECT 1 AS one FROM `users` WHERE users`.`country` = &amp;#39;UK&amp;#39; LIMIT 1
# SELECT 1 AS one FROM `users` WHERE users`.`country` = &amp;#39;Norway&amp;#39; LIMIT 1
# SELECT 1 AS one FROM `users` WHERE users`.`country` = &amp;#39;Netherlands&amp;#39; LIMIT 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above query required N queries to get the result. Instead of this, we can write a single query to find the users in the given countries and do the other calculation in memory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;existing_countries = User.distinct.pluck(:countries)
puts countries - existing_countries

# SELECT DISTINCT `users`.`countries` FROM `users`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can cache to reuse the request-response cycle and reduce database load in some cases. Rails provides three types of &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html&quot;&gt;caching&lt;/a&gt; techniques: page, action, and fragment caching (&lt;a href=&quot;https://blog.appsignal.com/2018/03/20/fragment-caching-in-rails.html&quot;&gt;fragment caching&lt;/a&gt; is offered by default).&lt;/p&gt;
&lt;h2&gt;Wrap Up: Optimize Your Ruby on Rails Performance with ActiveRecord and AppSignal&lt;/h2&gt;
&lt;p&gt;Okay, time to recap! In this post, we covered three ways to identify and test database performance issues in Rails by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Running Explain on ActiveRecord queries&lt;/li&gt;
&lt;li&gt;Measuring key database metrics&lt;/li&gt;
&lt;li&gt;Measuring Rails app performance using AppSignal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And seven ways to optimize your database performance, including the use of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Eager loading for N+1 queries&lt;/li&gt;
&lt;li&gt;A database index&lt;/li&gt;
&lt;li&gt;limits&lt;/li&gt;
&lt;li&gt;&lt;code&gt;find_each&lt;/code&gt; to load a large number of items&lt;/li&gt;
&lt;li&gt;Pluck to select a required field&lt;/li&gt;
&lt;li&gt;Bulk operations&lt;/li&gt;
&lt;li&gt;In-memory calculation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rails makes it super easy and fast to develop an application. ActiveRecord helps with the productivity, reusability, and maintainability of database code. Understanding how ActiveRecord queries translate to SQL queries and execute is important.&lt;/p&gt;
&lt;p&gt;However, the most crucial thing you need to optimize your Rails database performance is visibility over performance data. Performance issues are common, but you can resolve them once you have this visibility. You need a proper monitoring tool that provides database metrics. We like AppSignal ;)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Next Level Ruby on Rails Application Monitoring with AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2022/01/05/next-level-ruby-on-rails-application-monitoring-with-appsignal.html"/>
    <id>https://blog.appsignal.com/2022/01/05/next-level-ruby-on-rails-application-monitoring-with-appsignal.html</id>
    <published>2022-01-05T00:00:00+00:00</published>
    <updated>2022-01-05T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s run through adding custom instrumentation and monitoring to a Ruby on Rails application.</summary>
    <content type="html">&lt;p&gt;In the &lt;a href=&quot;https://blog.appsignal.com/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html&quot;&gt;first of this two-part series&lt;/a&gt;, we covered how to
set up AppSignal in a Ruby on Rails application for many great
insights out of the box. AppSignal can automatically track errors,
monitor performance, and report metrics about some dependencies.&lt;/p&gt;
&lt;p&gt;But, in many cases, each of our applications behaves in different ways, so we&amp;#39;ll want more than just generic monitoring.&lt;/p&gt;
&lt;p&gt;In this post, we will run through adding custom instrumentation and
monitoring to a Ruby on Rails application. This will give you deeper insights into how
your application is behaving.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites if you want to follow along with the code:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An account on &lt;a href=&quot;https://www.appsignal.com/&quot;&gt;www.appsignal.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop&quot;&gt;Docker&lt;/a&gt; installed and running (to use &lt;code&gt;docker-compose&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To follow along with this post, you will need to &lt;a href=&quot;https://blog.appsignal.com/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html&quot;&gt;set up AppSignal in
the sample application with your own AppSignal account&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Custom Instrumentation and Monitoring&lt;/h2&gt;
&lt;p&gt;When you need more than what AppSignal instruments out of the box, the AppSignal
gem allows you to add custom instrumentation to your Rails application.&lt;/p&gt;
&lt;h3&gt;Instrumenting Parts of the Code&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s say you want to add a new feature to an application. When a user visits
&lt;code&gt;/posts&lt;/code&gt; to view all posts, they should be able to filter for posts where
the title begins with a specific letter (or something a lot more complex 🪄).&lt;/p&gt;
&lt;p&gt;This new search functionality has already been implemented in the &lt;code&gt;Post&lt;/code&gt; model
with the method &lt;code&gt;Post.where_title_starts_with&lt;/code&gt;. Let&amp;#39;s update
the &lt;code&gt;PostsController#index&lt;/code&gt; to use the new method if a specific query parameter
is present:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
  def index
    starts_with = params[:starts_with]
    @posts = if starts_with.present?
               Post.where_title_starts_with(starts_with)
             else
               Post.all
             end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is such a core part of your application that you&amp;#39;ll want to know how it
performs and when that performance changes. AppSignal provides a few ways to do
this.&lt;/p&gt;
&lt;p&gt;First, we will instrument the contents of the &lt;code&gt;Post.where_title_starts_with&lt;/code&gt;
method. If you want to receive insights about any code blocks, you can use &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/instrumentation.html&quot;&gt;instrumentation blocks&lt;/a&gt; to
wrap the code blocks. Update the method
like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/post.rb
def self.where_title_starts_with(letter)
  Appsignal.instrument(&amp;#39;Post.where_title_starts_with&amp;#39;, &amp;quot;Fetch posts that start with letter&amp;quot;) do
    Analytics.track_post_title_search(letter.downcase)
    select(&amp;#39;*, pg_sleep(0.01)&amp;#39;).where(&amp;quot;title ILIKE :letter&amp;quot;, letter: &amp;quot;#{letter.downcase}%&amp;quot;).load
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Secondly, we also want to instrument the &lt;code&gt;Analytics.track_post_title_search&lt;/code&gt;
method being called because
&lt;code&gt;app/services/analytics.rb&lt;/code&gt; is doing some heavy processing. In this case, we
will use &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html&quot;&gt;method instrumentation&lt;/a&gt;
to instrument the entire method more accurately:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/services/analytics.rb
require &amp;#39;appsignal/integrations/object&amp;#39;

class Analytics
  def self.track_post_title_search(letter, sleep = sleep(1))
    # Some heavy processing
    sleep 1
  end
  appsignal_instrument_class_method :track_post_title_search
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Insights&lt;/h4&gt;
&lt;p&gt;A few minutes after saving the above to the application, take a
look at whatever new information is available on your AppSignal dashboard (if you
don&amp;#39;t see the information, you may need to restart the
docker containers again). You can verify that the new feature works by visiting
the posts index page with a search param:
&lt;a href=&quot;http://localhost:3000/posts?starts_with=f&quot;&gt;http://localhost:3000/posts?starts_with=f&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Depending on the number of posts created in the database, the
&lt;code&gt;/posts&lt;/code&gt; endpoint will have become a lot slower.&lt;/p&gt;
&lt;p&gt;If you open up performance issues on AppSignal (&amp;#39;Performance&amp;#39; -&amp;gt; &amp;#39;Issue
list&amp;#39;) and view the &lt;code&gt;PostsController#index&lt;/code&gt; action, lower down on the page, you
should be able to see an &amp;#39;Event Timeline&amp;#39;. This gives you a breakdown of how
much time is spent running specific code:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-01/event-timeline.png&quot; alt=&quot;Performance issue event timeline&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This timeline exists for all performance events, but
here, we can also see the custom instrumentation
events. It shows us that calling &lt;code&gt;Post.where_title_starts_with&lt;/code&gt; took
8.84 seconds to run, with 2.01 seconds used up by the
&lt;code&gt;Analytics.track_post_title_search&lt;/code&gt; method, and the remaining time used up by an
active record query. You can also click into
individual events for further investigation and see more information about their performance — e.g. the &lt;code&gt;sql.active_record&lt;/code&gt; event.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;AppSignal&amp;#39;s instrumentation helpers give you a more detailed
breakdown of the application code, so it&amp;#39;s easier to gain insights into
particular pieces of code that you think could impact an application&amp;#39;s
performance. You can learn more about this in &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/instrumentation.html&quot;&gt;AppSignal&amp;#39;s instrumentation guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Exception Handling&lt;/h3&gt;
&lt;p&gt;Besides monitoring the performance of your code, you also need to know when the
application doesn&amp;#39;t behave as expected and where things go wrong. We&amp;#39;ve
already seen how AppSignal reports exceptions that have not been handled by our
code. But there&amp;#39;s always a bit more than what comes out of the box.&lt;/p&gt;
&lt;p&gt;You can start by removing existing code that causes an intermittent error. We
see where this error occurs in the backtrace when viewing the error on
the dashboard. Inside of &lt;code&gt;app/controllers/pages_controller.rb&lt;/code&gt; remove the &lt;code&gt;if&lt;/code&gt;
statement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class PagesController &amp;lt; ApplicationController
  def home
    CreateRandomPostsJob.perform_later
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, in the overview dashboard, the application&amp;#39;s error rate will drop significantly.&lt;/p&gt;
&lt;p&gt;Currently, when a user tries to view a post that doesn&amp;#39;t exist, the application
crashes — e.g.,
&lt;a href=&quot;http://localhost:3000/posts/doesnotexist&quot;&gt;http://localhost:3000/posts/doesnotexist&lt;/a&gt;.
Instead, you might want to show them a message. Add a
rescue to where this could happen inside the &lt;code&gt;PostsController&lt;/code&gt;. Update the
&lt;code&gt;#set_post&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
class PostsController &amp;lt; ApplicationController
    .
    .
    .
    private
    def set_post
      @post = Post.find(params[:id])
    rescue ActiveRecord::RecordNotFound =&amp;gt; e
      render json: { error: &amp;quot;Oops. That post isn&amp;#39;t here&amp;quot; } , status: :not_found
    end
    .
    .
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because we are manually handling the exception, it
won&amp;#39;t automatically be reported to AppSignal. You can still track errors
manually by using &lt;code&gt;Appsignal.set_error&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The easiest way to track an error is to simply add it as the function&amp;#39;s only argument like &lt;code&gt;Appsignal.set_error(e)&lt;/code&gt;. We also want to take advantage
of the ability to add more context to the request. AppSignal allows you to tag
events with your own arbitrary information using &lt;code&gt;Appsignal.tag_request&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def set_post
  Appsignal.tag_request(user_id: &amp;#39;user-from-params&amp;#39;, post_id: params[:id])
  @post = Post.find(params[:id])
rescue ActiveRecord::RecordNotFound =&amp;gt; e
  Appsignal.set_error(e)
  render json: { error: &amp;quot;Oops. That post isn&amp;#39;t here&amp;quot; }, status: :not_found
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now visit
&lt;a href=&quot;http://localhost:3000/posts/doesnotexist&quot;&gt;http://localhost:3000/posts/doesnotexist&lt;/a&gt;
to verify that you get back the JSON response as expected, instead of having the
application crash.&lt;/p&gt;
&lt;h4&gt;Insights&lt;/h4&gt;
&lt;p&gt;After you try to view a post that does not exist, the added updates ensure that the errors are reported to AppSignal.
On the AppSignal dashboard, in &lt;em&gt;&amp;#39;Errors -&amp;gt; Issue
list&amp;#39;&lt;/em&gt;, find and view the new reported error
(&lt;code&gt;ActiveRecord::RecordNotFound&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The error detail page gives us useful context about the error, which by default,
includes information about the request such as the HTTP method, parameters, and
session data. You can see that the custom tags are also included, which
gives you the ability to filter for all of the errors with a matching tag.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2022-01/tags.png&quot; alt=&quot;Error tags&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Because we tagged the request, it adds this information to errors and other instrumented events. If you view an individual post a few
times, e.g., &lt;a href=&quot;http://localhost:3000/posts/posts/1&quot;&gt;http://localhost:3000/posts/1&lt;/a&gt;,
you will notice that the tags are also included when you look at the performance
measurement (&amp;#39;Performance&amp;#39; -&amp;gt; &amp;#39;Issue list&amp;#39; -&amp;gt; &amp;#39;PostsController#show&amp;#39;). You can
&lt;a href=&quot;https://docs.appsignal.com/guides/custom-data/tagging-request.html&quot;&gt;read more about tagging transactions in the guides&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This ability to add custom metadata to the transaction opens up many
opportunities to help diagnose issues in production. A great example of this
is &lt;a href=&quot;https://blog.appsignal.com/2021/06/23/adding-kubernetes-metadata-to-your-appsignal-errors.html&quot;&gt;adding Kubernetes metadata to your errors&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Metrics&lt;/h3&gt;
&lt;p&gt;Now that there is some custom instrumentation and error monitoring in place, you might
realize that sometimes, there are large spikes in posts searches. Whenever a
user searches, &lt;code&gt;Analytics#track_post_title_search&lt;/code&gt; is called, which does some calculations and makes an API call to a third-party service.
This third party has rate limits on the API. We want to track how often it is
called to keep an eye on how close the application is to its
limits.&lt;/p&gt;
&lt;p&gt;AppSignal allows you to track custom metrics throughout the application as you
wish.&lt;/p&gt;
&lt;p&gt;First, we will track how often we&amp;#39;re calling our analytics service and with
what data, using a counter and tags:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;#app/services/analytics.rb
require &amp;#39;appsignal/integrations/object&amp;#39;

class Analytics
  def self.track_post_title_search(letter, sleep = sleep(1))
    Appsignal.increment_counter(&amp;quot;track_post_search&amp;quot;, 1, { letter: letter })
    # Some heavy processing
    sleep 1
  end
  appsignal_instrument_class_method :track_post_title_search
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Secondly, we will also track the number of posts being returned in the
&lt;code&gt;PostsController#index&lt;/code&gt;, because this is a core part of the application&amp;#39;s
behavior, and we know it keeps growing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;#app/controllers/posts_controller.rb
class PostsController &amp;lt; ApplicationController
    .
    .
  def index
    .
        .
    Appsignal.set_gauge(&amp;quot;posts_index&amp;quot;, @posts.size, starts_with: params[:starts_with])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fake traffic script still running on the application will generate some
data, but to add more variety, let&amp;#39;s also search for posts starting with
&lt;a href=&quot;http://localhost:3000/posts?starts_with=f&quot;&gt;f&lt;/a&gt;,
&lt;a href=&quot;http://localhost:3000/posts?starts_with=l&quot;&gt;l&lt;/a&gt;, and
&lt;a href=&quot;http://localhost:3000/posts?starts_with=v&quot;&gt;v&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Insights&lt;/h4&gt;
&lt;p&gt;To view the custom metrics, you will need to create a dashboard with custom
graphs on AppSignal. This can be done through the UI, but we
will just import one for this example. Under the &amp;#39;Dashboard&amp;#39; section, click on &amp;#39;Add dashboard&amp;#39;
and import a dashboard with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;title&amp;quot;: &amp;quot;Post Search&amp;quot;,
  &amp;quot;description&amp;quot;: &amp;quot;Sample dashboard about posts search activity&amp;quot;,
  &amp;quot;visuals&amp;quot;: [
    {
      &amp;quot;title&amp;quot;: &amp;quot;Analytics&amp;quot;,
      &amp;quot;line_label&amp;quot;: &amp;quot;%name% %letter%&amp;quot;,
      &amp;quot;display&amp;quot;: &amp;quot;LINE&amp;quot;,
      &amp;quot;format&amp;quot;: &amp;quot;number&amp;quot;,
      &amp;quot;draw_null_as_zero&amp;quot;: true,
      &amp;quot;metrics&amp;quot;: [
        {
          &amp;quot;name&amp;quot;: &amp;quot;track_post_search&amp;quot;,
          &amp;quot;fields&amp;quot;: [
            {
              &amp;quot;field&amp;quot;: &amp;quot;COUNTER&amp;quot;
            }
          ],
          &amp;quot;tags&amp;quot;: [
            {
              &amp;quot;key&amp;quot;: &amp;quot;letter&amp;quot;,
              &amp;quot;value&amp;quot;: &amp;quot;*&amp;quot;
            }
          ]
        }
      ],
      &amp;quot;type&amp;quot;: &amp;quot;timeseries&amp;quot;
    },
    {
      &amp;quot;title&amp;quot;: &amp;quot;Search&amp;quot;,
      &amp;quot;line_label&amp;quot;: &amp;quot;%name% %starts_with%&amp;quot;,
      &amp;quot;display&amp;quot;: &amp;quot;LINE&amp;quot;,
      &amp;quot;format&amp;quot;: &amp;quot;number&amp;quot;,
      &amp;quot;draw_null_as_zero&amp;quot;: true,
      &amp;quot;metrics&amp;quot;: [
        {
          &amp;quot;name&amp;quot;: &amp;quot;posts_index&amp;quot;,
          &amp;quot;fields&amp;quot;: [
            {
              &amp;quot;field&amp;quot;: &amp;quot;GAUGE&amp;quot;
            }
          ],
          &amp;quot;tags&amp;quot;: [
            {
              &amp;quot;key&amp;quot;: &amp;quot;starts_with&amp;quot;,
              &amp;quot;value&amp;quot;: &amp;quot;*&amp;quot;
            }
          ]
        }
      ],
      &amp;quot;type&amp;quot;: &amp;quot;timeseries&amp;quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;Video fileName=&quot;/images/blog/2022-01/custom-dashboard&quot; /&gt;

&lt;p&gt;You should see data on your graphs within a few minutes. Hovering over
the lines shows you a legend of the metrics collected within
the timeframe you&amp;#39;re viewing.&lt;/p&gt;
&lt;p&gt;Notice that this shows different lines for each tag value. Currently, our fake
traffic is only searching for the letter &lt;code&gt;e&lt;/code&gt;, but because we manually searched
for other letters, you will see a new line on the graph for each one to indicate
another data point.&lt;/p&gt;
&lt;p&gt;Thought that was enough?
AppSignal has more custom instrumentation solutions to offer that we won&amp;#39;t
be covering here. One that&amp;#39;s worth a quick mention is
&lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/breadcrumbs.html&quot;&gt;breadcrumbs&lt;/a&gt;.
Breadcrumbs allow you to track a list of actions in your application, which will
then be reported in an error. You&amp;#39;ll have even more specific and ordered
information about what led up to an error.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/&quot;&gt;Read all about custom instrumentation in the guides&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrap-up: Custom Instrumentation and Monitoring for Ruby Apps with AppSignal&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://appsignal-nextjs-blog-p18ainqzq-appsignal.vercel.app/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html&quot;&gt;Part 1 of this series&lt;/a&gt; covered the basic setup and use of AppSignal for your Ruby applications.&lt;/p&gt;
&lt;p&gt;In this part, we&amp;#39;ve taken an application that already has great out-of-the-box monitoring and made
it even better using the AppSignal gem.&lt;/p&gt;
&lt;p&gt;AppSignal&amp;#39;s custom instrumentation, error tracking, and performance
monitoring features give you the insights you need into how
your applications are behaving. It gives
your application a lot out of the box while letting you take control when needed.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s time to let your code run free in the wild, as
long as you keep an eye on how it&amp;#39;s doing. Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby on Rails Application Monitoring with AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html"/>
    <id>https://blog.appsignal.com/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html</id>
    <published>2021-12-01T00:00:00+00:00</published>
    <updated>2021-12-01T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the first of this two-part series, we&#039;ll set up monitoring for a Ruby on Rails application with AppSignal.</summary>
    <content type="html">&lt;p&gt;When running and maintaining an application in a production environment, we want
to feel confident about the behavior of the application and know
when it isn&amp;#39;t working as expected. At the least, we want to track
errors, monitor performance, and collect specific metrics throughout the
application.&lt;/p&gt;
&lt;p&gt;Because we&amp;#39;re developers and love maintainable solutions (right?), we also
don&amp;#39;t want to end up in a jumble of tools, integrations, and dependencies that
make it harder for us to keep track of everything.&lt;/p&gt;
&lt;p&gt;In this post, we will add AppSignal to a Ruby on Rails
application to help give clear insights into application
behavior.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites if you want to follow along with the code:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An account on &lt;a href=&quot;https://www.appsignal.com/&quot;&gt;www.appsignal.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop&quot;&gt;Docker&lt;/a&gt; installed and running
(to use &lt;code&gt;docker-compose&lt;/code&gt;).
&lt;em&gt;*only required if using the sample application in
this post&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Setting up AppSignal&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;You can skip this section if you&amp;#39;re adding AppSignal to your own Rails application. In that case, you can also ignore the instructions
related to Docker or how to restart your Rails server throughout the post — you instead
restart/redeploy your application as you usually would.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We will use a sample application to get us started and run it on
our machine using Docker.&lt;/p&gt;
&lt;p&gt;Run the following commands to clone the repository, install dependencies, and run the application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ git clone --branch appsignal-setup/start-docker --single-branch https://github.com/choncou/sample_rails_app appsignal-setup
$ cd appsignal-setup
$ yarn start:compose
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you run this for the first time, it may take a while to build the docker images and
download all the dependencies. Once it is complete, it will start the Rails
server, PostgreSQL database, and Redis. You should see requests in the
logs and view the running application on
&lt;a href=&quot;http://localhost:3000/&quot;&gt;http://localhost:3000/&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;About Our Sample Application&lt;/h3&gt;
&lt;p&gt;The application we&amp;#39;re working with is very minimal, so there are only a few things to note.&lt;/p&gt;
&lt;p&gt;We have a &lt;code&gt;Post&lt;/code&gt; model, &lt;code&gt;PostsController&lt;/code&gt;, and all CRUD actions exposed via the &lt;code&gt;/posts&lt;/code&gt; route.&lt;/p&gt;
&lt;p&gt;We also have a home page rendered by the &lt;code&gt;PagesController&lt;/code&gt;, which enqueues a
background job &lt;code&gt;CreateRandomPostsJob&lt;/code&gt; to generate random posts asynchronously.&lt;/p&gt;
&lt;p&gt;Lastly, we&amp;#39;re using &lt;a href=&quot;https://github.com/mperham/sidekiq&quot;&gt;Sidekiq&lt;/a&gt; for background job processing.&lt;/p&gt;
&lt;p&gt;Our small blogging platform also already has some active users. There is a
script (&lt;code&gt;./bin/traffic&lt;/code&gt;) running in the background together with the server
that regularly makes a few requests to imitate traffic on the applications.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s it. We&amp;#39;ve just released a product, and everything works perfectly...&lt;/p&gt;
&lt;p&gt;Or,
at least that&amp;#39;s what we think until we start seeing user reports about the
application&amp;#39;s performance and users running into errors.&lt;/p&gt;
&lt;p&gt;So how do we figure out what&amp;#39;s going on? When we&amp;#39;re working in a development
environment, it&amp;#39;s easier to look into errors. But when our application is
running in production, this becomes harder. There must be a better way to find errors
than to search through those server logs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AppSignal to the rescue!&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Getting Started with AppSignal&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s begin our monitoring journey by adding AppSignal to our application.
You&amp;#39;ll need to log into your account at
&lt;a href=&quot;https://www.appsignal.com/&quot;&gt;www.appsignal.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re a new AppSignal user, you will see the page below to add an
application (whereas existing users need to click on &amp;#39;Add app&amp;#39;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/appsignal-install.png&quot; alt=&quot;Appsignal Install: step 1&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Select &amp;quot;Install for Ruby&amp;quot;, which will then give us a few simple steps to follow:&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add the gem to your &lt;code&gt;Gemfile&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile
gem &amp;#39;appsignal&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install the gem by running &lt;code&gt;bundle install&lt;/code&gt; inside of the docker container,
which you can access with &lt;code&gt;yarn compose:sh&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ yarn compose:sh
# We are now in a bash console within a docker container
$ bundle install
# Stay in this console for the next command
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install AppSignal&lt;/p&gt;
&lt;p&gt;During the installation, you&amp;#39;ll need to respond to two prompts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Do you want to change how this is displayed in AppSignal? (y/n): n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;How do you want to configure AppSignal?&lt;/code&gt; : Input &lt;code&gt;1&lt;/code&gt; because we will use a config file instead of just environment variables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use the API key shown on the AppSignal setup page:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ bundle exec appsignal install &amp;lt;your-api-key&amp;gt;
...
...
#####################################
## AppSignal installation complete ##
#####################################

  Sending example data to AppSignal...
  Example data sent!
  It may take about a minute for the data to appear on https://appsignal.com/accounts

  Please return to your browser and follow the instructions.
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After you run the install script, you&amp;#39;ll notice when the
AppSignal setup is complete in your browser. You can then &amp;#39;Go to app&amp;#39; to view your AppSignal
dashboard for your development environment:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/install-complete.png&quot; alt=&quot;AppSignal Install: complete&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The installation command creates a &lt;code&gt;config/appsignal.yml&lt;/code&gt; which allows you to
configure AppSignal settings for different environments. You can learn more in the
&lt;a href=&quot;https://docs.appsignal.com/ruby/configuration/&quot;&gt;AppSignal Ruby configuration docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;config/appsignal.yml&lt;/code&gt; is your push API key. You would
usually remove it from this file and use an environment variable instead. For
this post, that won&amp;#39;t be necessary.&lt;/p&gt;
&lt;p&gt;We need to restart the application to ensure that our changes take effect
because we have installed a new gem. The simplest way is to stop the
docker containers with &lt;code&gt;ctrl-c&lt;/code&gt; inside of the terminal window running
the server and start docker-compose again with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ yarn start:compose
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can take a while because it rebuilds the docker image when the dependencies change.&lt;/p&gt;
&lt;h2&gt;AppSignal Main Dashboard&lt;/h2&gt;
&lt;p&gt;Now that we have the application running with AppSignal installed, we can view
the dashboard. To view all the applications and environments you have running on
AppSignal, you can go to
&lt;a href=&quot;https://appsignal.com/accounts&quot;&gt;appsignal.com/accounts&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/application-list.png&quot; alt=&quot;AppSignal application list&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Click on the application name, and you can explore what AppSignal monitors
out-of-the-box.&lt;/p&gt;
&lt;p&gt;Within a few minutes, some data will already be collected because of your
traffic generation script.&lt;/p&gt;
&lt;p&gt;From the main dashboard, you can get a good high-level overview of a few
essential application metrics. We&amp;#39;re now going to dig into some of the more specific
areas within AppSignal.&lt;/p&gt;
&lt;h2&gt;Errors Dashboard in AppSignal&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/dashboard-errors.png&quot; alt=&quot;AppSignal dashboard errors&quot;/&gt;&lt;/p&gt;
&lt;p&gt;On the errors page, you find a list of all errors reported
by your application. When we ran the install script, a test error was sent to
AppSignal. You can see that we also have an error that seems to be occurring
pretty regularly. Clicking on the error will open up a page with more details,
which can be useful to debug the issue.&lt;/p&gt;
&lt;p&gt;In this case, we can see from the backtrace section that on the homepage of the
application (&lt;code&gt;/&lt;/code&gt;) there is a validation error occurring on
&lt;code&gt;app/controllers/pages_controller.rb:7 home&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Other AppSignal Dashboards&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/magic-dashboards.png&quot; alt=&quot;AppSignal magic dashboards&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Under &amp;#39;Dashboard&amp;#39; in the sidebar, you can see that AppSignal has provided us
with a few different dashboards already.&lt;/p&gt;
&lt;p&gt;The &amp;#39;Overview&amp;#39; dashboard is available for every application and provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your application error rate&lt;/li&gt;
&lt;li&gt;Throughput&lt;/li&gt;
&lt;li&gt;Response times&lt;/li&gt;
&lt;li&gt;Other recent activity that you can dig into for more details&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You also have two magic dashboards — one for Active Job and another for Sidekiq. AppSignal has built-in integrations for many popular
frameworks and gems that work automatically with your Rails application.&lt;/p&gt;
&lt;p&gt;When viewing these dashboards, you can
see information relevant to the activity of that integration. In the Active Job
dashboard, you&amp;#39;ll see graphs for the number of jobs in each queue, the recorded
duration of each job, and more. Give it a look!&lt;/p&gt;
&lt;h2&gt;Performance of Your App&lt;/h2&gt;
&lt;p&gt;Inside of &lt;em&gt;Performance → Issue list&lt;/em&gt;, you can view a list of actions that
AppSignal measures. These measurements can provide some valuable insights
into what parts of your application consume a lot of performance.&lt;/p&gt;
&lt;p&gt;For example, by clicking into the issue for the action &lt;code&gt;PostsController#index&lt;/code&gt;, we can dive
deeper into the controller&amp;#39;s performance. You can quickly see a breakdown of
where the most time is spent, or the most object allocations happen.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/performance-dashboard.png&quot; alt=&quot;AppSignal performance dashboard&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Quick Wins and Hidden Gems from AppSignal: Using Puma&lt;/h2&gt;
&lt;p&gt;There are a few hidden gems (not the Ruby kind) that you can get from AppSignal
without much effort. Let&amp;#39;s see how easily we can get these up and running with an example.&lt;/p&gt;
&lt;p&gt;Inside of &lt;code&gt;config/puma.rb&lt;/code&gt;, add the following line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/puma.rb
plugin :appsignal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we restart the application, add the following environment variable after
line 12 in the &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# docker-compose.yml
APP_REVISION: &amp;quot;latest_version_tag&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because we&amp;#39;ve updated the Puma server config and our docker-compose config, we
will need to restart the docker containers with &lt;code&gt;ctrl-c&lt;/code&gt; and start-up
docker-compose again with &lt;code&gt;yarn start:compose&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Tracking Your New Insights&lt;/h3&gt;
&lt;p&gt;After the server restarts, allow it to run for about a minute, then head
back to the AppSignal dashboard and refresh the page. There are two main areas
with new information now:&lt;/p&gt;
&lt;p&gt;The first is the new automatically generated Puma metrics magic dashboard.
Because of AppSignal&amp;#39;s built-in integration with Puma, we now have information about
the application server&amp;#39;s activity. Specifying the &lt;code&gt;plugin&lt;/code&gt; in the config file has activated a &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/minutely-probes.html&quot;&gt;minutely
probe&lt;/a&gt;
that reports metrics about the Puma server.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/puma-metrics.png&quot; alt=&quot;Puma metrics&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Lastly, across the entire AppSignal dashboard, there are now &lt;a href=&quot;https://docs.appsignal.com/application/markers/deploy-markers.html#revision-config-option&quot;&gt;deployment
markers&lt;/a&gt;.
You can set the &lt;code&gt;APP_REVISION&lt;/code&gt; environment variable to any value that helps you
determine the current version of your application, such as a git commit SHA.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/deploy-marker.png&quot; alt=&quot;Deploy Marker&quot;/&gt;&lt;/p&gt;
&lt;p&gt;You can also see a history of your deployments by viewing the &amp;#39;Deploys&amp;#39; section
in the AppSignal dashboard and filter insights (such as
Errors/Performance issue lists) by deploy. View
how performance or reliability has changed between deploys, making it a bit
easier to dig into which changes could have introduced a new bug.&lt;/p&gt;
&lt;h2&gt;Next Up: Custom Instrumentation and Monitoring for Ruby Apps&lt;/h2&gt;
&lt;p&gt;This post has shown how you can set up and use AppSignal in your applications to
help increase your code&amp;#39;s visibility in the wild.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve demonstrated how AppSignal automatically integrates into Rails and some
of the application&amp;#39;s dependencies, such as Sidekiq and Puma. AppSignal works with
many more Ruby frameworks and gems out-of-the-box. &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/&quot;&gt;You can find a complete list of AppSignal&amp;#39;s Ruby integrations here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Part 2 of this series will cover how to add custom instrumentation and monitoring to your Ruby on Rails application for deeper insights.&lt;/p&gt;
&lt;p&gt;Until next time!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Practical Garbage Collection Tuning in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/11/17/practical-garbage-collection-tuning-in-ruby.html"/>
    <id>https://blog.appsignal.com/2021/11/17/practical-garbage-collection-tuning-in-ruby.html</id>
    <published>2021-11-17T00:00:00+00:00</published>
    <updated>2021-11-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Explore how to implement and customize garbage collection in Ruby.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;We have discovered that the below post is based on an article by Nate Berkopec from 2017 called &lt;a href=&quot;https://www.speedshop.co/2017/03/09/a-guide-to-gc-stat.html&quot;&gt;&amp;#39;Understanding Ruby GC through GC.stat&amp;#39;&lt;/a&gt;. It appears that parts of this article were plagiarised, something we were unaware of until the original author mentioned it. We run all of our articles through a plagiarism tool before publishing, but it didn&amp;#39;t pick this up. We give huge apologies to Nate and our readers for this inadvertent error.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It is vital that you understand how garbage collection works in Ruby to stay in complete control of your app&amp;#39;s performance.&lt;/p&gt;
&lt;p&gt;In this post, we will dive into how to implement and customize garbage collection in Ruby.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get going!&lt;/p&gt;
&lt;h2&gt;The Ruby Garbage Collector Module&lt;/h2&gt;
&lt;p&gt;The Ruby Garbage Collector module is an interface to Ruby&amp;#39;s mark and sweep garbage collection mechanism.&lt;/p&gt;
&lt;p&gt;While it runs automatically in the background when needed, the GC module lets you call the GC manually whenever required and gain insights into how garbage collection cycles are running. The module provides some parameters which you can alter to moderate performance.&lt;/p&gt;
&lt;p&gt;Some of the most commonly used methods of this module are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;start/garbage_collect&lt;/strong&gt;: This method initiates the garbage collection cycle manually.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;enable/disable&lt;/strong&gt;: These methods enable or disable the automatic garbage collection cycles. They return a boolean value indicating if the operation was successful.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stat&lt;/strong&gt;: This method provides a list of keys and values that describe the performance of the GC module. We will take a look at these metrics in detail in the next section.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Understanding Ruby Garbage Collector Parameters&lt;/h2&gt;
&lt;p&gt;To understand how Ruby’s GC works internally, let’s look at the GC module&amp;#39;s metrics. Run the following command on a freshly booted irb:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;puts GC.stat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will notice that a bunch of numbers pop up on your screen, looking something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{
    :count=&amp;gt;12,
    :heap_allocated_pages=&amp;gt;49,
    :heap_sorted_length=&amp;gt;49,
    :heap_allocatable_pages=&amp;gt;0,
    :heap_available_slots=&amp;gt;19975,
    :heap_live_slots=&amp;gt;19099,
    :heap_free_slots=&amp;gt;876,
    :heap_final_slots=&amp;gt;0,
    :heap_marked_slots=&amp;gt;16659,
    :heap_eden_pages=&amp;gt;49,
    :heap_tomb_pages=&amp;gt;0,
    :total_allocated_pages=&amp;gt;49,
    :total_freed_pages=&amp;gt;0,
    :total_allocated_objects=&amp;gt;66358,
    :total_freed_objects=&amp;gt;47259,
    :malloc_increase_bytes=&amp;gt;16216,
    :malloc_increase_bytes_limit=&amp;gt;16777216,
    :minor_gc_count=&amp;gt;10,
    :major_gc_count=&amp;gt;2,
    :remembered_wb_unprotected_objects=&amp;gt;191,
    :remembered_wb_unprotected_objects_limit=&amp;gt;312,
    :old_objects=&amp;gt;16024,
    :old_objects_limit=&amp;gt;23556,
    :oldmalloc_increase_bytes=&amp;gt;158824,
    :oldmalloc_increase_bytes_limit=&amp;gt;16777216
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This holds all the information about how garbage collection has been happening in the runtime. Let&amp;#39;s examine each of these numbers in detail.&lt;/p&gt;
&lt;h3&gt;Counts in Ruby Garbage Collector&lt;/h3&gt;
&lt;p&gt;We&amp;#39;ll begin by describing these keys:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{
    :count=&amp;gt;12,
    #…
    :minor_gc_count=&amp;gt;10,
    :major_gc_count=&amp;gt;2,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are GC counts, and they convey pretty straightforward information. &lt;code&gt;minor_gc_count&lt;/code&gt; and &lt;code&gt;major_gc_count&lt;/code&gt; are the counts of each type of garbage collection run.&lt;/p&gt;
&lt;p&gt;There are two types of garbage collections in Ruby.&lt;/p&gt;
&lt;p&gt;Minor GC refers to a garbage collection attempt that tries to garbage collect only those objects that are new, i.e., they have survived three or fewer garbage collection cycles.&lt;/p&gt;
&lt;p&gt;On the other hand, major GC is a garbage collection attempt that tries to garbage collect all objects, even those that have survived more than three garbage collection cycles. &lt;code&gt;count&lt;/code&gt; is the sum of &lt;code&gt;minor_gc_count&lt;/code&gt; and &lt;code&gt;major_gc_count&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Tracking the GC count can be helpful for a few reasons. You can figure out if a particular job or process always triggers GCs and the number of times it triggers them. It might not be 100% accurate in cases like multithreaded applications, but it is a good starting point to figure out where your memory is bleeding.&lt;/p&gt;
&lt;h3&gt;Heap Numbers: Slots and Pages&lt;/h3&gt;
&lt;p&gt;Next, let’s talk about these keys, also known as &lt;em&gt;heap numbers&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{
    # page numbers
    :heap_allocated_pages=&amp;gt;49,
    :heap_sorted_length=&amp;gt;49,
    :heap_allocatable_pages=&amp;gt;0,

    # slots
    :heap_available_slots=&amp;gt;19975,
    :heap_live_slots=&amp;gt;19099,
    :heap_free_slots=&amp;gt;876,
    :heap_final_slots=&amp;gt;0,
    :heap_marked_slots=&amp;gt;16659,

    # Eden and Tomb pages
    :heap_eden_pages=&amp;gt;49,
    :heap_tomb_pages=&amp;gt;0,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The heap that we are talking about here is a C data structure. It contains references to all the currently live Ruby objects. A heap &lt;em&gt;page&lt;/em&gt; is composed of memory slots, and each slot includes information on only one live Ruby object:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;heap_allocated_pages&lt;/code&gt; is the number of currently allocated heap pages. These pages can be completely empty, completely filled, or partly filled.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heap_sorted_length&lt;/code&gt; is the actual size that the heap has occupied in memory and is different from &lt;code&gt;heap_allocated_pages&lt;/code&gt;, as the length is the &lt;em&gt;length&lt;/em&gt; of the heap pages put together, not their &lt;em&gt;count&lt;/em&gt;. If you initially allocated 10 pages and then freed one from the middle of the set, your &lt;code&gt;heap_allocated_pages&lt;/code&gt; would be 9, but the &lt;code&gt;heap_sorted_length&lt;/code&gt; would still be 10.&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;heap_allocatable_pages&lt;/code&gt; is the count of heaps that Ruby currently owns that can be used when needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, coming to the slots:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;heap_available_slots&lt;/code&gt; is the total number of available slots in the heap pages.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heap_live_slots&lt;/code&gt; is the number of live objects in the memory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heap_free_slots&lt;/code&gt; are slots in the allocated heap pages that are empty.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heap_final_slots&lt;/code&gt; is the count of slots whose objects have &lt;em&gt;finalizers&lt;/em&gt; attached to them. Finalizers are Procs that run when an object is freed, similarly to destructors in OOPS.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heap_marked_slots&lt;/code&gt; is the count of old objects (i.e., the objects that have been around for more than 3 GC cycles) and write-barrier unprotected objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then we have &lt;code&gt;tomb_pages&lt;/code&gt; and &lt;code&gt;eden_pages&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tomb_pages&lt;/code&gt; is the count of pages that contain no live objects. These pages are eventually released back to the operating system by Ruby.&lt;/p&gt;
&lt;p&gt;On the other hand, &lt;code&gt;eden_pages&lt;/code&gt; is the count of those pages that contain at least one live object, so they can&amp;#39;t be released back to the operating system.&lt;/p&gt;
&lt;p&gt;Consider monitoring the metric &lt;code&gt;heap_free_slots&lt;/code&gt; if you face memory bloat issues in your application.&lt;/p&gt;
&lt;p&gt;A high number of free slots (more than 250,000) usually indicates that you have a handful of controller actions that allocate many objects at once and then free them. This can permanently bloat the size of your running Ruby process.&lt;/p&gt;
&lt;h3&gt;Cumulative Numbers&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{
    :total_allocated_pages=&amp;gt;49,
    :total_freed_pages=&amp;gt;0,
    :total_allocated_objects=&amp;gt;66358,
    :total_freed_objects=&amp;gt;47259,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These numbers are cumulative or additive in nature for the entire life of the process. They are never reset by the GC and can&amp;#39;t technically go down. All four of these numbers are self-explanatory.&lt;/p&gt;
&lt;h3&gt;Garbage Collection Thresholds&lt;/h3&gt;
&lt;p&gt;To understand these numbers, you first need to understand when GC is triggered:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{
    :malloc_increase_bytes=&amp;gt;16216,
    :malloc_increase_bytes_limit=&amp;gt;16777216,
    :remembered_wb_unprotected_objects=&amp;gt;191,
    :remembered_wb_unprotected_objects_limit=&amp;gt;312,
    :old_objects=&amp;gt;16024,
    :old_objects_limit=&amp;gt;23556,
    :oldmalloc_increase_bytes=&amp;gt;158824,
    :oldmalloc_increase_bytes_limit=&amp;gt;16777216
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Contrary to a common assumption that GC runs happen at fixed intervals, GC runs are triggered when Ruby starts running out of memory space. Minor GC happens when Ruby runs out of &lt;code&gt;free_slots&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If Ruby is still low on &lt;code&gt;free_slots&lt;/code&gt; after a minor GC run — or the threshold of oldmalloc, malloc, old object count, or &lt;em&gt;shady&lt;/em&gt;/write-barrier-unprotected count is exceeded — a major GC run is triggered. The above part of gc.stat shows the values of these thresholds.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;malloc_increase_bytes&lt;/code&gt; refers to the amount of memory allocated outside of the &lt;em&gt;heap&lt;/em&gt; we talked about so far. When the size of an object exceeds the standard size of a memory slot — say, 40 bytes — Ruby &lt;code&gt;malloc&lt;/code&gt;s some space somewhere else just for that object. When the total extra allocated space exceeds &lt;code&gt;malloc_increase_bytes_limit&lt;/code&gt;, a major GC is triggered.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;oldmalloc_increase_bytes&lt;/code&gt; is a similar threshold for old objects. &lt;code&gt;old_objects&lt;/code&gt; is a count of object slots marked as old. When the number exceeds the &lt;code&gt;old_objects_limit&lt;/code&gt;, major GC is triggered.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;remembered_wb_unprotected_objects&lt;/code&gt; is the total count of objects that are not protected by the &lt;em&gt;write-barrier&lt;/em&gt; and are a part of the &lt;em&gt;remembered set&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The write-barrier is an interface between Ruby runtime and its objects, which allows the interpreter to track references to and from the object as soon as they are created.&lt;/p&gt;
&lt;p&gt;C-extensions can make new references to objects without using the write-barrier, in which case the objects are marked &lt;em&gt;shady&lt;/em&gt; or &lt;em&gt;write-barrier unprotected&lt;/em&gt;. The remembered set is simply a list of &lt;em&gt;old&lt;/em&gt; objects with at least one reference to a &lt;em&gt;new&lt;/em&gt; object.&lt;/p&gt;
&lt;h2&gt;Customize Ruby Garbage Collection Performance&lt;/h2&gt;
&lt;p&gt;Now that you understand how Ruby GC manages your application&amp;#39;s memory, it is time to look at the options available to customize GC&amp;#39;s behavior.&lt;/p&gt;
&lt;p&gt;Here are environment variables that you can use to moderate the performance of Ruby GC and, in turn, improve the performance of your application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;RUBY_GC_HEAP_INIT_SLOTS
RUBY_GC_HEAP_FREE_SLOTS
RUBY_GC_HEAP_GROWTH_FACTOR
RUBY_GC_HEAP_GROWTH_MAX_SLOTS
RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR
and other variables
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s talk about the important parameters here, one by one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RUBY_GC_HEAP_INIT_SLOTS&lt;/code&gt;: defines the initial number of slots on the Ruby heap and is set to 10000 by default. You might want to change this parameter if you&amp;#39;re sure that your app will initially allocate most of its objects.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUBY_GC_HEAP_FREE_SLOTS&lt;/code&gt;: controls the minimum number of free slots that must be available right after a GC cycle. Its default value is 4096. This value is only used once at runtime during the first heap growth.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUBY_GC_HEAP_GROWTH_FACTOR&lt;/code&gt;: the factor by which available heaps to the Ruby interpreter grow. Its default value is 1.8. Changing this makes little sense, as Ruby is already aggressive at heap growth. It will not make much difference if you try decreasing it since heaps are allocated on-demand in modern interpreters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUBY_GC_HEAP_GROWTH_MAX_SLOTS&lt;/code&gt;: the maximum number of slots Ruby can add to the heap space at once. The default value is 0, which stands for no limit on the number. If your app needs to allocate millions of objects in its lifetime, you might want to put a cap on this parameter. However, it will have a pretty low effect on the GC time of your app.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR&lt;/code&gt; forces the interpreter to carry out a major GC cycle when the total number of old objects in memory = more than this number x number of old objects in memory after the last GC cycle. You might want to increase this number if you think many of your objects will become unused after they enter the old generation. However, this is rarely needed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUBY_GC_MALLOC_LIMIT&lt;/code&gt; is the minimum limit of malloc calls for the new generation. Its default value is 16 MB. &lt;code&gt;RUBY_GC_MALLOC_LIMIT_MAX&lt;/code&gt; is the maximum limit for the same malloc calls. Its default value is 32 MB. You might want to increase these two limits if your application uses higher than average memory. However, be careful not to raise these too much. Otherwise, they might lead to higher peak memory consumption. Always increase these limits incrementally — say, by 4 or 8 MB.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR&lt;/code&gt; is the growth factor of the malloc limit for the new generation. Its default value is 1.4. You should consider increasing this number if your app allocates memory in chunks rather than whole at once.&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;RUBY_GC_OLDMALLOC_LIMIT&lt;/code&gt; and &lt;code&gt;RUBY_GC_OLDMALLOC_LIMIT_MAX&lt;/code&gt; are the minimum and maximum malloc limits for the old generation. The default values of these parameters are 16 MB and 128 MB.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR&lt;/code&gt; is the growth factor of this limit. Its default value is 1.2. You can consider changing these with the new generation limits for maximum effect.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fine-tuning Garbage Collection in Ruby&lt;/h2&gt;
&lt;p&gt;We discussed some common and simple ways to customize the GC module to help you improve your application&amp;#39;s overall performance. However, these tweaks might not work in all cases. You need to figure out the memory usage pattern of your app before deciding what to customize.&lt;/p&gt;
&lt;p&gt;On the flip side, you can consider running an automated test that finds the best values of these parameters for you. Tools like &lt;a href=&quot;https://rubygems.org/gems/tunemygc/versions/1.0.4&quot;&gt;TuneMyGC&lt;/a&gt; are pretty straightforward when figuring out the best set of values for your environment variables.&lt;/p&gt;
&lt;p&gt;Definitely look at GC parameters if your application is behaving weirdly. A small change here can go a long way to bring down your app&amp;#39;s memory consumption and prevent memory bloats.&lt;/p&gt;
&lt;p&gt;I hope this article has given you a good idea of what to look out for when customizing your Ruby Garbage Collection module. For more of an introduction, check out the Introduction to Garbage Collection &lt;a href=&quot;https://blog.appsignal.com/2016/07/12/ruby-magic-garbage-collection-part-1.html&quot;&gt;Part I&lt;/a&gt; and &lt;a href=&quot;https://blog.appsignal.com/2016/07/28/ruby-magic-garbage-collection-part-2.html&quot;&gt;Part II&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Have fun coding!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Optimistic Locking in Rails REST APIs</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/10/20/optimistic-locking-in-rails-rest-apis.html"/>
    <id>https://blog.appsignal.com/2021/10/20/optimistic-locking-in-rails-rest-apis.html</id>
    <published>2021-10-20T00:00:00+00:00</published>
    <updated>2021-10-20T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Implement optimistic locking in Rails APIs to avoid potentially critical issues.</summary>
    <content type="html">&lt;p&gt;Imagine the following hypothetical scenario: in a rental property management system, Employee A starts editing contact info for Rental X, adding some extra phone numbers. Around the same time, Employee B notices a typo in the contact info for exactly that Rental X and performs an update. A couple of minutes later, Employee A updates Rental X&amp;#39;s contact info with the new phone numbers, and ... the update fixing the typo is now gone!&lt;/p&gt;
&lt;p&gt;That&amp;#39;s definitely not great! And this is a pretty trivial scenario. Imagine a similar conflict happening in a financial system!&lt;/p&gt;
&lt;p&gt;Could we avoid such scenarios in the future? Fortunately, the answer is yes! We need concurrency protection and locking — specifically, optimistic locking — to prevent such problems.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s explore optimistic locking in Rails REST APIs.&lt;/p&gt;
&lt;h2&gt;&amp;#39;Lost Updates&amp;#39; and Optimistic Locking vs. Pessimistic Locking&lt;/h2&gt;
&lt;p&gt;The scenario we&amp;#39;ve just gone through is a type of &amp;#39;Lost Update&amp;#39;. When two concurrent transactions update the same column of the same row, the second one will override the changes from the first one, essentially as if the first transaction never happened.&lt;/p&gt;
&lt;p&gt;Usually, this problem can be addressed by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting a proper &lt;a href=&quot;https://fauna.com/blog/introduction-to-transaction-isolation-levels&quot;&gt;Transaction Isolation level&lt;/a&gt;, which handles the problem on the database level.&lt;/li&gt;
&lt;li&gt;Pessimistic locking — preventing concurrent transactions from updating the same row. The second transaction waits for the first transaction to finish before it even reads the data. The great advantage here is that it is impossible to operate on stale data. The major disadvantage, though, is that it also blocks reading the data from a given row.&lt;/li&gt;
&lt;li&gt;Optimistic locking — stops the modification of the given row if its state at the time of modification is different from when it was read.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our problem is not about concurrent database transactions (more like business transactions) — so the first solution is not really applicable. This means we&amp;#39;re left with pessimistic locking and optimistic locking.&lt;/p&gt;
&lt;p&gt;Pessimistic locking would prevent the lost update from happening in our hypothetical scenario in the first place. However, it would also make life difficult for users if it blocked access to data for a very long time (imagine it reading and editing some fields for 30 minutes or more).&lt;/p&gt;
&lt;p&gt;Optimistic locking would be far less restrictive, as it would allow multiple users to access data. However, if several users start editing the data concurrently, only one can perform the operation. The rest would see an error stating that they operated on stale data and need to retry. Not ideal, but with proper UX, this might not necessarily be that painful.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see how we could implement optimistic locking in a hypothetical Rails REST API.&lt;/p&gt;
&lt;h2&gt;Optimistic Locking in REST APIs&lt;/h2&gt;
&lt;p&gt;Before we get to implementation in the actual Rails app, let&amp;#39;s think about what optimistic locking could look like in the context of general REST APIs.&lt;/p&gt;
&lt;p&gt;As described above, we need to track an object&amp;#39;s original state when reading it to compare against its later state during the update. If the state doesn&amp;#39;t change since the last read, the operation is allowed. If it has changed, though, it will fail.&lt;/p&gt;
&lt;p&gt;What we need to figure out in the context of REST APIs is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When reading the data of a given resource, how do we express the current state of an object and return it in a response for a consumer?&lt;/li&gt;
&lt;li&gt;How should consumers propagate the original state of a resource to an API when performing the update?&lt;/li&gt;
&lt;li&gt;What should the API return to the consumer if the state has changed and the update is not possible?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The great news is that all these questions can be answered and handled with HTTP semantics.&lt;/p&gt;
&lt;p&gt;As far as tracking the state of a resource goes, we can take advantage of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag&quot;&gt;&lt;code&gt;Entity Tags&lt;/code&gt;&lt;/a&gt; (or ETags). We can return the resource&amp;#39;s fingerprint/checksum/version number in the dedicated &lt;code&gt;ETag&lt;/code&gt; header to API consumers to send later with the PATCH request. We can use an &lt;code&gt;If-Match&lt;/code&gt; header, making it pretty straightforward for the API server to check if the resource has changed or not. It is just a case of comparing the checksums/version number/whatever else you choose as the ETag.&lt;/p&gt;
&lt;p&gt;The request will succeed if the current &lt;code&gt;ETag&lt;/code&gt; and &lt;code&gt;If-Match&lt;/code&gt; values are the same. If not, the API should respond with the rarely-used &lt;code&gt;412 Precondition Failed&lt;/code&gt; status, the most appropriate and expressive status that we can use for this purpose.&lt;/p&gt;
&lt;p&gt;There is one other possible scenario. We can only compare the ETags if the API consumer provides the &lt;code&gt;If-Match&lt;/code&gt; header. What if it doesn&amp;#39;t? You could ignore concurrency protection and forget about optimistic locking, but that might not be ideal. One other solution would be to make it a requirement to provide the &lt;code&gt;If-Match&lt;/code&gt; header and respond with &lt;code&gt;428 Precondition Required&lt;/code&gt; status if it&amp;#39;s not.&lt;/p&gt;
&lt;p&gt;Now that we have a solid overview of how optimistic locking could work in REST APIs, let&amp;#39;s implement it in Rails.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Optimistic Locking in Rails&lt;/h2&gt;
&lt;p&gt;The great news is that Rails offers optimistic locking out-of-the-box — we can use the feature provided by &lt;code&gt;ActiveRecord::Locking::Optimistic&lt;/code&gt;. When you add the &lt;code&gt;lock_version&lt;/code&gt; column (or whatever else you want, although that requires additional declarations on the model level to define the locking column), ActiveRecord will increment it after each change and check if the currently assigned version is the expected one. If it&amp;#39;s stale, the &lt;code&gt;ActiveRecord::StaleObjectError&lt;/code&gt; exception will be raised on the update/destroy attempt.&lt;/p&gt;
&lt;p&gt;The easiest way to handle optimistic locking in our API is to use the value from &lt;code&gt;lock_version&lt;/code&gt; as an ETag. Let&amp;#39;s do this as the first step in our hypothetical &lt;code&gt;RentalsController&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class RentalsController
  after_action :assign_etag, only: [:show]

  def show
    @rental = Rental.find(params[:id])
    respond_with @rental
  end

  private

  def assign_etag
    response.headers[&amp;quot;ETag&amp;quot;] = @rental.lock_version
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is, of course, a very simplified version of the controller as we are only interested in whatever is required for optimistic locking, not authentication, authorization, or other concepts. This is enough to expose the proper ETag to the consumer. Let&amp;#39;s now take care of the &lt;code&gt;If-Match&lt;/code&gt; header that the consumers can provide:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class RentalsController
  after_action :assign_etag, only: [:show, :update]

  def show
    @rental = Rental.find(params[:id])
    respond_with @rental
  end

  def update
    @rental = Rental.find(params[:id])
    @rental.update(rental_params)
    respond_with @rental
  end

  private

  def assign_etag
    response.headers[&amp;quot;ETag&amp;quot;] = @rental.lock_version
  end

  def rental_params
    params
      .require(:rental)
      .permit(:some, :permitted, :attributes).merge(lock_version: lock_version_from_if_match_header)
  end

  def lock_version_from_if_match_header
    request.headers[&amp;quot;If-Match&amp;quot;].to_i
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&amp;#39;s actually enough to have the minimal version of optimistic locking working! Although, clearly, we don&amp;#39;t want to return 500 responses if there is any conflict. We will make &lt;code&gt;If-Match&lt;/code&gt; required for any update too:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class RentalsController
  before_action :ensure_if_match_header_provided, only: [:update]
  after_action :assign_etag, only: [:show, :update]

  rescue_from ActiveRecord::StaleObjectError do
    head 412
  end

  def show
    @rental = Rental.find(params[:id])
    respond_with @rental
  end

  def update
    @rental = Rental.find(params[:id])
    @rental.update(rental_params)
    respond_with @rental
  end

  private

  def ensure_if_match_header_provided
     request.headers[&amp;quot;If-Match&amp;quot;].present? or head 428 and return
  end

  def assign_etag
    response.headers[&amp;quot;ETag&amp;quot;] = @rental.lock_version
  end

  def rental_params
    params
      .require(:rental)
      .permit(:some, :permitted, :attributes)
      .merge(lock_version: lock_version_from_if_match_header)
  end

  def lock_version_from_if_match_header
    request.headers[&amp;quot;If-Match&amp;quot;].to_i
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&amp;#39;s pretty much everything required to implement all the functionality that we discussed earlier. We could improve way more things — e.g., by providing some extra error messages besides just the response code — but that would be outside the scope of this article.&lt;/p&gt;
&lt;h2&gt;Wrap-up: The Importance of Optimistic Locking in Rails APIs&lt;/h2&gt;
&lt;p&gt;Concurrency protection is often overlooked when designing REST APIs, which can lead to severe consequences.&lt;/p&gt;
&lt;p&gt;Nevertheless, implementing optimistic locking in Rails APIs is pretty straightforward — as demonstrated in this article — and will help avoid potentially critical issues.&lt;/p&gt;
&lt;p&gt;Have fun coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Reduce Memory Bloat in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/09/21/how-to-reduce-memory-bloat-in-ruby.html"/>
    <id>https://blog.appsignal.com/2021/09/21/how-to-reduce-memory-bloat-in-ruby.html</id>
    <published>2021-09-21T00:00:00+00:00</published>
    <updated>2021-09-21T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Memory bloat can be a serious issue in Ruby. This article explores two common causes of memory bloat and how you can overcome them.</summary>
    <content type="html">&lt;p&gt;The issue of memory bloat in Ruby applications is a topic of frequent discussion. In this post, we will look at how Ruby memory management can go wrong, and what you can do to prevent your Ruby application from blowing up.&lt;/p&gt;
&lt;p&gt;First, we need to understand what bloat means in the context of an application’s memory.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive in!&lt;/p&gt;
&lt;h2&gt;What Is Memory Bloat in Ruby?&lt;/h2&gt;
&lt;p&gt;Memory bloat is when your application’s memory usage increases all of a sudden without an apparent explanation. This increase may or may not be quick, but in most cases, it becomes continuous. It is different from a memory leak, which results from multiple executions taking place over some time.&lt;/p&gt;
&lt;p&gt;Memory bloat can be one of the worst things to happen to your Ruby application in production. However, it is avoidable if you take proper measures. For example, if you notice a spike in your application’s memory usage, it is best to check for signs of memory bloat before troubleshooting other issues.&lt;/p&gt;
&lt;p&gt;Before we explore how to diagnose and fix memory bloat, let’s take a quick walkthrough of Ruby’s memory architecture.&lt;/p&gt;
&lt;h2&gt;Ruby&amp;#39;s Memory Structure&lt;/h2&gt;
&lt;p&gt;Ruby&amp;#39;s memory usage revolves around specific elements that manage the judicious use of available system resources. These elements include the Ruby language, host operating system, and the system kernel.&lt;/p&gt;
&lt;p&gt;Apart from these, the garbage collection process also plays a vital role in determining how Ruby memory is managed and reused.&lt;/p&gt;
&lt;h2&gt;Ruby Heap Pages and Memory Slots&lt;/h2&gt;
&lt;p&gt;The Ruby language organizes objects into segments called heap pages. The entire heap space (available memory) is divided into used and empty sections. These heap pages are further split into equal-sized slots, which allow one unit-sized object each.&lt;/p&gt;
&lt;p&gt;When allocating memory to a new object, Ruby first looks in the used heap space for free slots. If none are found, it allocates a new heap page from the empty section.&lt;/p&gt;
&lt;p&gt;Memory slots are small memory locations, each nearly 40 bytes in size. The data that overflows out of these slots is stored in a different area outside the heap page and each slot stores a pointer to the external information.&lt;/p&gt;
&lt;p&gt;A system’s memory allocator makes all allocations in the Ruby runtime environment, including heap pages and external data pointers.&lt;/p&gt;
&lt;h2&gt;Operating System Memory Allocation in Ruby&lt;/h2&gt;
&lt;p&gt;Memory allocation calls made by the Ruby language are handled and responded to by the host operating system’s memory allocator.&lt;/p&gt;
&lt;p&gt;Usually, the memory allocator consists of a group of C functions, namely &lt;em&gt;malloc&lt;/em&gt;, &lt;em&gt;calloc&lt;/em&gt;, &lt;em&gt;realloc&lt;/em&gt;, and &lt;em&gt;free&lt;/em&gt;. Let’s quickly take a look at each:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Malloc&lt;/strong&gt;: Malloc stands for memory allocation, and it is used to allocate free memory to objects. It takes in the size of the memory to be allocated and returns a pointer to the starting index of the allocated memory block.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Calloc&lt;/strong&gt;: Calloc stands for contiguous allocation, and it allows the Ruby language to allocate consecutive blocks of memory. It is beneficial when allocating object arrays of known length.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Realloc&lt;/strong&gt;: Realloc stands for re-allocation, and it allows the language to re-allocate memory with a new size.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free&lt;/strong&gt;: Free is used to clear out pre-allocated sets of memory locations. It takes in a pointer to the starting index of the memory block that has to be freed.&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Garbage Collection in Ruby&lt;/h2&gt;
&lt;p&gt;The garbage collection process of a language runtime dramatically affects how well it utilizes its available memory.&lt;/p&gt;
&lt;p&gt;Ruby happens to have pretty advanced garbage collection that uses all of the above-described API methods to optimize application memory consumption at all times.&lt;/p&gt;
&lt;p&gt;An interesting fact about the garbage collection process in Ruby is that it halts the complete application! This ensures that no new object allocation happens during garbage collection. Because of this, your garbage collection routine should be infrequent and as quick as possible.&lt;/p&gt;
&lt;h2&gt;Two Common Causes of Memory Bloat in Ruby&lt;/h2&gt;
&lt;p&gt;This section will discuss two of the most significant reasons why memory bloats occur in Ruby: fragmentation and slow release.&lt;/p&gt;
&lt;h2&gt;Memory Fragmentation&lt;/h2&gt;
&lt;p&gt;Memory fragmentation is when object allocations in memory are scattered all over, reducing the number of contiguous chunks of free memory. Memory can&amp;#39;t be allocated to objects without contiguous blocks, even if more than enough free memory is available on the disk. This problem can occur in any programming language or environment, and each language has its methods for solving the issue.&lt;/p&gt;
&lt;p&gt;Fragmentation can occur at two different levels: the language’s level and the memory allocator’s level. Let’s take a look at both of these in detail.&lt;/p&gt;
&lt;h2&gt;Fragmentation at the Ruby Level&lt;/h2&gt;
&lt;p&gt;Fragmentation at the language level occurs due to the design of the garbage collection process. The garbage collection process marks a Ruby heap page slot as free, allowing reuse of that slot to allocate another object in the memory. If a complete Ruby heap page consists of only free slots, then that heap page can be released to the memory allocator for reuse.&lt;/p&gt;
&lt;p&gt;But what if a tiny number of slots are not marked free on a heap? It will not be released back to the memory allocator. Now, think about the many slots in various heap pages simultaneously allocated and freed by garbage collection. It is improbable for entire heap pages to be released at once. Even though the garbage collection process frees memory, it can&amp;#39;t be reused by the memory allocator since memory blocks partially occupy it.&lt;/p&gt;
&lt;h2&gt;Fragmentation at the Memory Allocator Level&lt;/h2&gt;
&lt;p&gt;The memory allocator itself faces a similar problem: it has to release OS heaps once they are all entirely free. But it is improbable that an entire OS heap can be freed at once considering the random nature of the garbage collection process.&lt;/p&gt;
&lt;p&gt;The memory allocator also provisions OS heaps from system memory for an application’s use. It will simply move to provision new OS heaps, even if the existing heaps have enough free memory to satisfy the application’s memory requirements. This is the perfect recipe for a spike in the memory metrics of an application.&lt;/p&gt;
&lt;h2&gt;Slow Release&lt;/h2&gt;
&lt;p&gt;Another important cause of memory bloat in Ruby is a slow release of freed memory back to the system. In this situation, memory is freed much more slowly than the rate at which new memory blocks are allocated to objects. While this is not a conventional or a rookie issue to solve, it intensely affects memory bloat — even more so than fragmentation!&lt;/p&gt;
&lt;p&gt;Upon investigating the memory allocator’s source, it turns out that the allocator is designed to release OS pages just at the end of OS heaps, and even then, only very occasionally. This is probably for performance reasons, but it can backfire and be counter-productive.&lt;/p&gt;
&lt;h2&gt;How to Fix Ruby Memory Bloat&lt;/h2&gt;
&lt;p&gt;Now that we know what causes Ruby’s memory to bloat, let’s take a look at how you can fix these issues and improve your app&amp;#39;s performance through defragmentation and trimming.&lt;/p&gt;
&lt;h2&gt;Fix Ruby Memory Bloat with Defragmentation&lt;/h2&gt;
&lt;p&gt;Fragmentation happens due to the design of garbage collection, and there isn&amp;#39;t much you can do to fix it. However, there are a few steps that you can follow to reduce the chances of ending up with a fragmented memory disk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you declare a reference to an object that uses a considerable amount of memory, make sure you free it manually when its job is done.&lt;/li&gt;
&lt;li&gt;Try to declare all of your static object allocations in one big block. This will put all your permanent classes, objects, and other data on the same heap pages. Later, when you play around with dynamic allocations, you won’t have to worry about the static heap pages.&lt;/li&gt;
&lt;li&gt;If possible, try to do large dynamic allocations at the &lt;em&gt;beginning&lt;/em&gt; of your code. This will put them close to your bigger static allocation memory blocks and will keep the rest of your memory clean.&lt;/li&gt;
&lt;li&gt;If you use a small and rarely cleared cache, it is better to group it with permanent static allocation in the beginning. You can even consider removing it altogether to improve your app’s memory management.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://github.com/jemalloc/jemalloc&quot;&gt;jemalloc&lt;/a&gt; instead of the standard glibc memory allocator. This small tweak can bring down your Ruby memory consumption &lt;a href=&quot;https://www.mikeperham.com/2018/04/25/taming-rails-memory-bloat/&quot;&gt;by up to four times&lt;/a&gt;. The only caveat here is that it might not be compatible in all environments, so be sure to test your app thoroughly before rolling into production.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Trimming to Fix Ruby Memory Bloat&lt;/h2&gt;
&lt;p&gt;You need to override the garbage collection process and release memory more often to fix slow memory release. There is an API that can do this called &lt;em&gt;malloc_trim&lt;/em&gt;. All you need to do is modify Ruby to call this function during the garbage collection process.&lt;/p&gt;
&lt;p&gt;Here’s the modified Ruby 2.6 code that calls &lt;em&gt;malloc_trim&lt;/em&gt; in gc.c function &lt;code&gt;gc_start&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;gc_prof_timer_start(objspace);
{
    gc_marks(objspace, do_full_mark);
    // BEGIN MODIFICATION
    if (do_full_mark)
    {
        malloc_trim(0);
    }
    // END MODIFICATION
}
gc_prof_timer_stop(objspace);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; This is not recommended in production applications as it can make your app unstable. However, it comes in handy when slow memory release causes major hits to your performance, and you are ready to try out any solution.&lt;/p&gt;
&lt;h2&gt;Wrap-up and Next Steps&lt;/h2&gt;
&lt;p&gt;Memory bloats are tricky to identify and even more challenging to fix.&lt;/p&gt;
&lt;p&gt;This article looked at two significant reasons behind memory bloats in Ruby apps — fragmentation and slow release — and two fixes: defragmentation and trimming.&lt;/p&gt;
&lt;p&gt;You must keep a constant eye on your app’s metrics to identify an impending bloat incident and fix it before it takes your app down.&lt;/p&gt;
&lt;p&gt;I hope that I&amp;#39;ve helped you take some steps towards fixing memory bloat in your Ruby application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Responsible Monkeypatching in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/08/24/responsible-monkeypatching-in-ruby.html"/>
    <id>https://blog.appsignal.com/2021/08/24/responsible-monkeypatching-in-ruby.html</id>
    <published>2021-08-24T00:00:00+00:00</published>
    <updated>2021-08-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Monkeypatching can be a great way to improve existing code if used right. Let&#039;s find out how.</summary>
    <content type="html">&lt;p&gt;When I first started writing Ruby code professionally back in 2011, one of the things that impressed me the most about the language was its flexibility. It felt as though with Ruby, everything was possible. Compared to the rigidity of languages like C# and Java, Ruby programs almost seemed like they were &lt;em&gt;alive&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Consider how many incredible things you can do in a Ruby program. You can define and delete methods at will. You can call methods that don&amp;#39;t exist. You can conjure entire nameless classes out of thin air. It&amp;#39;s absolutely wild.&lt;/p&gt;
&lt;p&gt;But that&amp;#39;s not where the story ends. While you can apply these techniques inside your own code, Ruby also lets you apply them to anything loaded into the virtual machine. In other words, you can mess with other people&amp;#39;s code as easily as you can your own.&lt;/p&gt;
&lt;h2&gt;What Are Monkeypatches?&lt;/h2&gt;
&lt;p&gt;Enter the &lt;em&gt;monkeypatch&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In short, monkeypatches &amp;quot;monkey with&amp;quot; existing code. The existing code is often code you don&amp;#39;t have direct access to, like code from a gem or from the Ruby standard library. Patches are usually designed to alter the original code&amp;#39;s behavior to fix a bug, improve performance, etc.&lt;/p&gt;
&lt;p&gt;The most unsophisticated monkeypatches reopen ruby classes and modify behavior by adding or overriding methods.&lt;/p&gt;
&lt;p&gt;This reopening idea is core to Ruby&amp;#39;s object model. Whereas in Java, classes can only be defined once, Ruby classes (and modules for that matter) can be defined multiple times. When we define a class a second, third, fourth time, etc, we say that we&amp;#39;re &lt;em&gt;reopening&lt;/em&gt; it. Any new methods we define are added to the existing class definition and can be called on instances of that class.&lt;/p&gt;
&lt;p&gt;This short example illustrates the class reopening concept:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Sounds
  def honk
    &amp;quot;Honk!&amp;quot;
  end
end

class Sounds
  def squeak
    &amp;quot;Squeak!&amp;quot;
  end
end

sounds = Sounds.new
sounds.honk    # =&amp;gt; &amp;quot;Honk!&amp;quot;
sounds.squeak  # =&amp;gt; &amp;quot;Squeak!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that both the &lt;code&gt;#honk&lt;/code&gt; and &lt;code&gt;#squeak&lt;/code&gt; methods are available on the &lt;code&gt;Sounds&lt;/code&gt; class through the magic of reopening.&lt;/p&gt;
&lt;p&gt;Essentially, monkeypatching is the act of reopening classes in 3rd-party code.&lt;/p&gt;
&lt;h2&gt;Is Monkeypatching Dangerous?&lt;/h2&gt;
&lt;p&gt;If the previous sentence scared you, that&amp;#39;s probably a good thing. Monkeypatching, especially when done carelessly, can cause real chaos.&lt;/p&gt;
&lt;p&gt;Consider for a moment what would happen if we were to redefine &lt;code&gt;Array#&amp;lt;&amp;lt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Array
  def &amp;lt;&amp;lt;(*args)
    # do nothing 😈
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these four lines of code, every single array instance in the entire program is now broken.&lt;/p&gt;
&lt;p&gt;What&amp;#39;s more, the original implementation of &lt;code&gt;#&amp;lt;&amp;lt;&lt;/code&gt; is gone. Aside from restarting the Ruby process, there&amp;#39;s no way to get it back.&lt;/p&gt;
&lt;h2&gt;When Monkeypatching Goes Horribly Wrong&lt;/h2&gt;
&lt;p&gt;Back in 2011, I worked for a prominent social networking company. At the time, the codebase was a massive Rails monolith running on Ruby 1.8.7. Several hundred engineers contributed to the codebase on a daily basis, and the pace of development was very fast.&lt;/p&gt;
&lt;p&gt;At one point, my team decided to monkeypatch &lt;code&gt;String#%&lt;/code&gt; to make writing plurals easier for internationalization purposes. Here&amp;#39;s an example of what our patch could do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;replacements = {
  horse_count: 3,
  horses: {
    one: &amp;quot;is 1 horse&amp;quot;,
    other: &amp;quot;are %{horse_count} horses&amp;quot;
  }
}

# &amp;quot;there are 3 horses in the barn&amp;quot;
&amp;quot;there %{horse_count:horses} in the barn&amp;quot; % replacements
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We wrote up the patch and eventually got it deployed into production... only to find that it didn&amp;#39;t work. Our users were seeing strings with literal &lt;code&gt;%{...}&lt;/code&gt; characters instead of nicely pluralized text. It didn&amp;#39;t make sense. The patch had worked perfectly well in the development environment on my laptop. Why wasn&amp;#39;t it working in production?&lt;/p&gt;
&lt;p&gt;Initially, we thought we&amp;#39;d found a bug in Ruby itself, only later, to find that a production Rails console produced a different result than a Rails console in development. Since both consoles ran on the same Ruby version, we could rule out a bug in the Ruby standard library. Something else was going on.&lt;/p&gt;
&lt;p&gt;After several days of head-scratching, a co-worker was able to track down a Rails initializer that added &lt;em&gt;another&lt;/em&gt; implementation of &lt;code&gt;String#%&lt;/code&gt; that none of us had seen before. To further complicate things, this earlier implementation also contained a bug, so the results we saw in the production console differed from Ruby&amp;#39;s official documentation.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s not the end of the story though. In tracking down the earlier monkeypatch, we also found no less than three others, &lt;em&gt;all patching the same method.&lt;/em&gt; We looked at each other in horror. How did this ever work??&lt;/p&gt;
&lt;p&gt;We eventually chalked the inconsistent behavior up to Rails&amp;#39; eager loading. In development, Rails lazy loads Ruby files, i.e., only loads them when they are &lt;code&gt;require&lt;/code&gt;d. In production, however, Rails loads all of the app&amp;#39;s Ruby files at initialization. This can throw a big monkey wrench into monkeypatching.&lt;/p&gt;
&lt;h2&gt;Consequences of Reopening a Class&lt;/h2&gt;
&lt;p&gt;In this case, each of the monkeypatches reopened the &lt;code&gt;String&lt;/code&gt; class and effectively replaced the existing version of the &lt;code&gt;#%&lt;/code&gt; method with another one. There are several major pitfalls to this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The last patch applied &amp;quot;wins&amp;quot;, meaning, behavior is dependent on load order&lt;/li&gt;
&lt;li&gt;There&amp;#39;s no way to access the original implementation&lt;/li&gt;
&lt;li&gt;Patches leave almost no audit trail, which makes them very difficult to find later&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not surprisingly, perhaps, we ran into all of these.&lt;/p&gt;
&lt;p&gt;At first, we didn&amp;#39;t even know there were other monkeypatches at play. Because of the bug in the winning method, it appeared the original implementation was broken. When we discovered the other competing patches, it was impossible to tell which won without adding copious &lt;code&gt;puts&lt;/code&gt; statements.&lt;/p&gt;
&lt;p&gt;Finally, even when we did discover which method won in development, a different one would win in production. It was also programmatically difficult to tell which patch had been applied last since Ruby 1.8 didn&amp;#39;t have the wonderful &lt;code&gt;Method#source_location&lt;/code&gt; method we now have.&lt;/p&gt;
&lt;p&gt;I spent at least a week trying to figure out what was going on, time I essentially wasted chasing an entirely avoidable problem.&lt;/p&gt;
&lt;p&gt;Eventually, we decided to introduce the &lt;code&gt;LocalizedString&lt;/code&gt; wrapper class with an accompanying &lt;code&gt;#%&lt;/code&gt; method. Our &lt;code&gt;String&lt;/code&gt; monkeypatch then simply became:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class String
  def localize
    LocalizedString.new(self)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;When Monkeypatching Fails&lt;/h2&gt;
&lt;p&gt;In my experience, monkeypatches often fail for one of two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The patch itself is broken.&lt;/strong&gt; In the codebase I mentioned above, not only were there several competing implementations of the same method, but the method that &amp;quot;won&amp;quot; didn&amp;#39;t work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assumptions are invalid.&lt;/strong&gt; The host code has been updated and the patch no longer applies as written.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s look at the second bullet point in more detail.&lt;/p&gt;
&lt;h2&gt;Even the Best-Laid Plans...&lt;/h2&gt;
&lt;p&gt;Monkeypatching often fails for the same reason you reached for it in the first place — because you don&amp;#39;t have access to the original code. For precisely that reason, the original code can change out from under you.&lt;/p&gt;
&lt;p&gt;Consider this example in a gem that your app depends on:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Sale
  def initialize(amount, discount_pct, tax_rate = nil)
    @amount = amount
    @discount_pct = discount_pct
    @tax_rate = tax_rate
  end

  def total
    discounted_amount + sales_tax
  end

  private

  def discounted_amount
    @amount * (1 - @discount_pct)
  end

  def sales_tax
    if @tax_rate
      discounted_amount * @tax_rate
    else
      0
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wait, that&amp;#39;s not right. Sales tax should be applied to the full amount, not the discounted amount. You submit a pull request to the project. While you&amp;#39;re waiting for the maintainer to merge your PR, you add this monkeypatch to your app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Sale
  private

  def sales_tax
    if @tax_rate
      @amount * @tax_rate
    else
      0
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It works perfectly. You check it in and forget about it.&lt;/p&gt;
&lt;p&gt;Everything is fine for a long time. Then one day the finance team sends you an email asking why the company hasn&amp;#39;t been collecting sales tax for a month.&lt;/p&gt;
&lt;p&gt;Confused, you start digging into the issue and eventually notice one of your co-workers recently updated the gem that contains the &lt;code&gt;Sale&lt;/code&gt; class. Here&amp;#39;s the updated code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Sale
  def initialize(amount, discount_pct, sales_tax_rate = nil)
    @amount = amount
    @discount_pct = discount_pct
    @sales_tax_rate = sales_tax_rate
  end

  def total
    discounted_amount + sales_tax
  end

  private

  def discounted_amount
    @amount * (1 - @discount_pct)
  end

  def sales_tax
    if @sales_tax_rate
      discounted_amount * @sales_tax_rate
    else
      0
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks like one of the project maintainers renamed the &lt;code&gt;@tax_rate&lt;/code&gt; instance variable to &lt;code&gt;@sales_tax_rate&lt;/code&gt;. The monkeypatch checks the value of the old &lt;code&gt;@tax_rate&lt;/code&gt; variable, which is always &lt;code&gt;nil&lt;/code&gt;. Nobody noticed because no errors were ever raised. The app chugged along as if nothing had happened.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Why Monkeypatch?&lt;/h2&gt;
&lt;p&gt;Given these examples, it might seem like monkeypatching just isn&amp;#39;t worth the potential headaches. So why do we do it? In my opinion, there are three major use-cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To fix broken or incomplete 3rd-party code&lt;/li&gt;
&lt;li&gt;To quickly test a change or multiple changes in development&lt;/li&gt;
&lt;li&gt;To wrap existing functionality with instrumentation or annotation code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In some cases, the &lt;em&gt;only&lt;/em&gt; viable way to address a bug or performance issue in 3rd-party code is to apply a monkeypatch.&lt;/p&gt;
&lt;p&gt;But with great power comes great responsibility.&lt;/p&gt;
&lt;h2&gt;Monkeypatching Responsibly&lt;/h2&gt;
&lt;p&gt;I like to frame the monkeypatching conversation around responsibility instead of whether or not it&amp;#39;s good or bad. Sure, monkeypatching can cause chaos when done poorly. However, if done with some care and diligence, there&amp;#39;s no reason to avoid reaching for it when the situation warrants it.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the list of rules I try to follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Wrap the patch in a module with an obvious name and use &lt;code&gt;Module#prepend&lt;/code&gt; to apply it&lt;/li&gt;
&lt;li&gt;Make sure you&amp;#39;re patching the right thing&lt;/li&gt;
&lt;li&gt;Limit the patch&amp;#39;s surface area&lt;/li&gt;
&lt;li&gt;Give yourself escape hatches&lt;/li&gt;
&lt;li&gt;Over-communicate&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For the remainder of this article, we&amp;#39;re going to use these rules to write up a monkeypatch for Rails&amp;#39; &lt;code&gt;DateTimeSelector&lt;/code&gt; so it optionally skips rendering discarded fields. This is a change I actually tried to make to Rails a few years ago. &lt;a href=&quot;https://github.com/rails/rails/pull/31533&quot;&gt;You can find the details here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You don&amp;#39;t have to know much about discarded fields to understand the monkeypatch, though. At the end of the day, all it does is replace a single method called &lt;code&gt;build_hidden&lt;/code&gt; with one that effectively does nothing.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h3&gt;Use &lt;code&gt;Module#prepend&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;In the codebase I encountered in my previous role, all the implementations of &lt;code&gt;String#%&lt;/code&gt; were applied by reopening the &lt;code&gt;String&lt;/code&gt; class. Here&amp;#39;s an augmented list of the drawbacks I mentioned earlier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Errors appear to have originated from the host class or module instead of from the patch code&lt;/li&gt;
&lt;li&gt;Any methods you define in the patch replace existing methods with the same name, meaning, there&amp;#39;s no way of invoking the original implementation.&lt;/li&gt;
&lt;li&gt;There&amp;#39;s no way to know which patches were applied and therefore, which methods &amp;quot;won&amp;quot;&lt;/li&gt;
&lt;li&gt;Patches leave almost no audit trail, which makes them very difficult to find later&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead, it&amp;#39;s much better to wrap your patch in a module and apply it using &lt;code&gt;Module#prepend&lt;/code&gt;. Doing so leaves you free to call the original implementation, and a quick call to &lt;code&gt;Module#ancestors&lt;/code&gt; will show the patch in the inheritance hierarchy so it&amp;#39;s easier to find if things go wrong.&lt;/p&gt;
&lt;p&gt;Finally, a simple &lt;code&gt;prepend&lt;/code&gt; statement is easy to comment out if you need to disable the patch for some reason.&lt;/p&gt;
&lt;p&gt;Here are the beginnings of a module for our Rails monkeypatch:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module RenderDiscardedMonkeypatch
end

ActionView::Helpers::DateTimeSelector.prepend(
  RenderDiscardedMonkeypatch
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Patch the Right Thing&lt;/h3&gt;
&lt;p&gt;If you take one thing away from this article, let it be this: don&amp;#39;t apply a monkeypatch unless you know you&amp;#39;re patching the right code. In most cases, it should be possible to verify programmatically that your assumptions still hold (this is Ruby after all). Here&amp;#39;s a checklist:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make sure the class or module you&amp;#39;re trying to patch exists&lt;/li&gt;
&lt;li&gt;Make sure methods exist and have the right arity&lt;/li&gt;
&lt;li&gt;If the code you&amp;#39;re patching lives in a gem, check the gem&amp;#39;s version&lt;/li&gt;
&lt;li&gt;Bail out with a helpful error message if assumptions don&amp;#39;t hold&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Right off the bat, our patch code has made a pretty important assumption. It assumes a constant called &lt;code&gt;ActionView::Helpers::DateTimeSelector&lt;/code&gt; exists and is a class or module.&lt;/p&gt;
&lt;h3&gt;Check Class/Module&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s ensure that constant exists before trying to patch it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module RenderDiscardedMonkeypatch
end

const = begin
  Kernel.const_get(&amp;#39;ActionView::Helpers::DateTimeSelector&amp;#39;)
rescue NameError
end

if const
  const.prepend(RenderDiscardedMonkeypatch)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great, but now we&amp;#39;ve leaked a local variable (&lt;code&gt;const&lt;/code&gt;) into the global scope. Let&amp;#39;s fix that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module RenderDiscardedMonkeypatch
  def self.apply_patch
    const = begin
      Kernel.const_get(&amp;#39;ActionView::Helpers::DateTimeSelector&amp;#39;)
    rescue NameError
    end

    if const
      const.prepend(self)
    end
  end
end

RenderDiscardedMonkeypatch.apply_patch
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Check Methods&lt;/h3&gt;
&lt;p&gt;Next, let&amp;#39;s introduce the patched &lt;code&gt;build_hidden&lt;/code&gt; method. Let&amp;#39;s also add a check to make sure it exists and accepts the right number of arguments (i.e. has the right arity). If those assumptions don&amp;#39;t hold, something&amp;#39;s probably wrong:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module RenderDiscardedMonkeypatch
  class &amp;lt;&amp;lt; self
    def apply_patch
      const = find_const
      mtd = find_method(const)

      if const &amp;amp;&amp;amp; mtd &amp;amp;&amp;amp; mtd.arity == 2
        const.prepend(self)
      end
    end

    private

    def find_const
      Kernel.const_get(&amp;#39;ActionView::Helpers::DateTimeSelector&amp;#39;)
    rescue NameError
    end

    def find_method(const)
      return unless const
      const.instance_method(:build_hidden)
    rescue NameError
    end
  end

  def build_hidden(type, value)
    &amp;#39;&amp;#39;
  end
end

RenderDiscardedMonkeypatch.apply_patch
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Check Gem Versions&lt;/h3&gt;
&lt;p&gt;Finally, let&amp;#39;s check that we&amp;#39;re using the right version of Rails. If Rails gets upgraded, we might need to update the patch too (or get rid of it entirely).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module RenderDiscardedMonkeypatch
  class &amp;lt;&amp;lt; self
    def apply_patch
      const = find_const
      mtd = find_method(const)

      if const &amp;amp;&amp;amp; mtd &amp;amp;&amp;amp; mtd.arity == 2 &amp;amp;&amp;amp; rails_version_ok?
        const.prepend(self)
      end
    end

    private

    def find_const
      Kernel.const_get(&amp;#39;ActionView::Helpers::DateTimeSelector&amp;#39;)
    rescue NameError
    end

    def find_method(const)
      return unless const
      const.instance_method(:build_hidden)
    rescue NameError
    end

    def rails_version_ok?
      Rails::VERSION::MAJOR == 6 &amp;amp;&amp;amp; Rails::VERSION::MINOR == 1
    end
  end

  def build_hidden(type, value)
    &amp;#39;&amp;#39;
  end
end

RenderDiscardedMonkeypatch.apply_patch
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Bail Out Helpfully&lt;/h3&gt;
&lt;p&gt;If your verification code uncovers a discrepancy between expectations and reality, it&amp;#39;s a good idea to raise an error or at least print a helpful warning message. The idea here is to alert you and your co-workers when something seems amiss.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s how we might modify our Rails patch:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module RenderDiscardedMonkeypatch
  class &amp;lt;&amp;lt; self
    def apply_patch
      const = find_const
      mtd = find_method(const)

      unless const &amp;amp;&amp;amp; mtd &amp;amp;&amp;amp; mtd.arity == 2
        raise &amp;quot;Could not find class or method when patching &amp;quot;\
          &amp;quot;ActionView&amp;#39;s date_select helper. Please investigate.&amp;quot;
      end

      unless rails_version_ok?
        puts &amp;quot;WARNING: It looks like Rails has been upgraded since &amp;quot;\
          &amp;quot;ActionView&amp;#39;s date_select helper was monkeypatched in &amp;quot;\
          &amp;quot;#{__FILE__}. Please reevaluate the patch.&amp;quot;
      end

      const.prepend(self)
    end

    private

    def find_const
      Kernel.const_get(&amp;#39;ActionView::Helpers::DateTimeSelector&amp;#39;)
    rescue NameError
    end

    def find_method(const)
      return unless const
      const.instance_method(:build_hidden)
    rescue NameError
    end

    def rails_version_ok?
      Rails::VERSION::MAJOR == 6 &amp;amp;&amp;amp; Rails::VERSION::MINOR == 1
    end
  end

  def build_hidden(type, value)
    &amp;#39;&amp;#39;
  end
end

RenderDiscardedMonkeypatch.apply_patch
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Limit Surface Area&lt;/h3&gt;
&lt;p&gt;While it may seem perfectly innocuous to define helper methods in a monkeypatch, remember that any methods defined via &lt;code&gt;Module#prepend&lt;/code&gt; will override existing ones through the magic of inheritance. While it might seem as though a host class or module doesn&amp;#39;t define a particular method, it&amp;#39;s difficult to know for sure. For this reason, I try to only define methods I intend to patch.&lt;/p&gt;
&lt;p&gt;Note that this also applies to methods defined in the object&amp;#39;s singleton class, i.e. methods defined inside &lt;code&gt;class &amp;lt;&amp;lt; self&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s how to modify our Rails patch to only replace the one &lt;code&gt;#build_hidden&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module RenderDiscardedMonkeypatch
  class &amp;lt;&amp;lt; self
    def apply_patch
      const = find_const
      mtd = find_method(const)

      unless const &amp;amp;&amp;amp; mtd &amp;amp;&amp;amp; mtd.arity == 2
        raise &amp;quot;Could not find class or method when patching&amp;quot;\
          &amp;quot;ActionView&amp;#39;s date_select helper. Please investigate.&amp;quot;
      end

      unless rails_version_ok?
        puts &amp;quot;WARNING: It looks like Rails has been upgraded since&amp;quot;\
          &amp;quot;ActionView&amp;#39;s date_selet helper was monkeypatched in &amp;quot;\
          &amp;quot;#{__FILE__}. Please reevaluate the patch.&amp;quot;
      end

      const.prepend(InstanceMethods)
    end

    private

    def find_const
      Kernel.const_get(&amp;#39;ActionView::Helpers::DateTimeSelector&amp;#39;)
    rescue NameError
    end

    def find_method(const)
      return unless const
      const.instance_method(:build_hidden)
    rescue NameError
    end

    def rails_version_ok?
      Rails::VERSION::MAJOR == 6 &amp;amp;&amp;amp; Rails::VERSION::MINOR == 1
    end
  end

  module InstanceMethods
    def build_hidden(type, value)
      &amp;#39;&amp;#39;
    end
  end
end

RenderDiscardedMonkeypatch.apply_patch
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Give Yourself Escape Hatches&lt;/h3&gt;
&lt;p&gt;When possible, I like to make my monkeypatch&amp;#39;s functionality opt-in. That&amp;#39;s only really an option if you have control over where the patched code is invoked. In the case of our Rails patch, it&amp;#39;s doable via the &lt;code&gt;@options&lt;/code&gt; hash in &lt;code&gt;DateTimeSelector&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module RenderDiscardedMonkeypatch
  class &amp;lt;&amp;lt; self
    def apply_patch
      const = find_const
      mtd = find_method(const)

      unless const &amp;amp;&amp;amp; mtd &amp;amp;&amp;amp; mtd.arity == 2
        raise &amp;quot;Could not find class or method when patching&amp;quot;\
          &amp;quot;ActionView&amp;#39;s date_select helper. Please investigate.&amp;quot;
      end

      unless rails_version_ok?
        puts &amp;quot;WARNING: It looks like Rails has been upgraded since&amp;quot;\
          &amp;quot;ActionView&amp;#39;s date_selet helper was monkeypatched in &amp;quot;\
          &amp;quot;#{__FILE__}. Please reevaluate the patch.&amp;quot;
      end

      const.prepend(InstanceMethods)
    end

    private

    def find_const
      Kernel.const_get(&amp;#39;ActionView::Helpers::DateTimeSelector&amp;#39;)
    rescue NameError
    end

    def find_method(const)
      return unless const
      const.instance_method(:build_hidden)
    rescue NameError
    end

    def rails_version_ok?
      Rails::VERSION::MAJOR == 6 &amp;amp;&amp;amp; Rails::VERSION::MINOR == 1
    end
  end

  module InstanceMethods
    def build_hidden(type, value)
      if @options.fetch(:render_discarded, true)
        super
      else
        &amp;#39;&amp;#39;
      end
    end
  end
end

RenderDiscardedMonkeypatch.apply_patch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice! Now callers can opt-in by calling the &lt;code&gt;date_select&lt;/code&gt; helper with the new option. No other codepaths are affected:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;date_select(@user, :date_of_birth, {
  order: [:month, :day],
  render_discarded: false
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Over-Communicate&lt;/h3&gt;
&lt;p&gt;The last piece of advice I have for you is perhaps the most important — communicating what your patch does and when it&amp;#39;s time to re-examine it. Your goal with monkeypatches should always be to eventually remove the patch altogether. To that end, a responsible monkeypatch includes comments that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Describe what the patch does&lt;/li&gt;
&lt;li&gt;Explain why the patch is necessary&lt;/li&gt;
&lt;li&gt;Outline the assumptions the patch makes&lt;/li&gt;
&lt;li&gt;Specify a date in the future when your team should reconsider alternative solutions, like pulling in an updated gem&lt;/li&gt;
&lt;li&gt;Include links to relevant pull requests, blog posts, StackOverflow answers, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might even print a warning or fail a test on a predetermined date to urge the team to reconfirm the patch&amp;#39;s assumptions and consider whether or not it&amp;#39;s still necessary.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the final version of our Rails &lt;code&gt;date_select&lt;/code&gt; patch, complete with comments and a date check:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# ActionView&amp;#39;s date_select helper provides the option to &amp;quot;discard&amp;quot; certain
# fields. Discarded fields are (confusingly) still rendered to the page
# using hidden inputs, i.e. &amp;lt;input type=&amp;quot;hidden&amp;quot; /&amp;gt;. This patch adds an
# additional option to the date_select helper that allows the caller to
# skip rendering the chosen fields altogether. For example, to render all
# but the year field, you might have this in one of your views:
#
# date_select(:date_of_birth, order: [:month, :day])
#
# or, equivalently:
#
# date_select(:date_of_birth, discard_year: true)
#
# To avoid rendering the year field altogether, set :render_discarded to
# false:
#
# date_select(:date_of_birth, discard_year: true, render_discarded: false)
#
# This patch assumes the #build_hidden method exists on
# ActionView::Helpers::DateTimeSelector and accepts two arguments.
#
module RenderDiscardedMonkeypatch
  class &amp;lt;&amp;lt; self
    EXPIRATION_DATE = Date.new(2021, 8, 15)

    def apply_patch
      if Date.today &amp;gt; EXPIRATION_DATE
        puts &amp;quot;WARNING: Please re-evaluate whether or not the ActionView &amp;quot;\
          &amp;quot;date_select patch present in #{__FILE__} is still necessary.&amp;quot;
      end

      const = find_const
      mtd = find_method(const)

      # make sure the class we want to patch exists;
      # make sure the #build_hidden method exists and accepts exactly
      # two arguments
      unless const &amp;amp;&amp;amp; mtd &amp;amp;&amp;amp; mtd.arity == 2
        raise &amp;quot;Could not find class or method when patching &amp;quot;\
          &amp;quot;ActionView&amp;#39;s date_select helper. Please investigate.&amp;quot;
      end

      # if rails has been upgraded, make sure this patch is still
      # necessary
      unless rails_version_ok?
        puts &amp;quot;WARNING: It looks like Rails has been upgraded since &amp;quot;\
          &amp;quot;ActionView&amp;#39;s date_select helper was monkeypatched in &amp;quot;\
          &amp;quot;#{__FILE__}. Please re-evaluate the patch.&amp;quot;
      end

      # actually apply the patch
      const.prepend(InstanceMethods)
    end

    private

    def find_const
      Kernel.const_get(&amp;#39;ActionView::Helpers::DateTimeSelector&amp;#39;)
    rescue NameError
      # return nil if the constant doesn&amp;#39;t exist
    end

    def find_method(const)
      return unless const
      const.instance_method(:build_hidden)
    rescue NameError
      # return nil if the method doesn&amp;#39;t exist
    end

    def rails_version_ok?
      Rails::VERSION::MAJOR == 6 &amp;amp;&amp;amp; Rails::VERSION::MINOR == 1
    end
  end

  module InstanceMethods
    # :render_discarded is an additional option you can pass to the
    # date_select helper in your views. Use it to avoid rendering
    # &amp;quot;discarded&amp;quot; fields, i.e. fields marked as discarded or simply
    # not included in date_select&amp;#39;s :order array. For example,
    # specifying order: [:day, :month] will cause the helper to
    # &amp;quot;discard&amp;quot; the :year field. Discarding a field renders it as a
    # hidden input. Set :render_discarded to false to avoid rendering
    # it altogether.
    def build_hidden(type, value)
      if @options.fetch(:render_discarded, true)
        super
      else
        &amp;#39;&amp;#39;
      end
    end
  end
end

RenderDiscardedMonkeypatch.apply_patch
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I totally get that some of the suggestions I&amp;#39;ve outlined above might seem like overkill. Our Rails patch contains way more defensive verification code than actual patch code!&lt;/p&gt;
&lt;p&gt;Think of all that extra code as a sheath for your broadsword. It&amp;#39;s a lot easier to avoid getting cut if it&amp;#39;s enveloped in a layer of protection.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-08/giphy-downsized.gif&quot; alt=&quot;Sword guitar&quot;/&gt;&lt;/p&gt;
&lt;p&gt;What really matters, though, is that I feel confident deploying responsible monkeypatches into production. Irresponsible ones are just time bombs waiting to cost you or your company time, money, and developer health.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby&#039;s Hidden Gems: Bullet</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/08/11/ruby-hidden-gems-bullet-and-how-it-integrates-with-appsignal.html"/>
    <id>https://blog.appsignal.com/2021/08/11/ruby-hidden-gems-bullet-and-how-it-integrates-with-appsignal.html</id>
    <published>2021-08-11T00:00:00+00:00</published>
    <updated>2021-08-11T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s dive into Bullet and find out how it can help you identify some database-related problems.</summary>
    <content type="html">&lt;p&gt;A database is the heart of many applications, and having problems with it may result in serious performance issues.&lt;/p&gt;
&lt;p&gt;ORMs such as ActiveRecord and Mongoid help us abstract implementation and deliver code faster, but sometimes, we forget to check what queries are running under the hood.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/flyerhzm/bullet&quot;&gt;bullet&lt;/a&gt; gem helps us identify some well-known database-related problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;quot;N+1 Queries&amp;quot;: when the application runs a query to load each item of a list&lt;/li&gt;
&lt;li&gt;&amp;quot;Unused Eager Loading&amp;quot;: when the application loads data, usually to avoid N+1 queries, but doesn&amp;#39;t use it&lt;/li&gt;
&lt;li&gt;&amp;quot;Missing Counter Cache&amp;quot;: when the application needs to execute count queries to get the number of associated items&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this post, I&amp;#39;m going to show:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how to configure the &lt;code&gt;bullet&lt;/code&gt; gem in a Ruby project,&lt;/li&gt;
&lt;li&gt;examples of each problem mentioned before,&lt;/li&gt;
&lt;li&gt;how &lt;code&gt;bullet&lt;/code&gt; detects each,&lt;/li&gt;
&lt;li&gt;how to fix each problem, and&lt;/li&gt;
&lt;li&gt;how to integrate &lt;code&gt;bullet&lt;/code&gt; with AppSignal.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will use some examples from &lt;a href=&quot;https://github.com/fabioperrella/bullet-test&quot;&gt;a project that I created for this post&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;How to Configure Bullet in a Ruby Project&lt;/h2&gt;
&lt;p&gt;First, add the gem to &lt;code&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can add it to all environments given, we can enable or disable it and use a different approach on each one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &amp;#39;bullet&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, it&amp;#39;s necessary to configure it.&lt;/p&gt;
&lt;p&gt;If you are in a Rails project, you can run the following command to generate the configuration code automatically:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle exec rails g bullet:install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are in a non Rails project, you can add it manually, for example, by adding the following code in &lt;code&gt;spec_helper.rb&lt;/code&gt; after loading the application&amp;#39;s code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Bullet.enable        = true
Bullet.bullet_logger = true
Bullet.raise         = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And adding the following code in the main file after loading the application&amp;#39;s code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Bullet.enable = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&amp;#39;m going to share more details on configurations in this post. If you want to see them all, go to &lt;a href=&quot;https://github.com/flyerhzm/bullet#configuration&quot;&gt;bullet&amp;#39;s README page&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Using bullet In Tests&lt;/h2&gt;
&lt;p&gt;With the previously suggested configuration, Bullet will detect bad queries executed in tests and raise exceptions for them.&lt;/p&gt;
&lt;p&gt;Now, let&amp;#39;s see some examples.&lt;/p&gt;
&lt;h3&gt;Detecting N+1 Queries&lt;/h3&gt;
&lt;p&gt;Given an &lt;code&gt;index&lt;/code&gt; action as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
class PostsController &amp;lt; ApplicationController
  def index
    @posts = Post.all
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a view like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;# app/views/posts/index.html.erb

&amp;lt;h1&amp;gt;Posts&amp;lt;/h1&amp;gt;

&amp;lt;table&amp;gt;
  &amp;lt;thead&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
      &amp;lt;th&amp;gt;Comments&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;

  &amp;lt;tbody&amp;gt;
    &amp;lt;% @posts.each do |post| %&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= post.name %&amp;gt;&amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= post.comments.map(&amp;amp;:name) %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;bullet&lt;/code&gt; will raise an error detecting an &amp;quot;N+1&amp;quot; when running an integrated test that executes code from the view and the controller, for example, using a request spec as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# spec/requests/posts_request_spec.rb
require &amp;#39;rails_helper&amp;#39;

RSpec.describe &amp;quot;Posts&amp;quot;, type: :request do
  describe &amp;quot;GET /index&amp;quot; do
    it &amp;#39;lists all posts&amp;#39; do
      post1 = Post.create!
      post2 = Post.create!

      get &amp;#39;/posts&amp;#39;

      expect(response.status).to eq(200)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;In this case, it will raise this exception:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Failures:

  1) Posts GET /index lists all posts
     Failure/Error: get &amp;#39;/posts&amp;#39;

     Bullet::Notification::UnoptimizedQueryError:
       user: fabioperrella
       GET /posts
       USE eager loading detected
         Post =&amp;gt; [:comments]
         Add to your query: .includes([:comments])
       Call stack
         /Users/fabioperrella/projects/bullet-test/app/views/posts/index.html.erb:17:in `map&amp;#39;
         ...
     # ./spec/requests/posts_controller_spec.rb:9:in `block (3 levels) in &amp;lt;top (required)&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This happens because the view is executing one query to load each comment name in &lt;code&gt;post.comments.map(&amp;amp;:name)&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Processing by PostsController#index as HTML
  Post Load (0.4ms)  SELECT &amp;quot;posts&amp;quot;.* FROM &amp;quot;posts&amp;quot;
  ↳ app/views/posts/index.html.erb:14
  Comment Load (0.0ms)  SELECT &amp;quot;comments&amp;quot;.* FROM &amp;quot;comments&amp;quot; WHERE &amp;quot;comments&amp;quot;.&amp;quot;post_id&amp;quot; = ?  [[&amp;quot;post_id&amp;quot;, 1]]
  ↳ app/views/posts/index.html.erb:17:in `map&amp;#39;
  Comment Load (0.1ms)  SELECT &amp;quot;comments&amp;quot;.* FROM &amp;quot;comments&amp;quot; WHERE &amp;quot;comments&amp;quot;.&amp;quot;post_id&amp;quot; = ?  [[&amp;quot;post_id&amp;quot;, 2]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To fix it, we can simply follow the instruction in the error message and add &lt;code&gt;.includes([:comments])&lt;/code&gt; to the query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;-@posts = Post.all
+@posts = Post.all.includes([:comments])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will instruct ActiveRecord to load all the comments with only 1 query.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Processing by PostsController#index as HTML
  Post Load (0.2ms)  SELECT &amp;quot;posts&amp;quot;.* FROM &amp;quot;posts&amp;quot;
  ↳ app/views/posts/index.html.erb:14
  Comment Load (0.0ms)  SELECT &amp;quot;comments&amp;quot;.* FROM &amp;quot;comments&amp;quot; WHERE &amp;quot;comments&amp;quot;.&amp;quot;post_id&amp;quot; IN (?, ?)  [[&amp;quot;post_id&amp;quot;, 1], [&amp;quot;post_id&amp;quot;, 2]]
  ↳ app/views/posts/index.html.erb:14
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, &lt;code&gt;bullet&lt;/code&gt; will not raise an exception in a controller test like the following, because controller tests don&amp;#39;t render views by default, so the N+1 query will not be triggered.&lt;/p&gt;
&lt;p&gt;Note: &lt;a href=&quot;https://rspec.info/blog/2016/07/rspec-3-5-has-been-released/#rails-support-for-rails-5&quot;&gt;controller tests are discouraged&lt;/a&gt; since Rails 5:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# spec/controllers/posts_controller_spec.rb
require &amp;#39;rails_helper&amp;#39;

RSpec.describe PostsController do
  describe &amp;#39;GET index&amp;#39; do
    it &amp;#39;lists all posts&amp;#39; do
      post1 = Post.create!
      post2 = Post.create!

      get :index

      expect(response.status).to eq(200)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another example of a test that Bullet will not detect an &amp;quot;N+1&amp;quot; is a view test because, in this case, it will not run the N+1 queries in the database:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# spec/views/posts/index.html.erb_spec.rb
require &amp;#39;rails_helper&amp;#39;

describe &amp;quot;posts/index.html.erb&amp;quot; do
  it &amp;#39;lists all posts&amp;#39; do
    post1 = Post.create!(name: &amp;#39;post1&amp;#39;)
    post2 = Post.create!(name: &amp;#39;post2&amp;#39;)

    assign(:posts, [post1, post2])

    render

    expect(rendered).to include(&amp;#39;post1&amp;#39;)
    expect(rendered).to include(&amp;#39;post2&amp;#39;)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;A Tip to Have More Chances to Detect an N+1 in Tests&lt;/h3&gt;
&lt;p&gt;I recommend creating at least 1 request spec for each controller action, just to test if it returns the correct HTTP status, then &lt;code&gt;bullet&lt;/code&gt; will be watching the queries when rendering these views.&lt;/p&gt;
&lt;h3&gt;Detecting Unused Eager Loading&lt;/h3&gt;
&lt;p&gt;Given the following &lt;code&gt;basic_index&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
class PostsController &amp;lt; ApplicationController
  def basic_index
    @posts = Post.all.includes(:comments)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the following &lt;code&gt;basic_index&lt;/code&gt; view:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;# app/views/posts/basic_index.html.erb

&amp;lt;h1&amp;gt;Posts&amp;lt;/h1&amp;gt;

&amp;lt;table&amp;gt;
  &amp;lt;thead&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;

  &amp;lt;tbody&amp;gt;
    &amp;lt;% @posts.each do |post| %&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= post.name %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we run the following test:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# spec/requests/posts_request_spec.rb
require &amp;#39;rails_helper&amp;#39;

RSpec.describe &amp;quot;Posts&amp;quot;, type: :request do
  describe &amp;quot;GET /basic_index&amp;quot; do
    it &amp;#39;lists all posts&amp;#39; do
      post1 = Post.create!
      post2 = Post.create!

      get &amp;#39;/posts/basic_index&amp;#39;

      expect(response.status).to eq(200)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bullet will raise the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  1) Posts GET /basic_index lists all posts
     Failure/Error: get &amp;#39;/posts/basic_index&amp;#39;

     Bullet::Notification::UnoptimizedQueryError:
       user: fabioperrella
       GET /posts/basic_index
       AVOID eager loading detected
         Post =&amp;gt; [:comments]
         Remove from your query: .includes([:comments])
       Call stack
         /Users/fabioperrella/projects/bullet-test/spec/requests/posts_request_spec.rb:20:in `block (3 levels) in &amp;lt;top (required)&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This happens because it&amp;#39;s not necessary to load the list of comments for this view.&lt;/p&gt;
&lt;p&gt;To fix the problem, we can simply follow the instruction in the error above and remove the query &lt;code&gt;.includes([:comments])&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;-@posts = Post.all.includes(:comments)
+@posts = Post.all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s worth saying that it will not raise the same error if we run only a controller test, without &lt;code&gt;render_views&lt;/code&gt;, as shown before.&lt;/p&gt;
&lt;h3&gt;Detecting Missing Counter Cache&lt;/h3&gt;
&lt;p&gt;Given a controller like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/posts_controller.rb
class PostsController &amp;lt; ApplicationController
  def index_with_counter
    @posts = Post.all
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a view like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;# app/views/posts/index_with_counter.html.erb

&amp;lt;h1&amp;gt;Posts&amp;lt;/h1&amp;gt;

&amp;lt;table&amp;gt;
  &amp;lt;thead&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
      &amp;lt;th&amp;gt;Number of comments&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;

  &amp;lt;tbody&amp;gt;
    &amp;lt;% @posts.each do |post| %&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= post.name %&amp;gt;&amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= post.comments.size %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we run the following request spec:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;describe &amp;quot;GET /index_with_counter&amp;quot; do
  it &amp;#39;lists all posts&amp;#39; do
    post1 = Post.create!
    post2 = Post.create!

    get &amp;#39;/posts/index_with_counter&amp;#39;

    expect(response.status).to eq(200)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;bullet&lt;/code&gt; will raise the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1) Posts GET /index_with_counter lists all posts
  Failure/Error: get &amp;#39;/posts/index_with_counter&amp;#39;

  Bullet::Notification::UnoptimizedQueryError:
    user: fabioperrella
    GET /posts/index_with_counter
    Need Counter Cache
      Post =&amp;gt; [:comments]
  # ./spec/requests/posts_request_spec.rb:31:in `block (3 levels) in &amp;lt;top (required)&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This happens because this view is executing 1 query to count the number of comments in &lt;code&gt;post.comments.size&lt;/code&gt; for each post.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Processing by PostsController#index_with_counter as HTML
  ↳ app/views/posts/index_with_counter.html.erb:14
  Post Load (0.4ms)  SELECT &amp;quot;posts&amp;quot;.* FROM &amp;quot;posts&amp;quot;
  ↳ app/views/posts/index_with_counter.html.erb:14
   (0.4ms)  SELECT COUNT(*) FROM &amp;quot;comments&amp;quot; WHERE &amp;quot;comments&amp;quot;.&amp;quot;post_id&amp;quot; = ?  [[&amp;quot;post_id&amp;quot;, 1]]
  ↳ app/views/posts/index_with_counter.html.erb:17
   (0.1ms)  SELECT COUNT(*) FROM &amp;quot;comments&amp;quot; WHERE &amp;quot;comments&amp;quot;.&amp;quot;post_id&amp;quot; = ?  [[&amp;quot;post_id&amp;quot;, 2]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To fix this, we can create a counter cache, which can be a bit complex, especially if there is data in the production database.&lt;/p&gt;
&lt;p&gt;A counter cache is a column that we can add to a table, that ActiveRecord will update automatically when we insert and delete associated models. There are more details in &lt;a href=&quot;/2018/06/19/activerecords-counter-cache.html&quot;&gt;this post&lt;/a&gt;. I suggest reading it to know how to create and sync the counter cache.&lt;/p&gt;
&lt;h2&gt;Using Bullet in Development&lt;/h2&gt;
&lt;p&gt;Sometimes, tests might not detect the problems previously mentioned, for example, if test coverage is low, so it&amp;#39;s possible to enable &lt;code&gt;bullet&lt;/code&gt; in other environments using different approaches.&lt;/p&gt;
&lt;p&gt;In the development environment, we can enable the following configurations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Bullet.alert         = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, it will show alerts like this in the browser:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-08/bullet-alert.png&quot; alt=&quot;bullet adding an alert in the browser&quot;/&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Bullet.add_footer    = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will add a footer on the page with the error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-08/bullet-footer.png&quot; alt=&quot;bullet adding a footer to the page&quot;/&gt;&lt;/p&gt;
&lt;p&gt;It&amp;#39;s also possible to enable errors to be logged in the browser&amp;#39;s console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Bullet.console    = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will add an error like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-08/bullet-console.png&quot; alt=&quot;bullet adding a message in the console&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Using Bullet in Staging with Appsignal&lt;/h2&gt;
&lt;p&gt;In the &lt;em&gt;staging&lt;/em&gt; environment, we don&amp;#39;t want these error messages to be shown to end-users, but it would be great to know if the application starts to have one of the problems mentioned previously.&lt;/p&gt;
&lt;p&gt;At the same time, &lt;code&gt;bullet&lt;/code&gt; may degrade performance and increase memory consumption in the application, so it&amp;#39;s better to enable it only temporarily in &lt;em&gt;staging&lt;/em&gt;, but don&amp;#39;t enable it in &lt;em&gt;production&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Assuming the &lt;em&gt;staging&lt;/em&gt; environment is using the same configuration file as the &lt;em&gt;production&lt;/em&gt; environment, which is a good practice to reduce the difference between them, we can use an environment variable to enable or disable &lt;code&gt;bullet&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/environments/production.rb
config.after_initialize do
  Bullet.enabled   = ENV.fetch(&amp;#39;BULLET_ENABLED&amp;#39;, false)
  Bullet.appsignal = true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To receive notifications about issues Bullet has found in your staging environment, you can use AppSignal to report those notifications as errors. You&amp;#39;ll need to have the &lt;code&gt;appsignal&lt;/code&gt; gem installed and configured in your project. You can see more details in the &lt;a href=&quot;https://docs.appsignal.com/ruby/installation/&quot;&gt;Ruby gem docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then, if a problem is detected by &lt;code&gt;bullet&lt;/code&gt;, it will create an error incident like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-08/bullet-error-on-appsignal.png&quot; alt=&quot;bullet error on Appsignal&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This error is raised by the &lt;a href=&quot;https://github.com/flyerhzm/uniform_notifier&quot;&gt;uniform_notifier gem&lt;/a&gt; which was extracted from &lt;code&gt;bullet&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, the error message doesn&amp;#39;t show enough information, but I sent in &lt;a href=&quot;https://github.com/flyerhzm/uniform_notifier/pull/69&quot;&gt;a Pull Request to improve this&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;bullet&lt;/code&gt; gem is a great tool that can help us detect problems that will degrade performance in applications.&lt;/p&gt;
&lt;p&gt;Try to keep good test coverage, as previously mentioned, to have greater chances of detecting these problems before going to production.&lt;/p&gt;
&lt;p&gt;As an extra tip, if you want to be even more protected against performance problems related to the database, take a look at the &lt;a href=&quot;https://github.com/WeTransfer/wt-activerecord-index-spy&quot;&gt;wt-activerecord-index-spy&lt;/a&gt; gem, which helps to detect queries that are not using proper indexes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>An Introduction to Pattern Matching in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/07/28/introduction-to-pattern-matching-in-ruby.html"/>
    <id>https://blog.appsignal.com/2021/07/28/introduction-to-pattern-matching-in-ruby.html</id>
    <published>2021-07-28T00:00:00+00:00</published>
    <updated>2021-07-28T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out about pattern matching in Ruby, what it does, and how it can help improve code readability.</summary>
    <content type="html">&lt;p&gt;Let&amp;#39;s start with a brief discussion about pattern matching in Ruby, what it does, and how it can help improve code readability.&lt;/p&gt;
&lt;p&gt;If you are anything like me a few years ago, you might confuse it with pattern matching in Regex. Even a quick Google search of &amp;#39;pattern matching&amp;#39; with no other context brings you content that&amp;#39;s pretty close to that definition.&lt;/p&gt;
&lt;p&gt;Formally, pattern matching is the process of checking any data (be it a sequence of characters, a series of tokens, a tuple, or anything else) against other data.&lt;/p&gt;
&lt;p&gt;In terms of programming, depending on the capabilities of the language, this could mean any of the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Matching against an expected data type&lt;/li&gt;
&lt;li&gt;Matching against an expected hash structure (e.g. presence of specific keys)&lt;/li&gt;
&lt;li&gt;Matching against an expected array length&lt;/li&gt;
&lt;li&gt;Assigning the matches (or a part of them) to some variables&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My first foray into pattern matching was through Elixir. Elixir has &lt;a href=&quot;https://elixir-lang.org/getting-started/pattern-matching.html&quot;&gt;first class support&lt;/a&gt; for pattern matching, so much so that the &lt;code&gt;=&lt;/code&gt; operator is, in fact, the &lt;code&gt;match&lt;/code&gt; operator, rather than simple assignment.&lt;/p&gt;
&lt;p&gt;This means that in Elixir, the following is actually valid code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-elixir&quot;&gt;iex&amp;gt; x = 1
iex&amp;gt; 1 = x
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that in mind, let&amp;#39;s look at the new pattern matching support for Ruby 2.7+ and how we can use it to make our code more readable, starting from today.&lt;/p&gt;
&lt;h2&gt;Ruby Pattern Matching with &lt;code&gt;case&lt;/code&gt;/&lt;code&gt;in&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Ruby supports pattern matching with a special &lt;code&gt;case&lt;/code&gt;/&lt;code&gt;in&lt;/code&gt; expression. The syntax is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;case &amp;lt;expression&amp;gt;
in &amp;lt;pattern1&amp;gt;
  # ...
in &amp;lt;pattern2&amp;gt;
  # ...
else
  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not to be confused with the &lt;code&gt;case&lt;/code&gt;/&lt;code&gt;when&lt;/code&gt; expression. &lt;code&gt;when&lt;/code&gt; and &lt;code&gt;in&lt;/code&gt; branches cannot be mixed in a single &lt;code&gt;case&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you do not provide an &lt;code&gt;else&lt;/code&gt; expression, any failing match will raise a &lt;code&gt;NoMatchingPatternError&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Pattern Matching Arrays in Ruby&lt;/h2&gt;
&lt;p&gt;Pattern matching can be used to match arrays to pre-required structures against data types, lengths or values.&lt;/p&gt;
&lt;p&gt;For example, all of the following are matches (note that only the first &lt;code&gt;in&lt;/code&gt; will be evaluated, as &lt;code&gt;case&lt;/code&gt; stops looking after the first match):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;case [1, 2, &amp;quot;Three&amp;quot;]
in [Integer, Integer, String]
  &amp;quot;matches&amp;quot;
in [1, 2, &amp;quot;Three&amp;quot;]
  &amp;quot;matches&amp;quot;
in [Integer, *]
  &amp;quot;matches&amp;quot; # because * is a spread operator that matches anything
in [a, *]
  &amp;quot;matches&amp;quot; # and the value of the variable a is now 1
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This type of pattern matching clause is very useful when you want to produce multiple signals from a method call.&lt;/p&gt;
&lt;p&gt;In the Elixir world, this is frequently used when performing operations that could have both an &lt;code&gt;:ok&lt;/code&gt; result and an &lt;code&gt;:error&lt;/code&gt; result, for example, inserted into a database.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Here is how we can use it for better readability:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def create
  case save(model_params)
  in [:ok, model]
    render :json =&amp;gt; model
  in [:error, errors]
    render :json =&amp;gt; errors
  end
end

# Somewhere in your code, e.g. inside a global helper or your model base class (with a different name).
def save(attrs)
  model = Model.new(attrs)
  model.save ? [:ok, model] : [:error, model.errors]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pattern Matching Objects in Ruby&lt;/h2&gt;
&lt;p&gt;You can also match objects in Ruby to enforce a specific structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;case {a: 1, b: 2}
in {a: Integer}
  &amp;quot;matches&amp;quot; # By default, all object matches are partial
in {a: Integer, **}
  &amp;quot;matches&amp;quot; # and is same as {a: Integer}
in {a: a}
  &amp;quot;matches&amp;quot; # and the value of variable a is now 1
in {a: Integer =&amp;gt; a}
  &amp;quot;matches&amp;quot; # and the value of variable a is now 1
in {a: 1, b: b}
  &amp;quot;matches&amp;quot; # and the value of variable b is now 2
in {a: Integer, **nil}
  &amp;quot;does not match&amp;quot; # This will match only if the object has a and no other keys
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works great when imposing strong rules for matching against any params.&lt;/p&gt;
&lt;p&gt;For example, if you are writing a fancy greeter, it could have the following (strongly opinionated) structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def greet(hash = {})
  case hash
  in {greeting: greeting, first_name: first_name, last_name: last_name}
    greet(greeting: greeting, name: &amp;quot;#{first_name} #{last_name}&amp;quot;)
  in {greeting: greeting, name: name}
    puts &amp;quot;#{greeting}, #{name}&amp;quot;
  in {name: name}
    greet(greeting: &amp;quot;Hello&amp;quot;, name: name)
  in {greeting: greeting}
    greet(greeting: greeting, name: &amp;quot;Anonymous&amp;quot;)
  else
    greet(greeting: &amp;quot;Hello&amp;quot;, name: &amp;quot;Anonymous&amp;quot;)
  end
end

greet # Hello, Anonymous
greet(name: &amp;quot;John&amp;quot;) # Hello, John
greet(first_name: &amp;quot;John&amp;quot;, last_name: &amp;quot;Doe&amp;quot;) # Hello, John Doe
greet(greeting: &amp;quot;Bonjour&amp;quot;, first_name: &amp;quot;John&amp;quot;, last_name: &amp;quot;Doe&amp;quot;) # Bonjour, John Doe
greet(greeting: &amp;quot;Bonjour&amp;quot;) # Bonjour, Anonymous
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Variable Binding and Pinning in Ruby&lt;/h2&gt;
&lt;p&gt;As we have seen in some of the above examples, pattern matching is really useful in assigning part of the patterns to arbitrary variables.
This is called variable binding, and there are several ways we can bind to a variable:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;With a strong type match, e.g. &lt;code&gt;in [Integer =&amp;gt; a]&lt;/code&gt; or &lt;code&gt;in {a: Integer =&amp;gt; a}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Without the type specification, e.g. &lt;code&gt;in [a, 1, 2]&lt;/code&gt; or &lt;code&gt;in {a: a}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Without the variable name, which defaults to using the key name, e.g. &lt;code&gt;in {a:}&lt;/code&gt; will define a variable named &lt;code&gt;a&lt;/code&gt; with the value at key &lt;code&gt;a&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Bind rest, e.g. &lt;code&gt;in [Integer, *rest]&lt;/code&gt; or &lt;code&gt;in {a: Integer, **rest}&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;How, then, can we match when we want to use an existing variable as a sub-pattern? This is when we can use variable &lt;em&gt;pinning&lt;/em&gt; with the &lt;code&gt;^&lt;/code&gt; (pin) operator:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;a = 1
case {a: 1, b: 2}
in {a: ^a}
  &amp;quot;matches&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can even use this when a variable is defined in a pattern itself, allowing you to write powerful patterns like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;case order
in {billing_address: {city:}, shipping_address: {city: ^city}}
  puts &amp;quot;both billing and shipping are to the same city&amp;quot;
else
  raise &amp;quot;both billing and shipping must be to the same city&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One important quirk to mention with variable binding is that even if the pattern doesn&amp;#39;t fully match, the variable will still have been bound.
This can sometimes be useful.&lt;/p&gt;
&lt;p&gt;But, in most cases, this could also be a cause of subtle bugs — so make sure that you don&amp;#39;t rely on shadowed variable values that have been used inside a match.
For example, in the following, you would expect the city to be &amp;quot;Amsterdam&amp;quot;, but it would instead be &amp;quot;Berlin&amp;quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;city = &amp;quot;Amsterdam&amp;quot;
order = {billing_address: {city: &amp;quot;Berlin&amp;quot;}, shipping_address: {city: &amp;quot;Zurich&amp;quot;}}
case order
in {billing_address: {city:}, shipping_address: {city: ^city}}
  puts &amp;quot;both billing and shipping are to the same city&amp;quot;
else
  puts &amp;quot;both billing and shipping must be to the same city&amp;quot;
end
puts city # Berlin instead of Amsterdam
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Matching Ruby&amp;#39;s Custom Classes&lt;/h2&gt;
&lt;p&gt;You can implement some special methods to make custom classes pattern matching aware in Ruby.&lt;/p&gt;
&lt;p&gt;For example, to pattern match a user against his &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt;, we can define &lt;code&gt;deconstruct_keys&lt;/code&gt; on the class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  def deconstruct_keys(keys)
    {first_name: first_name, last_name: last_name}
  end
end

case user
in {first_name: &amp;quot;John&amp;quot;}
  puts &amp;quot;Hey, John&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;keys&lt;/code&gt; argument to &lt;code&gt;deconstruct_keys&lt;/code&gt; contains the keys that have been requested in the pattern.
This is a way for the receiver to provide only the required keys if computing all of them is expensive.&lt;/p&gt;
&lt;p&gt;In the same way as &lt;code&gt;deconstruct_keys&lt;/code&gt;, we could provide an implementation of &lt;code&gt;deconstruct&lt;/code&gt; to allow objects to be pattern matched as an array.
For example, let&amp;#39;s say we have a &lt;code&gt;Location&lt;/code&gt; class that has latitude and longitude. In addition to using &lt;code&gt;deconstruct_keys&lt;/code&gt; to provide latitude and longitude keys, we could expose an array in the form of &lt;code&gt;[latitude, longitude]&lt;/code&gt; as well:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Location
  def deconstruct
    [latitude, longitude]
  end
end

case location
in [Float =&amp;gt; latitude, Float =&amp;gt; longitude]
  puts &amp;quot;#{latitude}, #{longitude}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Using Guards for Complex Patterns&lt;/h2&gt;
&lt;p&gt;If we have complex patterns that cannot be represented with regular pattern match operators, we can also use an &lt;code&gt;if&lt;/code&gt; (or &lt;code&gt;unless&lt;/code&gt;) statement to provide a guard for the match:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;case [1, 2]
in [a, b] if b == a * 2
  &amp;quot;matches&amp;quot;
else
  &amp;quot;no match&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pattern Matching with &lt;code&gt;=&amp;gt;&lt;/code&gt;/&lt;code&gt;in&lt;/code&gt; Without &lt;code&gt;case&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If you are on Ruby 3+, you have access to even more pattern matching magic. Starting from Ruby 3, pattern matching can be done in a single line without a case statement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;[1, 2, &amp;quot;Three&amp;quot;] =&amp;gt; [Integer =&amp;gt; one, two, String =&amp;gt; three]
puts one # 1
puts two # 2
puts three # Three

# Same as above
[1, 2, &amp;quot;Three&amp;quot;] in [Integer =&amp;gt; one, two, String =&amp;gt; three]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given that the above syntax does not have an &lt;code&gt;else&lt;/code&gt; clause, it is most useful when the data structure is known beforehand.&lt;/p&gt;
&lt;p&gt;As an example, this pattern could fit well inside a base controller that allows only admin users:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AdminController &amp;lt; AuthenticatedController
  before_action :verify_admin

  private

  def verify_admin
    Current.user =&amp;gt; {role: :admin}
  rescue NoMatchingPatternError
    raise NotAllowedError
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pattern Matching in Ruby: Watch This Space&lt;/h2&gt;
&lt;p&gt;At first, pattern matching can feel a bit strange to grasp.
To some, it might feel like glorified object/array deconstruction.&lt;/p&gt;
&lt;p&gt;But if the popularity of Elixir is any indication, pattern matching is a great tool to have in your arsenal.
Having first-hand experience using it on Elixir, I can confirm that it is hard to live without once you get used to it.&lt;/p&gt;
&lt;p&gt;If you are on Ruby 2.7, pattern matching (with &lt;code&gt;case&lt;/code&gt;/&lt;code&gt;in&lt;/code&gt;) is still experimental. With Ruby 3, &lt;code&gt;case&lt;/code&gt;/&lt;code&gt;in&lt;/code&gt; has moved to stable while the newly introduced single-line pattern matching expressions are experimental.
Warnings can be turned off with &lt;code&gt;Warning[:experimental] = false&lt;/code&gt; in code or &lt;code&gt;-W:no-experimental&lt;/code&gt; command-line key.&lt;/p&gt;
&lt;p&gt;Even though pattern matching in Ruby is still in its early stages, I hope you&amp;#39;ve found this introduction useful and that you&amp;#39;re as excited as I am about future developments to come!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you’d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>General Ruby on Rails Problems and Takeaways</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/07/07/general-ruby-on-rails-problems-and-takeaways.html"/>
    <id>https://blog.appsignal.com/2021/07/07/general-ruby-on-rails-problems-and-takeaways.html</id>
    <published>2021-07-07T00:00:00+00:00</published>
    <updated>2021-07-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this last part of the Rails Patterns and Anti-patterns series, I&#039;ll be going over the most common problems I&#039;ve encountered while building and shipping Ruby on Rails applications.</summary>
    <content type="html">&lt;p&gt;Welcome to the last part of my Ruby on Rails Patterns and Anti-Patterns series. It&amp;#39;s been quite a ride writing and researching all of these topics. In this blog post, we&amp;#39;ll go over the most common problems I&amp;#39;ve encountered when building and shipping Ruby on Rails applications through the years.&lt;/p&gt;
&lt;p&gt;The ideas I&amp;#39;ll go through here apply to almost anywhere in the code. So consider them as general ideas, not something related to the Model-View-Controller pattern. If you are interested in patterns and anti-patterns related to the Rails MVC, you can check out the &lt;a href=&quot;/2020/11/18/rails-model-patterns-and-anti-patterns.html&quot;&gt;Model&lt;/a&gt;, &lt;a href=&quot;/2021/02/10/ruby-on-rails-view-patterns-and-anti-patterns.html&quot;&gt;View&lt;/a&gt;, and &lt;a href=&quot;/2021/04/14/ruby-on-rails-controller-patterns-and-anti-patterns.html&quot;&gt;Controller&lt;/a&gt; blog posts.&lt;/p&gt;
&lt;p&gt;So let&amp;#39;s jump into general problems and takeaways.&lt;/p&gt;
&lt;h2&gt;Selfish Objects and the Law of Demeter&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Law_of_Demeter&quot;&gt;Law of Demeter&lt;/a&gt; is a heuristic that got its name when a group of people worked on the Demeter Project. The idea is that your objects are fine as long as they call one method at a time and don&amp;#39;t chain multiple method calls. What this means in practice is the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Bad
song.label.address

# Good
song.label_address
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now, the &lt;code&gt;song&lt;/code&gt; object no longer needs to know where the address comes from — the address is the responsibility of the &lt;code&gt;label&lt;/code&gt; object. You are encouraged to chain only one method call and make your objects &amp;#39;selfish&amp;#39; so that they don&amp;#39;t share their full information directly but through helper methods.&lt;/p&gt;
&lt;p&gt;Luckily, in Rails, you don&amp;#39;t have to write a helper method per se — you can use the &lt;code&gt;delegate&lt;/code&gt; helper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def Label &amp;lt; ApplicationModel
  belongs_to :song

  delegate :address, to: :song
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can go ahead and play around with the options that delegate accepts in &lt;a href=&quot;https://apidock.com/rails/Module/delegate&quot;&gt;delegete&amp;#39;s docs&lt;/a&gt;. But the idea and execution are pretty simple. By applying the Law of Demeter, you reduce structural coupling. Together with the powerful &lt;code&gt;delegate&lt;/code&gt;, you do it in fewer lines and with great options included.&lt;/p&gt;
&lt;p&gt;Another idea that&amp;#39;s very similar to the Law of Demeter is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Single-responsibility_principle&quot;&gt;Single-responsibility Principle&lt;/a&gt; (or SRP for short). It states that a module, class, or function should be responsible for a single part of a system. Or, presented in another way:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gather together the things that change for the same reasons. Separate those things that change for different reasons.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Folks can often have a different understanding of SRP, but the idea is to keep your building blocks responsible for a single thing. It might be challenging to achieve SRP as your Rails app expands, but be aware of it when refactoring.&lt;/p&gt;
&lt;p&gt;When adding features and increasing the LOC, I&amp;#39;ve found that folks often reach out for a quick solution. So let&amp;#39;s go through grabbing the quick fix.&lt;/p&gt;
&lt;h2&gt;I Know a Guy (Do You Need That Ruby Gem?)&lt;/h2&gt;
&lt;p&gt;Back in the day when Rails was a hot topic, there was a boom in open-source collaboration, with new Ruby gems popping up on every corner (like it is nowadays with all the emerging JavaScript libraries, but on a much smaller scale):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-07/npm-vs-rubygems.png&quot; alt=&quot;NPM vs RubyGems&quot;/&gt;
👆 Information from &lt;a href=&quot;http://www.modulecounts.com/&quot;&gt;Module Counts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, a common approach was to find an existing gem to solve your problem.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s nothing wrong with that, but I&amp;#39;d like to share some bits of advice before you decide to install a gem.&lt;/p&gt;
&lt;p&gt;First, ask yourself these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What portion of the gem&amp;#39;s features are you going to use?&lt;/li&gt;
&lt;li&gt;Is there a similar gem out there that is &amp;#39;simpler&amp;#39; or more up-to-date?&lt;/li&gt;
&lt;li&gt;Can you implement the feature you need easily and with confidence?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Evaluate whether it&amp;#39;s worth doing the implementation if you don&amp;#39;t plan to use the whole array of gem features. Or, if the gem&amp;#39;s implementation is too complex and you believe you can do it more simply, opt for a custom solution.&lt;/p&gt;
&lt;p&gt;Another factor that I consider is how active the gem&amp;#39;s repository is — are there any active maintainers? When was the last time a release happened?&lt;/p&gt;
&lt;p&gt;You should also watch out for the gem&amp;#39;s dependencies. You don&amp;#39;t want to get locked into a specific version of a dependency, so always check the &lt;code&gt;Gemfile.spec&lt;/code&gt; file. Consult the &lt;a href=&quot;https://guides.rubygems.org/patterns/#pessimistic-version-constraint&quot;&gt;RubyGems way of specifying gem versions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While we&amp;#39;re on the topic of gems, there is a related idea that I&amp;#39;ve encountered: the &amp;#39;Not Invented Here&amp;#39; (or NIH) phenomenon that applies to the Rails/Ruby world. Let&amp;#39;s see what it&amp;#39;s about in the next section.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Not Invented Here (Maybe You Need That Ruby Gem after All?)&lt;/h2&gt;
&lt;p&gt;In a couple of occurrences in my career, I had a chance to experience people (me included) fall for &amp;#39;Not Invented Here&amp;#39; syndrome. The idea is similar to &amp;#39;reinventing the wheel&amp;#39;. Sometimes, teams and organizations do not trust libraries (gems) that they can&amp;#39;t control. Lack of trust might be a trigger for them to reinvent a gem that is already out there.&lt;/p&gt;
&lt;p&gt;Sometimes, experiencing NIH can be a good thing. Making an in-house solution can be great, especially if you improve it over the other solutions out there. If you decide to open-source the solution, that can be even better (take a look at Ruby on Rails or React). But if you want to reinvent the wheel for the sake of it, don&amp;#39;t do it. The wheel itself is pretty great already.&lt;/p&gt;
&lt;p&gt;This topic is quite tricky, and if you ever get caught in such a situation, ask yourself these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are we confident that we can make a better solution than existing ones?&lt;/li&gt;
&lt;li&gt;If the existing open-source solution differs from what we need, can we make an open-source contribution and improve it?&lt;/li&gt;
&lt;li&gt;Furthermore, can we become the maintainers of the open-source solution and possibly improve lots of developers&amp;#39; lives?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But sometimes, you just have to go your own way and create a library yourself. Maybe your organization doesn&amp;#39;t like licensing an open-source library, so you are forced to build your own. But whatever you do, I&amp;#39;d say avoid reinventing the wheel.&lt;/p&gt;
&lt;h2&gt;Lifeguard on Duty (Over-rescuing Exceptions)&lt;/h2&gt;
&lt;p&gt;People tend to rescue more exceptions than they originally aimed for.&lt;/p&gt;
&lt;p&gt;This topic is a bit more related to the code than the previous ones. It might be common sense to some, but it can be seen in the code from time to time. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  song.upload_lyrics
rescue
  puts &amp;#39;Lyrics upload failed&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we don&amp;#39;t specify the exception we want to rescue, we will catch some exceptions that we didn&amp;#39;t plan to.&lt;/p&gt;
&lt;p&gt;In this case, the problem might be that the &lt;code&gt;song&lt;/code&gt; object is &lt;code&gt;nil&lt;/code&gt;. When that exception gets reported to the error tracker, you might think that something is off with the upload process, whereas actually, you might be experiencing something totally different.&lt;/p&gt;
&lt;p&gt;So, to be safe, when rescuing exceptions, make sure you get a list of all the exceptions that might occur. If you can&amp;#39;t obtain every exception for some reason, it&amp;#39;s better to under-rescue than to over-rescue. Rescue the exceptions that you know and handle the others at a later stage.&lt;/p&gt;
&lt;h2&gt;You Ask Too Much (Too Many SQL Queries)&lt;/h2&gt;
&lt;p&gt;In this section, we are going to go through another web development, relation-database problem.&lt;/p&gt;
&lt;p&gt;You bomb the webserver with too many SQL queries in one request. How does that problem arise? Well, it can happen if you try to fetch multiple records from multiple tables in one request. But what most often happens is the infamous N+1 query problem.&lt;/p&gt;
&lt;p&gt;Imagine the following models:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Song &amp;lt; ApplicationRecord
  belongs_to :artist
end

class Artist &amp;lt; ApplicationRecord
  has_many :songs
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we want to show a couple of songs in a genre and their artists:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;songs = Song.where(genre: genre).limit(10)

songs.each do |song|
  puts &amp;quot;#{song.title} by #{song.artist.name}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This piece of code will trigger one SQL query to get ten songs. After that, one extra SQL query will be performed to fetch the artist for each song. That&amp;#39;s eleven (11) queries total.&lt;/p&gt;
&lt;p&gt;Imagine the scenario if we load more songs — we&amp;#39;ll put the database under a heavier load trying to get all the artists.&lt;/p&gt;
&lt;p&gt;Alternatively, use &lt;code&gt;includes&lt;/code&gt; from Rails:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;songs = Song.includes(:artists).where(genre: genre).limit(10)

songs.each do |song|
  puts &amp;quot;#{song.title} by #{song.artist.name}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the &lt;code&gt;includes&lt;/code&gt;, we now only get two SQL queries, no matter how many songs we decide to show. How neat.&lt;/p&gt;
&lt;p&gt;One way you can diagnose too many SQL queries is in development. If you see a group of similar SQL queries fetching data from the same table, then something fishy is going on there. That&amp;#39;s why I strongly encourage you to turn on SQL logging for your development environment. Also, Rails supports &lt;a href=&quot;https://guides.rubyonrails.org/debugging_rails_applications.html#verbose-query-logs&quot;&gt;verbose query logs&lt;/a&gt; that show where a query is called from in the code.&lt;/p&gt;
&lt;p&gt;If looking at logs is not your thing, or you want something more serious, try out &lt;a href=&quot;/2020/06/09/n-plus-one-queries-explained.html&quot;&gt;AppSignal&amp;#39;s performance measuring and N+1 query detection&lt;/a&gt;. There, you will get an excellent indicator of whether your issue comes from an N+1 query. Here&amp;#39;s how it looks below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-07/nplus1.png&quot; alt=&quot;AppSignal&amp;#39;s N+1 query detection&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Sum Up&lt;/h2&gt;
&lt;p&gt;Thanks for reading this blog post series. I&amp;#39;m glad you joined me for this interesting ride, where we went from introducing patterns and anti-patterns in Rails to exploring what they are inside the Rails MVC pattern, before this final blog post on general problems.&lt;/p&gt;
&lt;p&gt;I hope you learned a lot, or at least revised and established what you already know. Do not stress about memorizing all of it. You can always consult the series if you are having trouble in any area.&lt;/p&gt;
&lt;p&gt;You will surely encounter both patterns and anti-patterns because this world (and software engineering especially) is not ideal. That shouldn&amp;#39;t worry you either.&lt;/p&gt;
&lt;p&gt;Mastering patterns and anti-patterns will make you a great software engineer. But what makes you even better is knowing when to break those patterns and molds, because there is no perfect solution.&lt;/p&gt;
&lt;p&gt;Thanks again for joining and reading. See you in the next one — and cheers!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Performance, Stress, and Load Tests in Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/06/09/performance-stress-and-load-tests-in-rails.html"/>
    <id>https://blog.appsignal.com/2021/06/09/performance-stress-and-load-tests-in-rails.html</id>
    <published>2021-06-09T00:00:00+00:00</published>
    <updated>2021-06-09T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s jump into theoretical and practical aspects of performance testing in Rails.</summary>
    <content type="html">&lt;p&gt;Tests are an integral part of most well-working Rails applications where maintenance isn&amp;#39;t a nightmare and new features are consistently added, or existing ones are improved. Unfortunately, for many applications, a production environment is where they are put under heavy workload or significant traffic for the first time. This is understandable as such tests are costly.&lt;/p&gt;
&lt;p&gt;Thankfully, Rails has good support not only for unit, end-to-end, and integration tests but also for tests related to performance and loading. I’ll cover all of them in the article and show some practical examples that will help you understand how to efficiently use tools that test the performance level of your application.&lt;/p&gt;
&lt;p&gt;The article is divided into two sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Theoretical&lt;/strong&gt; — I&amp;#39;ll show you why testing is necessary, the kinds of tests we can perform and the metrics that are essential when performing tests on an application&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical&lt;/strong&gt; — we&amp;#39;ll get our hands dirty and write tests for an actual application to get the output&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After reading the two sections, you&amp;#39;ll have a deeper understanding of the different types of tests and how to perform them on your Rails application. Sounds interesting? Then let&amp;#39;s get started with a pinch of theory about tests.&lt;/p&gt;
&lt;h2&gt;Testing in Theory&lt;/h2&gt;
&lt;p&gt;Testing should always be an inherent part of the development of any type of application. If you are still not convinced about that or haven’t written any tests yet, here are some arguments for testing that will help you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Introduce changes without worrying about breaking something&lt;/strong&gt; — this is the major reason why tests are necessary. Imagine working on a huge app where you have to click through the whole app to make sure nothing is broken each time you introduce some change, even a small one. With tests, you just execute one command and the verification process is automatic and fast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy refactoring process&lt;/strong&gt; — I mentioned above that tests are essential when adding new features or making changes. With testing in place, you are also more comfortable with improving your existing code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests are a form of documentation&lt;/strong&gt; — well-written tests can be a form of documentation for various sets of features in the application. They not only describe what the feature is but also how it should be working.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Opportunity to rethink the implementation&lt;/strong&gt; — when you write a test, you have a chance to think again if the way you want to implement the code is correct and reasonable. Also, you simply check if your code is working the way you expect it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope the above arguments convinced you to use tests during the development of any app. While knowing why to test the code is essential, it’s also crucial to learn about different types of tests.&lt;/p&gt;
&lt;h3&gt;Different Types of Tests&lt;/h3&gt;
&lt;p&gt;There are three primary types of tests that you can write to ensure that your Rails application’s performance is correct and the infrastructure is working well under the heavy workload. Those types are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Load testing&lt;/strong&gt; — this type of test answers the following question: how many simultaneous users can the system handle for a given period. Imagine that you launch a top-rated product on your website and thousands of users want to make the order at the same time. Without proper loading tests, you risk a crash during the most critical time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stress testing&lt;/strong&gt; — with this type of test, you don’t focus on verifying the number of users the system can handle simultaneously, but on how the system will behave when the limit of the users is hit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance testing&lt;/strong&gt; — I would say that this type of test is a parent of stress and load testing. The primary purpose of such tests is to get a specific set of metrics on which base we can take some action to improve the application’s code. I will talk about those metrics in a while.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That being said, we are now prepared to move to the last step of the theory part: learning what metrics are essential when doing performance testing on a Rails application. Without that knowledge, we won’t correctly interpret the test output and decide if we should change the code or not.&lt;/p&gt;
&lt;h2&gt;Important Metrics&lt;/h2&gt;
&lt;p&gt;The type of metrics you can receive can be different depending on the tool you use for testing, but generally, we can group them into a set of metrics that are pretty common:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Response time&lt;/strong&gt; — the time between the request being made and the response getting rendered in the browser. This metric shows us how long the user needs to wait before receiving the information he requested. It’s sometimes called process time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory usage&lt;/strong&gt; — the amount of memory consumed for the given request. This is a piece of essential information as it points you to the place where you can improve the code so the system can respond faster and use fewer resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Objects allocation&lt;/strong&gt; — a high memory allocation causes high memory usage and long response times. This metric can lead you to the exact place in code where many objects are allocated, so you can immediately inspect that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can have more metrics when testing, but those three are the most important and will be valid for any application that you test. We can now get our hands dirty and write real tests.&lt;/p&gt;
&lt;h2&gt;Practice&lt;/h2&gt;
&lt;p&gt;We aren&amp;#39;t able to write tests without having something to test. That’s why the first step in the practice part is to write a simple Rails application that we can write the tests for.&lt;/p&gt;
&lt;h3&gt;Sample Rails Application&lt;/h3&gt;
&lt;p&gt;I will use Ruby 3.0.1 and Rails 6.1.3.1 but feel free to use any version you are comfortable with. If you have Ruby and Rails installed, the next step is to create the application’s skeleton:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails new simpleapp -d=postgresql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the article&amp;#39;s purpose, I&amp;#39;ll create an app where a list of users is presented along with their pet’s names. Such a structure will allow us to easily create the N+1 queries that will offer more fun when doing performance tests and checking the impact on speed and other metrics that changes will have.&lt;/p&gt;
&lt;p&gt;Before we generate the models, let’s create the database first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;cd simpleapp/
bin/rails db:create
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can generate the models:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g model User name:string
rails g model Animal name:string user:references
bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Just one small update to the &lt;code&gt;User&lt;/code&gt; model to reflect the relationship with the &lt;code&gt;Animal&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  has_many :animals
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now add some seeds in &lt;code&gt;db/seeds.rb&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;people = {
  &amp;#39;Tim&amp;#39; =&amp;gt; [&amp;#39;Pinky&amp;#39;, &amp;#39;Rick&amp;#39;],
  &amp;#39;Martha&amp;#39; =&amp;gt; [&amp;#39;Rudolph&amp;#39;],
  &amp;#39;Mark&amp;#39; =&amp;gt; [&amp;#39;Niki&amp;#39;, &amp;#39;Miki&amp;#39;, &amp;#39;Bella&amp;#39;],
  &amp;#39;Tina&amp;#39; =&amp;gt; [&amp;#39;Tom&amp;#39;, &amp;#39;Luna&amp;#39;]
}

people.each_pair do |name, pets|
  user = User.create(name: name)
  pets.each do |pet_name|
    user.animals.create(name: pet_name)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and load the data into the database:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bin/rails db:seed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&amp;#39;ll create one controller with the users’ assignment, and then in view, I&amp;#39;ll list all users with their pets’ names. I’m intentionally using code that is causing performance problems so you can measure the improvements later.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;touch app/controllers/home_controller.rb
mkdir app/views/home
touch app/views/home/index.html.erb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The controller is simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class HomeController &amp;lt; ApplicationController
  def index
    @users = User.all
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and the view also:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;h1&amp;gt;List&amp;lt;/h1&amp;gt;

&amp;lt;ul&amp;gt;
  &amp;lt;% @users.each do |user| %&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;%= user.name %&amp;gt; (&amp;lt;%= user.animals.count %&amp;gt;)
      &amp;lt;ul&amp;gt;
        &amp;lt;% user.animals.each do |animal| %&amp;gt;
          &amp;lt;li&amp;gt;&amp;lt;%= animal.name %&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;% end %&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/li&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last step is to update the &lt;code&gt;config/routes.rb&lt;/code&gt; file to let Rails know what we would like to see when visiting the main URL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.routes.draw do
  root to: &amp;#39;home#index&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Load Tests With JMeter&lt;/h3&gt;
&lt;p&gt;JMeter is an open-source software created by the Apache software foundation, designed to load test functional behavior. Since it’s a program created with Java, you can install it on any operating system. You can download the files here: &lt;a href=&quot;https://jmeter.apache.org/download_jmeter.cgi&quot;&gt;https://jmeter.apache.org/download_jmeter.cgi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you are working on a macOS system, you can easily install JMeter with Homebrew:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;brew install jmeter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After installation, you can run the program with the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;jmeter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Configuring the test&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The configuration process consists of the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding the thread group — specifying the number of users and how long each will visit your website&lt;/li&gt;
&lt;li&gt;Configuring HTTP request — specifying the endpoint that JMeter should hit&lt;/li&gt;
&lt;li&gt;Setting the metrics we are interested in&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s walk step-by-step through a simple test configuration to simulate a single user request to the main page of the simple app we created before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Add thread group&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Select the Add -&amp;gt; Threads (Users) -&amp;gt; Thread Group from the menu that expands after you right-click on the “Test Plan”:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-06/images/1.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Specify the number of users and additional attributes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-06/images/2.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Configure HTTP request&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Right-click on the thread we created in the previous step and select Add -&amp;gt; Sampler -&amp;gt; HTTP Request:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-06/images/3.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Configure the protocol, server name, port, and the path of the request:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-06/images/4.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Specify the result view&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Right-click on the HTTP request and select Add -&amp;gt; Listener -&amp;gt; View Results Tree:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-06/images/5.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Running the Test&lt;/h3&gt;
&lt;p&gt;The test is now configured, and we can trigger it. To do this, simply click on the green play button:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-06/images/6.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the application passed the test, but it was just a single request, so the result was obvious. You can now experiment with the number of users and other configuration options to see how the application will behave. From my tests, the simple app started to crash when around 200 users started accessing it simultaneously.&lt;/p&gt;
&lt;h3&gt;Next Steps&lt;/h3&gt;
&lt;p&gt;After performing the load test, you&amp;#39;ll know the pain points of your application. Understanding the user limit, you can now perform the stress test to see how the application will behave.&lt;/p&gt;
&lt;h2&gt;Performance Tests With Ruby-prof&lt;/h2&gt;
&lt;p&gt;The performance test feature was built-in in Rails until version 3, and then it was extracted to the separate gem &lt;a href=&quot;https://github.com/rails/rails-perftest&quot;&gt;https://github.com/rails/rails-perftest&lt;/a&gt;. Since I had some problems using it with the latest version of Rails, I decided not to include it in this article. Instead, I will use the ruby-prof library that works very well.&lt;/p&gt;
&lt;p&gt;As usual, the first step is to add a gem to our application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;bundle add ruby-prof
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second and the last step of the configuration process is to update the &lt;code&gt;config/application.rb&lt;/code&gt; and use the middleware for the gem so the library can automatically inspect our requests and produce reports based on them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Simpleapp
  class Application &amp;lt; Rails::Application
    config.middleware.use Rack::RubyProf, :path =&amp;gt; &amp;#39;./tmp/profile&amp;#39;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now access the app, and each time you perform a request, the gem will generate a new report. It looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-06/images/7.png&quot; alt=&quot;alt text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;You can find it under the configured path, which is &lt;code&gt;tmp/profile&lt;/code&gt; in our case. The second report is also generated, and it shows the call stack, which is also a pretty helpful metric when debugging performance issues in a Rails application.&lt;/p&gt;
&lt;p&gt;It’s important to remember that setting the &lt;code&gt;cache_classes&lt;/code&gt; and &lt;code&gt;cache_template_loading&lt;/code&gt; settings to &lt;code&gt;true&lt;/code&gt; will slow down the application and overwhelm the application metrics as Rails will try to load the required files.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Testing is an essential part of every development process. Checking if the code behaves as we want it to is as crucial as verifying if our solutions have good performance. Skipping tests leads to serious problems that impact the app’s performance and your users’ trust. Hopefully, testing is not that hard.&lt;/p&gt;
&lt;p&gt;In this article, we covered the following important aspects of testing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the reason you should test your code&lt;/li&gt;
&lt;li&gt;the different types of performance tests&lt;/li&gt;
&lt;li&gt;the way you can test the performance of your Rails app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope that you are now more convinced why it is important to write tests since you know why and how.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re interested in monitoring your app’s performance not just locally but also in the production or staging environments, you should also &lt;a href=&quot;https://www.appsignal.com/&quot;&gt;check out AppSignal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Three Ways To Avoid Duplicate Sidekiq Jobs</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/05/12/three-ways-to-avoid-duplicate-sidekiq-jobs.html"/>
    <id>https://blog.appsignal.com/2021/05/12/three-ways-to-avoid-duplicate-sidekiq-jobs.html</id>
    <published>2021-05-12T00:00:00+00:00</published>
    <updated>2021-05-12T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Sidekiq is used to handle background processing. Whatever your background jobs may be, you&#039;ll eventually run into duplicate jobs. Let&#039;s see how to de-duplicate them.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;This post was updated on 4 August 2023 to reference the latest pricing for Sidekiq Enterprise. Some links were also updated.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Chances are, if you are writing Ruby code, you are using
Sidekiq to handle background processing. If you are coming from &lt;code&gt;ActiveJob&lt;/code&gt;
or some other background, stay tuned, some of the tips covered can be applied there as
well.&lt;/p&gt;
&lt;p&gt;Folks utilize (Sidekiq) background jobs for different cases. Some crunch
numbers, some dispatch welcome emails to users, and some schedule data
syncing. Whatever your case may be, you might eventually run into a requirement to
avoid duplicate jobs. By duplicate jobs, I envision two jobs that do the exact
same thing. Let&amp;#39;s dive in on that a bit.&lt;/p&gt;
&lt;h2&gt;Why De-Duplicate Jobs?&lt;/h2&gt;
&lt;p&gt;Imagine a scenario where your job looks like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class BookSalesWorker
  include Sidekiq::Worker

  def perform(book_id)
    crunch_some_numbers(book_id)

    upload_to_s3
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;BookSalesWorker&lt;/code&gt; always does the same thing — queries the DB for a book
based on the &lt;code&gt;book_id&lt;/code&gt; and fetches the latest sales data to calculate some
numbers. Then, it uploads them to a storage service. Keep in mind that every time a
book is sold on your website, you will have this job enqueued.&lt;/p&gt;
&lt;p&gt;Now, what if you got 100 sales at once? You&amp;#39;d have 100 of these jobs doing
the exact same thing. Maybe you are fine with that. You don&amp;#39;t care about S3
writes that much, and your queues aren&amp;#39;t as congested, so you can handle the
load. But, &amp;quot;does it scale?&amp;quot;™️&lt;/p&gt;
&lt;p&gt;Well, definitely not. If you start receiving more sales for more
books, your queue will quickly pile up with unnecessary work. If you have 100
jobs that do the same thing for a single book, and you have 10 books selling in
parallel, you are now 1000 jobs deep in your queue, where in reality, you could
just have 10 jobs for each book.&lt;/p&gt;
&lt;p&gt;Now, let&amp;#39;s go through a couple of options on how you can prevent duplicate jobs
from piling up your queues.&lt;/p&gt;
&lt;h2&gt;1. DIY Way&lt;/h2&gt;
&lt;p&gt;If you are not a fan of external dependencies and complex logic, you can go
ahead and add some custom solutions to your codebase. I created a
sample repo to try out our examples first-hand. There will be a link in each
approach to the example.&lt;/p&gt;
&lt;h3&gt;1.1 One Flag Approach&lt;/h3&gt;
&lt;p&gt;You can add one flag that decides whether to enqueue a job or not.
One might add a &lt;code&gt;sales_enqueued_at&lt;/code&gt; in their Book table and maintain that
one. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module BookSalesService
  def schedule_with_one_flag(book)
    # Check if the job was enqueued more than 10 minutes ago
    if book.sales_enqueued_at &amp;lt; 10.minutes.ago
      book.update(sales_enqueued_at: Time.current)

      BookSalesWorker.perform_async(book.id)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means that no new jobs will be enqueued until 10 minutes have passed from
the time the last job got enqueued. After 10 minutes have passed, we then update the
&lt;code&gt;sales_enqueued_at&lt;/code&gt; and enqueue a new job.&lt;/p&gt;
&lt;p&gt;Another thing you can do is set one flag that is a boolean, e.g.
&lt;code&gt;crunching_sales&lt;/code&gt;. You set &lt;code&gt;crunching_sales&lt;/code&gt; to true before the first job is
enqueued. Then, you set it to false once the job is complete. All other jobs
that try to get scheduled will be rejected until &lt;code&gt;crunching_sales&lt;/code&gt; is false.&lt;/p&gt;
&lt;p&gt;You can try this approach
&lt;a href=&quot;https://github.com/nikolalsvk/duplicate-sidekiq-jobs#1-one-flag-approach&quot;&gt;in the example repo&lt;/a&gt;
I created.&lt;/p&gt;
&lt;h3&gt;1.2 Two Flags Approach&lt;/h3&gt;
&lt;p&gt;If &amp;quot;locking&amp;quot; a job from being enqueued for 10 minutes sounds too scary, but you are
still fine with extra flags in your code, then the next suggestion might interest you.&lt;/p&gt;
&lt;p&gt;You can add another flag to the existing &lt;code&gt;sales_enqueued_at&lt;/code&gt; — the &lt;code&gt;sales_calculated_at&lt;/code&gt;.
Then our code will look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module BookSalesService
  def schedule_with_two_flags(book)
    # Check if sales are being calculated right now
    if book.sales_enqueued_at &amp;lt;= book.sales_calculated_at
      book.update(sales_enqueued_at: Time.current)

      BookSalesWorker.perform_async(book.id)
    end
  end
end

class BookSalesWorker
  include Sidekiq::Worker

  def perform(book_id)
    crunch_some_numbers(book_id)

    upload_to_s3

    # New adition
    book.update(sales_calculated_at: Time.current)
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;To try it out, check out the &lt;a href=&quot;https://github.com/nikolalsvk/duplicate-sidekiq-jobs#2-two-flag-approach&quot;&gt;instructions in the example repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now we control a portion of the time between when a job is enqueued and finished. In that
portion of time, no job can be enqueued. While the job is running, the
&lt;code&gt;sales_enqueued_at&lt;/code&gt; will be larger than &lt;code&gt;sales_calculated_at&lt;/code&gt;. When the job
finishes running, the &lt;code&gt;sales_calculated_at&lt;/code&gt; will be larger (more recent) than
the &lt;code&gt;sales_enqueued_at&lt;/code&gt; and a new job will get enqueued.&lt;/p&gt;
&lt;p&gt;Using two flags might be interesting, so you could show the last time
those sales numbers got updated in the UI. Then the users that read them
can have an idea of how recent the data is. A win-win situation.&lt;/p&gt;
&lt;h3&gt;Flag Sum Up&lt;/h3&gt;
&lt;p&gt;It might be tempting to create solutions like these in times of need, but to me,
they look a bit clumsy, and they add some overhead. I would recommend using
this if your use case is simple, but as soon as it proves complex or not
enough, I&amp;#39;d urge you to try out other options.&lt;/p&gt;
&lt;p&gt;A huge con with the flag approach is that you will lose all the jobs
that tried to enqueue during those 10 minutes. A huge pro is that you are not
bringing in dependencies, and it will alleviate the job number in queues
pretty quickly.&lt;/p&gt;
&lt;h3&gt;1.3 Traversing The Queue&lt;/h3&gt;
&lt;p&gt;Another approach you can take is to create a custom locking mechanism to
prevent the same jobs from enqueueing. We will check the Sidekiq queue we
are interested in and see if the job (worker) is already there. The code
will look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module BookSalesService
  def schedule_unique_across_queue(book)
    queue = Sidekiq::Queue.new(&amp;#39;default&amp;#39;)

    queue.each do |job|
      return if job.klass == BookSalesWorker.to_s &amp;amp;&amp;amp;
        job.args == [book.id]
    end

    BookSalesWorker.perform_async(book.id)
  end
end

class BookSalesWorker
  include Sidekiq::Worker

  def perform(book_id)
    crunch_some_numbers(book_id)

    upload_to_s3
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above we are checking whether the
&lt;code&gt;&amp;#39;default&amp;#39;&lt;/code&gt; queue has a job with the class name of &lt;code&gt;BookSalesWorker&lt;/code&gt;. We are also
checking if the job arguments match the book ID. If the &lt;code&gt;BookSalesWorker&lt;/code&gt; job
with the same Book ID is in the queue, we will return early and not schedule
another one.&lt;/p&gt;
&lt;p&gt;Note that some of them might get scheduled if you schedule jobs too fast
because the queue is empty. The exact thing happened to me when testing it
locally with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;100.times { BookSalesService.schedule_unique_across_queue(book) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can try it out in the &lt;a href=&quot;https://github.com/nikolalsvk/duplicate-sidekiq-jobs#3-traverse-sidekiq-queue-approach&quot;&gt;example repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The good thing about this approach is that you can traverse all queues to search
for an existing job if you need it. The con is that you can still have
duplicate jobs if your queue is empty and you schedule a lot of them at once.
Also, you are potentially traversing through all the jobs in the queue before
scheduling one, so that might be costly depending on the size of your queue.&lt;/p&gt;
&lt;h2&gt;2. Upgrading to Sidekiq Enterprise&lt;/h2&gt;
&lt;p&gt;If you or your organization has some money lying around, you can upgrade to
the Enterprise version of Sidekiq. It starts at $229 per month, and it has a cool
feature that helps you avoid duplicate jobs. Unfortunately, I don&amp;#39;t have Sidekiq
Enterprise, but I believe &lt;a href=&quot;https://github.com/mperham/sidekiq/wiki#sidekiq-enterprise&quot;&gt;their documentation&lt;/a&gt;
is sufficient.
You can easily have unique (non-duplicated) jobs with the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class BookSalesWorker
  include Sidekiq::Worker
  sidekiq_options unique_for: 10.minutes

  def perform(book_id)
    crunch_some_numbers(book_id)

    upload_to_s3
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&amp;#39;s it. You have a similar job implementation to what we described in
the &amp;#39;One Flag Approach&amp;#39; section. The job will be unique for 10 minutes, meaning that
no other job with the same arguments can be scheduled in that time period.&lt;/p&gt;
&lt;p&gt;Pretty cool one-liner, huh? Well, if you have Enterprise Sidekiq and you just
found out about this feature, I am truly glad I helped. Most of us are
not going to use it, so let&amp;#39;s jump into the next solution.&lt;/p&gt;
&lt;h2&gt;3. sidekiq-unique-jobs To The Rescue&lt;/h2&gt;
&lt;p&gt;Yes, I know we are about to mention a gem. And yes, it has some Lua files in it
which might put some people off. But bear with me, it&amp;#39;s a really sweet deal you
are getting with it. The &lt;a href=&quot;https://github.com/mhenrixon/sidekiq-unique-jobs&quot;&gt;sidekiq-unique-job&lt;/a&gt;
gem comes with a lot of locking and other configuration options — probably more
than you need.&lt;/p&gt;
&lt;p&gt;To get started quickly, put &lt;code&gt;sidekiq-unique-jobs&lt;/code&gt; gem into your Gemfile, do
&lt;code&gt;bundle&lt;/code&gt; and configure your worker as shown:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class UniqueBookSalesWorker
  include Sidekiq::Worker

  sidekiq_options lock: :until_executed,
                  on_conflict: :reject

  def perform(book_id)
    book = Book.find(book_id)

    logger.info &amp;quot;I am a Sidekiq Book Sales worker - I started&amp;quot;
    sleep 2
    logger.info &amp;quot;I am a Sidekiq Book Sales worker - I finished&amp;quot;

    book.update(sales_calculated_at: Time.current)
    book.update(crunching_sales: false)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a lot of options, but I decided to simplify and use this one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;sidekiq_options lock: :until_executed, on_conflict: :reject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;lock: :until_executed&lt;/code&gt; will lock the first &lt;code&gt;UniqueBookSalesWorker&lt;/code&gt; job
until it is executed. With &lt;code&gt;on_conflict: :reject&lt;/code&gt;, we are saying that we want
all other jobs that try to get executed to be rejected into the dead queue. What we
achieved here is similar to what we did in our DIY examples in the topics above.&lt;/p&gt;
&lt;p&gt;A slight improvement over those DIY examples is that we have a kind of log
of what happened. To get a sense of how it looks, let&amp;#39;s try the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;5.times { UniqueBookSalesWorker.perform_async(Book.last.id) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only one job will fully execute, and the other four jobs will be sent off to the
dead queue, where you can retry them. This approach differs from our examples where
duplicate jobs were just ignored.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-05/dead-queue.png&quot; alt=&quot;Dead queue&quot;/&gt;&lt;/p&gt;
&lt;p&gt;There are a lot of options to choose from when it comes to locking and conflict resolving.
I suggest you consult the &lt;a href=&quot;https://github.com/mhenrixon/sidekiq-unique-jobs&quot;&gt;gem&amp;#39;s documentation&lt;/a&gt;
for your specific use case.&lt;/p&gt;
&lt;h3&gt;Great Insights&lt;/h3&gt;
&lt;p&gt;The great thing about this gem is that you can view the locks and the history of what went down in your queues.
All you need to do is add the following lines in your &lt;code&gt;config/routes.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/routes.rb
require &amp;#39;sidekiq_unique_jobs/web&amp;#39;

Rails.application.routes.draw do
  mount Sidekiq::Web, at: &amp;#39;/sidekiq&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will include the original Sidekiq client, but it will also give you two more
pages — one for job locks and the other for the changelog. This is how it looks:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-05/new-pages.png&quot; alt=&quot;New Sidekiq Client pages&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Notice how we have two new pages, &amp;quot;Locks&amp;quot; and &amp;quot;Changelogs&amp;quot;. Pretty cool feature.&lt;/p&gt;
&lt;p&gt;You can try all of this in &lt;a href=&quot;https://github.com/nikolalsvk/duplicate-sidekiq-jobs#4-using-sidekiq-unique-jobs-gem&quot;&gt;the example
project&lt;/a&gt;
where the gem is installed and ready to go.&lt;/p&gt;
&lt;h3&gt;Why Lua?&lt;/h3&gt;
&lt;p&gt;First of all, I am not the author of the gem, so I&amp;#39;m just assuming things
here. The first time I saw the gem, I wondered: why use Lua inside a Ruby gem? It
might appear odd at first, but Redis supports running Lua scripts. I guess the
author of the gem had this in mind and wanted to do more nimble logic in Lua.&lt;/p&gt;
&lt;p&gt;If you look at the
&lt;a href=&quot;https://github.com/mhenrixon/sidekiq-unique-jobs/tree/main/lib/sidekiq_unique_jobs/lua&quot;&gt;Lua files in the gem&amp;#39;s repo&lt;/a&gt;,
they aren&amp;#39;t that complicated. All of the Lua scripts are called later from
the Ruby code in the
&lt;a href=&quot;https://github.com/mhenrixon/sidekiq-unique-jobs/blob/main/lib/sidekiq_unique_jobs/script/caller.rb&quot;&gt;&lt;code&gt;SidekiqUniqueJobs::Script::Caller&lt;/code&gt; here&lt;/a&gt;.
Please take a look at the source code, it is interesting to read and figure out how things work.&lt;/p&gt;
&lt;h3&gt;Alternative Gem&lt;/h3&gt;
&lt;p&gt;If you use &lt;code&gt;ActiveJob&lt;/code&gt; extensively, you can try the &lt;code&gt;active-job-uniqueness&lt;/code&gt; gem &lt;a href=&quot;https://github.com/veeqo/activejob-uniqueness&quot;&gt;right here&lt;/a&gt;.
The idea is similar, but instead of custom Lua scripts, it uses &lt;a href=&quot;https://github.com/leandromoreira/redlock-rb&quot;&gt;Redlock&lt;/a&gt; to
lock items in Redis.&lt;/p&gt;
&lt;p&gt;To have a unique job using this gem, you can imagine a job like this one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class BookSalesJob &amp;lt; ActiveJob::Base
  unique :until_executed

  def perform
    ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The syntax is less verbose but very similar to the &lt;code&gt;sidekiq-unique-jobs&lt;/code&gt;
gem. It might solve your case if you highly rely on &lt;code&gt;ActiveJob&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;I hope you gained some knowledge in how to deal with duplicate jobs in your
app. I definitely had fun researching and playing around with different
solutions. If you didn&amp;#39;t end up finding what you were looking for, I hope that some of
the examples inspired you to create something of your own.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the &lt;a href=&quot;https://github.com/nikolalsvk/duplicate-sidekiq-jobs&quot;&gt;example project&lt;/a&gt; with all the code snippets.&lt;/p&gt;
&lt;p&gt;I will see you in the next one, cheers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Linting Ruby Code</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/04/28/ruby-linting.html"/>
    <id>https://blog.appsignal.com/2021/04/28/ruby-linting.html</id>
    <published>2021-04-28T00:00:00+00:00</published>
    <updated>2021-04-28T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s take a look at what linting is and a few problems that come with it.</summary>
    <content type="html">&lt;p&gt;Linting is the process of statically analyzing code in search of potential problems.&lt;/p&gt;
&lt;p&gt;What constitutes a problem, in this case, can vary across programming languages, or even
across projects within the same language.
I would put these problems under a few different categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Programmatic&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Stylistic&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s take a look at a few examples of each.&lt;/p&gt;
&lt;h2&gt;Stylistic Problems&lt;/h2&gt;
&lt;p&gt;There&amp;#39;s no objectively right way of styling code, as it&amp;#39;s all about the
reader&amp;#39;s preference. The key is consistency though. Common points of debate
include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;double-quotes vs single-quotes&lt;/li&gt;
&lt;li&gt;tabs vs space&lt;/li&gt;
&lt;li&gt;maximum line length&lt;/li&gt;
&lt;li&gt;indentation of multiline calls, as shown below:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# always single-line
foo(:a, :b, :c)

# aligned with first argument
foo(:a,
    :b,
    :c
)

# aligned with function name
foo(
  :a,
  :b
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are entirely subjective, but it&amp;#39;s usually beneficial to agree on a standard
for each particular project, to keep the entire codebase consistent.&lt;/p&gt;
&lt;h2&gt;Programmatic Problems&lt;/h2&gt;
&lt;p&gt;I include here such problems as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extremely long method bodies, which leads to readability &amp;amp; maintainability
issues&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Cyclomatic_complexity&quot;&gt;Cyclomatic Complexity&lt;/a&gt;,
a metric commonly used to measure code complexity&lt;/li&gt;
&lt;li&gt;Assignments within conditions. Most likely, if you type &lt;code&gt;if x = true&lt;/code&gt;, you
actually meant &lt;code&gt;if x == true&lt;/code&gt;. and even if you did mean an assignment, it&amp;#39;s
still a less-intuitive approach&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Security Problems&lt;/h2&gt;
&lt;p&gt;Some functions or practices have potential security problems that developers may
not be aware of.&lt;/p&gt;
&lt;p&gt;For instance, in Ruby, &lt;code&gt;Kernel#open&lt;/code&gt; is a flexible function
that allows opening files or external URLs. But it also allows arbitrary
filesystem access, with weird calls such as &lt;code&gt;open(&amp;quot;| ls&amp;quot;)&lt;/code&gt;.
Therefore, it is sensible to warn developers about it so that they can either use
a safer approach (&lt;code&gt;File#open&lt;/code&gt;, &lt;code&gt;IO.popen&lt;/code&gt;, &lt;code&gt;URI.parse#open&lt;/code&gt;), or explicitly
decide to keep the behavior at their own risk.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Performance Problem&lt;/h2&gt;
&lt;p&gt;There are many details about Ruby&amp;#39;s inner workings that make some options more
performant than others, depending on the context.&lt;/p&gt;
&lt;p&gt;A linter warning us about them helps us learn along the way while optimizing
some details of our program.&lt;/p&gt;
&lt;p&gt;For example, Ruby 2.5 introduced &lt;code&gt;String#delete_suffix&lt;/code&gt; which deletes
a substring from the end of a string. These two lines are equivalent, but the
latter one is more performant since it doesn&amp;#39;t rely on a generic string regex
match:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;str = &amp;#39;string_with_suffix&amp;#39;

# bad
str.gsub(/suffix\z/, &amp;#39;&amp;#39;)

# good
str.delete_suffix(&amp;#39;suffix&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Auto Fixing&lt;/h2&gt;
&lt;p&gt;An important aspect of linters is their ability to automatically fix some or all
of the found issues.
Styling aspects such as line length are easily automated, so it makes sense to
remove that burden from the developer.
Other issues might be subjective or require human intervention, such as
refactoring a large method. In these cases, no automation is possible.&lt;/p&gt;
&lt;h2&gt;Convention or Configuration&lt;/h2&gt;
&lt;p&gt;There is often heavy debate within a community or project on which rules make
sense.&lt;/p&gt;
&lt;p&gt;The traditional solution is to allow each team to solve the debate within
its members by allowing them to configure linting rules to their own taste.
However, in recent years, there has been a push across several languages to
standardize to a single convention.
While this isn&amp;#39;t enforced everywhere, the general idea is to entirely remove the
mental overhead developers have when styling code. Instead of discussing which
line length works best, everyone just uses the community-agreed rules.&lt;/p&gt;
&lt;p&gt;In Ruby, this more or less translates to the two existing linters:
&lt;a href=&quot;https://github.com/rubocop/rubocop&quot;&gt;RuboCop&lt;/a&gt;, which allows full-configuration
and &lt;a href=&quot;https://github.com/testdouble/standard&quot;&gt;StandardRB&lt;/a&gt;, which takes the
opposite approach and defines a common standard.&lt;/p&gt;
&lt;h2&gt;RuboCop&lt;/h2&gt;
&lt;p&gt;Employes the usual approach of providing a documented set of rules, each of
which looks for a particular problem. Developers are able to disable or tweak
certain rules within their own projects:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# configure max allowed line length
Layout/LineLength:
  Max: 80

# disable cyclomatic complexity
Metrics/CyclomaticComplexity:
  Enabled: false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It already includes sane defaults, so configuration is only needed for the
specific rules you want to change.&lt;/p&gt;
&lt;p&gt;Running &lt;code&gt;bundle exec rubocop&lt;/code&gt; will make RuboCop analyze the entire codebase
and list all the issues it found:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# test.rb
def badName
  if something
    return &amp;quot;inner result&amp;quot;
  end

  &amp;quot;outer result&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ bundle exec rubocop
Inspecting 1 file
C

Offenses:

test.rb:1:1: C: [Correctable] Style/FrozenStringLiteralComment: Missing frozen string literal comment.
def badName
^
test.rb:1:5: C: Naming/MethodName: Use snake_case for method names.
def badName
    ^^^^^^^
test.rb:2:3: C: [Correctable] Style/IfUnlessModifier: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &amp;amp;&amp;amp;/||.
  if something
  ^^
test.rb:3:12: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don&amp;#39;t need string interpolation or special symbols.
    return &amp;quot;inner result&amp;quot;
           ^^^^^^^^^^^^^^
test.rb:6:3: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don&amp;#39;t need string interpolation or special symbols.
  &amp;quot;outer result&amp;quot;
  ^^^^^^^^^^^^^^

1 file inspected, 5 offenses detected, 4 offenses auto-correctable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can then run &lt;code&gt;bundle exec rubocop --auto-correct&lt;/code&gt;, and a large majority of
your issues will be fixed according to your configurations.&lt;/p&gt;
&lt;p&gt;Setting up &lt;code&gt;bundle exec rubocop&lt;/code&gt; as part of your CI pipeline will ensure no code
gets through without first fulfilling the linting rules.&lt;/p&gt;
&lt;h2&gt;StandardRB&lt;/h2&gt;
&lt;p&gt;A more recent project, which actually uses RuboCop under the hood.
The main goal of StandardRB is not to build an entirely separate linter, but to
reach a standard that everyone can just use instead of arguing about.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.youtube.com/watch?v=uLyV5hOqGQ8&quot;&gt;lightning talk&lt;/a&gt; where it was first announced is pretty clear about the
motivations: If people spend less time arguing about syntactic details and just
follow the decisions of a community-wide agreement, we can all spend more time
doing what really matters: building great products and libraries.&lt;/p&gt;
&lt;p&gt;Since RuboCop is used underneath, the output you get is actually in the same
format.
The only difference is that you&amp;#39;re not allowed to customize any of the rules.&lt;/p&gt;
&lt;p&gt;StandardRB recently reached 1.0.0, which means most of the discussion about
which rules to use has already happened on their &lt;a href=&quot;https://github.com/testdouble/standard/issues?q=is%3Aissue+is%3Aclosed&quot;&gt;issues page&lt;/a&gt;.
If you care or disagree about a particular rule, chances are you&amp;#39;ll see a related discussion about it in there.&lt;/p&gt;
&lt;p&gt;Ultimately though, you can be confident it was discussed in some depth.
It&amp;#39;s impossible for an entire community to agree 100% on all points. The
philosophy of this approach is that people are flexible and can &lt;a href=&quot;https://en.wikipedia.org/wiki/Disagree_and_commit&quot;&gt;disagree and
commit&lt;/a&gt; to a decision.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;After spending more time than I&amp;#39;m proud of nitpicking linting rules in past
projects, I definitely see the value in StandardRB&amp;#39;s approach and I recommend
using it whenever possible.&lt;/p&gt;
&lt;p&gt;Keeping all the benefits of consistency while removing the overhead of
discussions, along with the automated fixes for most rules helps us deliver
better software more efficiently, and focus on what really matters.&lt;/p&gt;
&lt;p&gt;Other languages are adopting similar low-configurability code formatters. &lt;code&gt;mix formatter&lt;/code&gt; in Elixir, and &lt;code&gt;rustfmt&lt;/code&gt; in Rust both allow for some configurability, but
the community is surprisingly onboard with keeping within the standard.&lt;/p&gt;
&lt;p&gt;With that said, RuboCop is still a perfectly valid option if you, ironically,
disagree with this sentiment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby on Rails Controller Patterns and Anti-patterns</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/04/14/ruby-on-rails-controller-patterns-and-anti-patterns.html"/>
    <id>https://blog.appsignal.com/2021/04/14/ruby-on-rails-controller-patterns-and-anti-patterns.html</id>
    <published>2021-04-14T00:00:00+00:00</published>
    <updated>2021-04-14T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this part of the series on Rails patterns and anti-patterns, we are going to analyze the final part of the MVC (Model-View-Controller) design pattern — the Controller.</summary>
    <content type="html">&lt;p&gt;Welcome back to the fourth installment of the Ruby on Rails Patterns and Anti-Patterns series.&lt;/p&gt;
&lt;p&gt;Previously, we covered patterns and anti-patterns in general as well as in
relation to Rails Models and Views. In this post, we are going to analyze
the final part of the MVC (Model-View-Controller) design pattern — the
Controller. Let&amp;#39;s dive in and go through the patterns and anti-patterns
related to Rails Controllers.&lt;/p&gt;
&lt;h2&gt;At The Front Lines&lt;/h2&gt;
&lt;p&gt;Since Ruby on Rails is a web framework, HTTP requests are a vital
part of it. All sorts of Clients reach out to Rails backends via
requests and this is where controllers shine. Controllers are at the front
lines of receiving and handling requests. That makes them a fundamental
part of the Ruby on Rails framework. Of course, there is code that comes before
controllers, but controller code is something most of us can control.&lt;/p&gt;
&lt;p&gt;Once you define routes at the &lt;code&gt;config/routes.rb&lt;/code&gt;, you can hit the server on the
set route, and the corresponding controller will take care of the rest. Reading
the previous sentence might give an impression that everything is as simple as
that. But, often, a lot of the weight falls on the controller&amp;#39;s shoulders.
There is the concern of authentication and authorization, then there are
problems of how to fetch the needed data, as well as where and how to perform
business logic.&lt;/p&gt;
&lt;p&gt;All of these concerns and responsibilities that can occur inside the controller
can lead to some anti-patterns. One of the most &amp;#39;famous&amp;#39; ones is the
anti-pattern of a &amp;quot;fat&amp;quot; controller.&lt;/p&gt;
&lt;h2&gt;Fat (Obese) Controllers&lt;/h2&gt;
&lt;p&gt;The problem with putting too much logic in the controller is that you are
starting to violate the Single Responsibility Principle (SRP). This means that
we are doing too much work inside the controller. Often, this leads to a lot of
code and responsibilities piling up there. Here, &amp;#39;fat&amp;#39; refers to the
extensive code contained in the controller files, as well as the logic the
controller supports. It is often considered an anti-pattern.&lt;/p&gt;
&lt;p&gt;There are a lot of opinions on what a controller should do. A common ground of
the responsibilities a controller should have include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authentication and authorization&lt;/strong&gt; — checking whether the entity (oftentimes,
a user) behind the request is who it says it is and whether it is allowed
to access the resource or perform the action. Often, authentication is
saved in the session or the cookie, but the controller should still check
whether authentication data is still valid.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data fetching&lt;/strong&gt; — it should call the logic for finding the right data based on
the parameters that came with the request. In the perfect world, it should be
a call to one method that does all the work. The controller should not do the
heavy work, it should delegate it further.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Template rendering&lt;/strong&gt; — finally, it should return the right response by
rendering the result with the proper format (HTML, JSON, etc.). Or, it should
redirect to some other path or URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Following these ideas can save you from having too much going on inside
the controller actions and controller in general. Keeping it simple at the
controller level will allow you to delegate work to other areas of your
application. Delegating responsibilities and testing them one by one will
ensure that you are developing your app to be robust.&lt;/p&gt;
&lt;p&gt;Sure, you can follow the above principles, but you must be eager for some
examples. Let&amp;#39;s dive in and see what patterns we can use to relieve
controllers of some weight.&lt;/p&gt;
&lt;h2&gt;Query Objects&lt;/h2&gt;
&lt;p&gt;One of the problems that happen inside controller actions is too much
querying of data. If you followed our blog post on
&lt;a href=&quot;/2020/11/18/rails-model-patterns-and-anti-patterns.html&quot;&gt;Rails Model anti-patterns and patterns&lt;/a&gt;,
we went through a similar problem where models had too much querying logic.
But, this time we&amp;#39;ll use a pattern called Query Object. A Query Object is a
technique that isolates your complex queries into a single object.&lt;/p&gt;
&lt;p&gt;In most cases, Query Object is a Plain Old Ruby Object that is initialized with
an &lt;code&gt;ActiveRecord&lt;/code&gt; relation. A typical Query Object might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/queries/all_songs_query.rb

class AllSongsQuery
  def initialize(songs = Song.all)
    @songs = songs
  end

  def call(params, songs = Song.all)
    songs.where(published: true)
         .where(artist_id: params[:artist_id])
         .order(:title)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is made to be used inside the controller like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class SongsController &amp;lt; ApplicationController
  def index
    @songs = AllSongsQuery.new.call(all_songs_params)
  end

  private

  def all_songs_params
    params.slice(:artist_id)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also try out another approach of the query object:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/queries/all_songs_query.rb

class AllSongsQuery
  attr_reader :songs

  def initialize(songs = Song.all)
    @songs = songs
  end

  def call(params = {})
    scope = published(songs)
    scope = by_artist_id(scope, params[:artist_id])
    scope = order_by_title(scope)
  end

  private

  def published(scope)
    scope.where(published: true)
  end

  def by_artist_id(scope, artist_id)
    artist_id ? scope.where(artist_id: artist_id) : scope
  end

  def order_by_title(scope)
    scope.order(:title)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The latter approach makes the query object more robust by making &lt;code&gt;params&lt;/code&gt;
optional. Also, notice that we can now call &lt;code&gt;AllSongsQuery.new.call&lt;/code&gt;.
If you&amp;#39;re not a big fan of this, you can resort to class methods. If you write
your query class with class methods, it will no longer be an &amp;#39;object&amp;#39;, but this
is a matter of personal taste. For illustration purposes, let&amp;#39;s see how we can make&lt;code&gt;AllSongsQuery&lt;/code&gt; simpler to call in the wild.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/queries/all_songs_query.rb

class AllSongsQuery
  class &amp;lt;&amp;lt; self
    def call(params = {}, songs = Song.all)
      scope = published(songs)
      scope = by_artist_id(scope, params[:artist_id])
      scope = order_by_title(scope)
    end

    private

    def published(scope)
      scope.where(published: true)
    end

    def by_artist_id(scope, artist_id)
      artist_id ? scope.where(artist_id: artist_id) : scope
    end

    def order_by_title(scope)
      scope.order(:title)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can call &lt;code&gt;AllSongsQuery.call&lt;/code&gt; and we&amp;#39;re done. We can pass in &lt;code&gt;params&lt;/code&gt;
with &lt;code&gt;artist_id&lt;/code&gt;. Also, we can pass the initial scope if we need to change it
for some reason. If you really want to avoid calling &lt;code&gt;new&lt;/code&gt; over a query class, try out this &amp;#39;trick&amp;#39;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/queries/application_query.rb

class ApplicationQuery
  def self.call(*params)
    new(*params).call
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can create the &lt;code&gt;ApplicationQuery&lt;/code&gt; and then inherit from it in other query
classes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/queries/all_songs_query.rb
class AllSongsQuery &amp;lt; ApplicationQuery
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You still kept the &lt;code&gt;AllSongsQuery.call&lt;/code&gt;, but you made it more elegant.&lt;/p&gt;
&lt;p&gt;What&amp;#39;s great about query objects is that you can test them in isolation and
ensure that they are doing what they should do. Furthermore, you can extend these query
classes and test them without worrying too much about the logic in the
controller. One thing to note is that you should handle your request parameters
elsewhere, and not rely on the query object to do so. What do you think, are you going to give query object a try?&lt;/p&gt;
&lt;h2&gt;Ready To Serve&lt;/h2&gt;
&lt;p&gt;OK, so we&amp;#39;ve handled ways to delegate the gathering and fetching of data into Query
Objects. What do we do with the pilled-up logic between data gathering and the
step where we render it? Good that you asked, because one of the solutions is
to use what are called Services. A service is oftentimes regarded as a PORO
(Plain Old Ruby Object) that performs a single (business) action. We will go ahead and explore this idea a bit below.&lt;/p&gt;
&lt;p&gt;Imagine we have two services. One creates a receipt, the other sends a receipt
to the user like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/services/create_receipt_service.rb
class CreateReceiptService
  def self.call(total, user_id)
    Receipt.create!(total: total, user_id: user_id)
  end
end

# app/services/send_receipt_service.rb
class SendReceiptService
  def self.call(receipt)
    UserMailer.send_receipt(receipt).deliver_later
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in our controller we would call the &lt;code&gt;SendReceiptService&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/controllers/receipts_controller.rb

class ReceiptsController &amp;lt; ApplicationController
  def create
    receipt = CreateReceiptService.call(total: receipt_params[:total],
                                        user_id: receipt_params[:user_id])

    SendReceiptService.call(receipt)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have two services doing all the work, and the controller just
calls them. You can test these separately, but the problem is, there&amp;#39;s no
clear connection between the services. Yes, in theory, all of them perform a
single business action. But, if we consider the abstraction level from the
stakeholders&amp;#39; perspective — their view of the action of creating a receipt
involves sending an email of it. Whose level of abstraction is &amp;#39;right&amp;#39;™️?&lt;/p&gt;
&lt;p&gt;To make this thought experiment a bit more complex, let&amp;#39;s add a requirement that
the total sum on the receipt has to be calculated or fetched from somewhere
during the creation of the receipt. What do we do then? Write another service to
handle the summation of the total sum? The answer might be to follow the Single
Responsibility Principle (SRP) and abstract things away from each other.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/services/create_receipt_service.rb
class CreateReceiptService
  ...
end

# app/services/send_receipt_service.rb
class SendReceiptService
  ...
end

# app/services/calculate_receipt_total_service.rb
class CalculateReceiptTotalService
  ...
end

# app/controllers/receipts_controller.rb
class ReceiptsController &amp;lt; ApplicationController
  def create
    total = CalculateReceiptTotalService.call(user_id: receipts_controller[:user_id])

    receipt = CreateReceiptService.call(total: total,
                                        user_id: receipt_params[:user_id])

    SendReceiptService.call(receipt)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By following SRP, we make sure that our services can be composed together into
larger abstractions, like the &lt;code&gt;ReceiptCreation&lt;/code&gt; process. By creating this &amp;#39;process&amp;#39;
class, we can group all the actions needed to complete the process. What do you
think about this idea? It might sound like too much abstraction at first,
but it might prove beneficial if you are calling these actions all over the place.
If this sounds good to you, check out the &lt;a href=&quot;https://trailblazer.to/2.1/docs/operation.html&quot;&gt;Trailblazer&amp;#39;s Operation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To sum up, the new &lt;code&gt;CalculateReceiptTotalService&lt;/code&gt; service can deal with all the
number crunching. Our &lt;code&gt;CreateReceiptService&lt;/code&gt; is responsible for writing a
receipt to the database. The &lt;code&gt;SendReceiptService&lt;/code&gt; is there to dispatch emails
to users about their receipts. Having these small and focused classes can make
combining them in other use cases easier, thus resulting in an easier to
maintain and easier to test codebase.&lt;/p&gt;
&lt;h3&gt;The Service Backstory&lt;/h3&gt;
&lt;p&gt;In the Ruby world, the approach of using service classes is also known as actions,
operations, and similar. What these all boil down to is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Command_pattern&quot;&gt;Command pattern&lt;/a&gt;.
The idea behind the Command pattern is that an object (or in our example, a
class) is encapsulating all the information needed to perform a business
action or trigger an event. The information that the caller of the command
should know is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;name of the command&lt;/li&gt;
&lt;li&gt;method name to call on the command object/class&lt;/li&gt;
&lt;li&gt;values to be passed for the method parameters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, in our case, the caller of a command is a controller. The approach
is very similar, just that the naming in Ruby is &amp;#39;Service&amp;#39;.&lt;/p&gt;
&lt;h2&gt;Split Up The Work&lt;/h2&gt;
&lt;p&gt;If your controllers are calling some 3rd party services and they are blocking
your rendering, maybe it&amp;#39;s time to extract these calls and render them
separately with another controller action. An example of this
can be when you try to render a book&amp;#39;s information and fetch its rating from some other service
that you can&amp;#39;t really influence (like Goodreads).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/controllers/books_controller.rb

class BooksController &amp;lt; ApplicationController
  def show
    @book = Book.find(params[:id])

    @rating = GoodreadsRatingService.new(book).call
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If Goodreads is down or something similar, your users are going to have to wait
for the request to Goodreads servers to timeout. Or, if something is slow on their
servers, the page will load slowly. You can extract the calling of the 3rd party service
into another action like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/controllers/books_controller.rb

class BooksController &amp;lt; ApplicationController
  ...

  def show
    @book = Book.find(params[:id])
  end

  def rating
    @rating = GoodreadsRatingService.new(@book).call

    render partial: &amp;#39;book_rating&amp;#39;
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, you will have to call the &lt;code&gt;rating&lt;/code&gt; path from your views, but hey, your show
action doesn&amp;#39;t have a blocker anymore. Also, you need the &amp;#39;book_rating&amp;#39;
partial. To do this more easily, you can use the &lt;a href=&quot;https://github.com/renderedtext/render_async&quot;&gt;render_async gem&lt;/a&gt;.
You just need to put the following statement where you render your book&amp;#39;s rating:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= render_async book_rating_path %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Extract HTML for rendering the rating into the &lt;code&gt;book_rating&lt;/code&gt; partial, and put:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= content_for :render_async %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside your layout file, the gem will call &lt;code&gt;book_rating_path&lt;/code&gt; with an AJAX
request once your page loads, and when the rating is fetched, it will show it
on the page. One big gain in this is that your users get to see the book page
faster by loading ratings separately.&lt;/p&gt;
&lt;p&gt;Or, if you want, you can use &lt;a href=&quot;https://turbo.hotwire.dev/handbook/frames#lazily-loading-frames&quot;&gt;Turbo Frames&lt;/a&gt; from Basecamp.
The idea is the same, but you just use the &lt;code&gt;&amp;lt;turbo-frame&amp;gt;&lt;/code&gt; element in your markup like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;turbo-frame id=&amp;quot;rating_1&amp;quot; src=&amp;quot;/books/1/rating&amp;quot;&amp;gt; &amp;lt;/turbo-frame&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whatever option you choose, the idea is to split the heavy or flaky work from
your main controller action and show the page to the user as soon as possible.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;If you like the idea of keeping controllers thin and picture them as just
&amp;#39;callers&amp;#39; of other methods, then I believe this post brought some insight on
how to keep them that way. The few patterns and anti-patterns that we mentioned
here are, of course, not an exhaustive list. If you have an idea on what is better or
what you prefer, please reach out on Twitter and we can discuss.&lt;/p&gt;
&lt;p&gt;Definitely stay tuned on this series, we are going to do at least one more
blog post where we sum up common Rails problems and takeaways from the series.&lt;/p&gt;
&lt;p&gt;Until next time, cheers!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Troubleshooting ActiveRecord Performance</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/02/24/troubleshooting-activerecord-performance.html"/>
    <id>https://blog.appsignal.com/2021/02/24/troubleshooting-activerecord-performance.html</id>
    <published>2021-02-24T00:00:00+00:00</published>
    <updated>2021-02-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">You usually don&#039;t need to worry about AcriveRecord and its inner workings. This post comes in handy when performance issues arise and you have to start worrying about it.</summary>
    <content type="html">&lt;p&gt;ActiveRecord is Ruby on Rails’ most magical feature. We don’t usually need to worry about its inner workings, but when we do, here’s how AppSignal can help us know what’s going on under the hood.&lt;/p&gt;
&lt;h2&gt;What Is ActiveRecord?&lt;/h2&gt;
&lt;p&gt;To talk about ActiveRecord, we need to first think of frameworks, specifically about MVC frameworks. MVC stands for Model-View-Controller, and it’s a popular software design pattern for graphical and web applications.&lt;/p&gt;
&lt;p&gt;MVC frameworks are composed of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt;: handles business logic and data persistence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View&lt;/strong&gt;: drives the presentation layer and draws the user interface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controller&lt;/strong&gt;: ties everything together.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ActiveRecord is the &lt;em&gt;model&lt;/em&gt; component in the Ruby in Rails framework. It introduces an abstraction layer between code and data, so we don’t have to write SQL code ourselves. Each model is mapped to one table and provides various methods to perform CRUD operations (Create, Read, Update and Delete).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/activerecord.png&quot; alt=&quot;ActiveRecord in action&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Monitoring ActiveRecord With AppSignal&lt;/h2&gt;
&lt;p&gt;Abstractions feel magical — they help us ignore details we don’t need to know and focus on the task at hand. But when things don’t work as expected, the complexity they add can make it harder to determine the root cause. AppSignal can give us a detailed breakdown of what’s really happening in Rails.&lt;/p&gt;
&lt;h2&gt;The Response Time Graph&lt;/h2&gt;
&lt;p&gt;Let’s get a first look into the problem. Troubleshooting performance issues is an iterative process. Once you have your Rails application &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/rails.html&quot;&gt;reporting to AppSignal&lt;/a&gt;, go to &lt;a href=&quot;https://appsignal.com/redirect-to/app?to=performance&quot;&gt;&lt;strong&gt;Incidents&lt;/strong&gt; &amp;gt; &lt;strong&gt;Performance&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/response-time.png&quot; alt=&quot;Response Time Graph&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The Response Time graph will show the response time percentiles for each &lt;a href=&quot;https://docs.appsignal.com/guides/namespaces.html&quot;&gt;namespace&lt;/a&gt;. ActiveRecord events are automatically assigned to the namespace the request or background job the queries are executed in.&lt;/p&gt;
&lt;p&gt;Next, look at the Event Group graph. It shows how much time is consumed by category. Check how much relative time is spent by &lt;code&gt;active_record&lt;/code&gt;. Check the usage in all your namespaces.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/events-group1.png&quot; alt=&quot;Event Group&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The graph will immediately tell us where we should focus our code-optimizing efforts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/events-group2.png&quot; alt=&quot;Event Group Durations&quot;/&gt;&lt;/p&gt;
&lt;p&gt;While you’re in the performance graph dashboard, check the response time and the throughput to ensure there isn’t any higher-than-usual activity on your application.&lt;/p&gt;
&lt;h2&gt;The Slow Queries Dashboard&lt;/h2&gt;
&lt;p&gt;Now that we’ve identified that the problem is data-bound let’s see if we can zoom in to determine the root cause.&lt;/p&gt;
&lt;p&gt;Open the &lt;a href=&quot;https://appsignal.com/redirect-to/app?to=improve/queries&quot;&gt;&lt;strong&gt;Improve&lt;/strong&gt; &amp;gt; &lt;strong&gt;Slow Queries&lt;/strong&gt;&lt;/a&gt; dashboard. This page shows the list of SQL queries ranked by impact on the overall time. All ActiveRecord-originated queries are shown as a &lt;code&gt;sql.active_record&lt;/code&gt; events.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/slow-queries1.png&quot; alt=&quot;Slow Query List&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Try clicking on the topmost query to see its details. The dashboard shows the average duration and the query text.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/query-detail3.png&quot; alt=&quot;Query Detail&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Scrolling below will show you the query’s response time in the last few hours and the originating action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/query-detail2.png&quot; alt=&quot;Query Action&quot;/&gt;&lt;/p&gt;
&lt;p&gt;You might find that some of the actions have an associated incident. This means that AppSignal created a performance incident while the query was running, but it doensn’t necessarily mean that ActiveRecord is the cause of it.&lt;/p&gt;
&lt;h2&gt;Performance Measurements Dashboard&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.appsignal.com/application/notification-settings.html#performance-incident-notification-settings&quot;&gt;Performance measurement incidents&lt;/a&gt; are opened when AppSignal records a new endpoint or background job.&lt;/p&gt;
&lt;p&gt;Incidents are located on &lt;a href=&quot;https://appsignal.com/redirect-to/app?to=performance&quot;&gt;&lt;strong&gt;Performance&lt;/strong&gt; &amp;gt; &lt;strong&gt;Issue List&lt;/strong&gt;&lt;/a&gt; dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/web-incidents.png&quot; alt=&quot;Performance issue list&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The incident page shows the elapsed time and number of allocations for each of the MVC components. ActiveRecord problems will exhibit long durations in the &lt;code&gt;active_record&lt;/code&gt; category.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/incident1-sample.png&quot; alt=&quot;Sample breakdown of an ActiveRecord incident&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The event timeline shows how the event progressed over time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/incident1-timeline.png&quot; alt=&quot;Incident timeline&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Finding ActiveRecord Issues&lt;/h2&gt;
&lt;p&gt;In this section, we’ll see how AppSignal can help us identify some common ActiveError issues.&lt;/p&gt;
&lt;h2&gt;Selecting the Relevant Columns&lt;/h2&gt;
&lt;p&gt;Database wisdom says that we should always retrieve the columns needed for the job. For example, instead of &lt;code&gt;SELECT * FROM people&lt;/code&gt; we should &lt;code&gt;SELECT first_name, surname, birthdate FROM people&lt;/code&gt;. That’s all well and good, but how do we do it on Rails?&lt;/p&gt;
&lt;p&gt;By default, ActiveRecord retrieves all columns.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Person.all.each {
      # process data
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Luckily, we have the &lt;code&gt;select&lt;/code&gt; method to pick and choose the required columns:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Person.select(:name, :address, :birthdate).each {
      # process data
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It may sound like I’m being obsessive about little details. But on wide tables, selecting all columns is just wasteful. You’ll notice that when this happens, ActiveRecord allocates big chunks of memory:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/allocations.png&quot; alt=&quot;Allocations&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;N+1 Problems&lt;/h2&gt;
&lt;p&gt;The N+1 problem happens when the application gets a set of records from the database and loops through it. This causes the application to execute N+1 queries, where N is the number of rows initially obtained. As you might imagine, this pattern scales poorly as the table grows. It’s such a damaging problem that AppSignal specifically warns you about it:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/n+1-warning.png&quot; alt=&quot;N+1 Warning&quot;/&gt;&lt;/p&gt;
&lt;p&gt;N+1 problems usually appear with associated models. Imagine we have a Person model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Person &amp;lt; ApplicationRecord
    has_many :addresses
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each person can have many addresses:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Address &amp;lt; ApplicationRecord
    belongs_to :person
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most straightforward way of retrieving the data leads to the N+1 problem:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class RelatedTablesController &amp;lt; ApplicationController
    def index
        Person.all.each do |person|
            person.addresses.each do |address|
            address.address
            end
        end
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see in AppSignal that the application is running a &lt;code&gt;SELECT&lt;/code&gt; per person:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/related-query.png&quot; alt=&quot;N+1 Query&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The fix for this particular case is simple: use &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-includes&quot;&gt;includes&lt;/a&gt;, which tells ActiveRecord to optimize the query for the needed columns:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class RelatedTablesController &amp;lt; ApplicationController
    def index
        Person.all.includes(:addresses).each do |person|
            person.addresses.each do |address|
            address.address
            end
        end
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have two queries instead of N+1:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Processing by RelatedTablesController#index as HTML
   (0.2ms)  SELECT sqlite_version(*)
  ↳ app/controllers/related_tables_controller.rb:12:in `index&amp;#39;
  Person Load (334.6ms)  SELECT &amp;quot;people&amp;quot;.* FROM &amp;quot;people&amp;quot;
  ↳ app/controllers/related_tables_controller.rb:12:in `index&amp;#39;

  Address Load (144.4ms)  SELECT &amp;quot;addresses&amp;quot;.* FROM &amp;quot;addresses&amp;quot; WHERE &amp;quot;addresses&amp;quot;.&amp;quot;person_id&amp;quot; IN (1, 2, 3, . . .)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Execute the Least Necessary Queries Per Table&lt;/h2&gt;
&lt;p&gt;This is sometimes confused with the N+1 problem, but it’s a bit different. When we query a table, we should retrieve all the data we think we’ll need to minimize read operations. There is, however, a lot of innocent-looking code that triggers redundant queries. For example, look how &lt;code&gt;count&lt;/code&gt; always results in a &lt;code&gt;SELECT COUNT(*)&lt;/code&gt; query in the following view:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;lt;ul&amp;gt;
    &amp;lt;% @people.each do |person| %&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;%= person.name %&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;h2&amp;gt;Number of Persons: &amp;lt;%= @people.count %&amp;gt;&amp;lt;/h2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now ActiveRecord does two queries:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Rendering duplicated_table_query/index.html.erb within layouts/application
  (69.1ms)  SELECT COUNT(*) FROM &amp;quot;people&amp;quot; WHERE &amp;quot;people&amp;quot;.&amp;quot;name&amp;quot; = ?  [[&amp;quot;name&amp;quot;, &amp;quot;John Waters&amp;quot;]]
↳ app/views/duplicated_table_query/index.html.erb:3
Person Load (14.6ms)  SELECT &amp;quot;people&amp;quot;.* FROM &amp;quot;people&amp;quot; WHERE &amp;quot;people&amp;quot;.&amp;quot;name&amp;quot; = ?  [[&amp;quot;name&amp;quot;, &amp;quot;John Waters&amp;quot;]]
↳ app/views/duplicated_table_query/index.html.erb:6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In AppSignal, the symptom you’ll notice is that there are two &lt;code&gt;active_record&lt;/code&gt; events on the same table:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/duplicated-events.png&quot; alt=&quot;Duplicated Query&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The reality is that we don’t need two queries; we already have all the data we need in memory. In this case, the solution is to swap &lt;code&gt;count&lt;/code&gt; with &lt;code&gt;size&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;lt;ul&amp;gt;
&amp;lt;% @people.each do |person| %&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;%= person.name %&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;h2&amp;gt;Number of Persons: &amp;lt;%= @people.size %&amp;gt;&amp;lt;/h2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have a single &lt;code&gt;SELECT&lt;/code&gt;, as it should be:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Rendering duplicated_table_query/index.html.erb within layouts/application
Person Load (63.2ms)  SELECT &amp;quot;people&amp;quot;.* FROM &amp;quot;people&amp;quot; WHERE &amp;quot;people&amp;quot;.&amp;quot;name&amp;quot; = ?  [[&amp;quot;name&amp;quot;, &amp;quot;Abdul Strosin&amp;quot;]]
↳ app/views/duplicated_table_query/index.html.erb:5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another solution is to use &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-preload&quot;&gt;preload&lt;/a&gt; to cache the data in memory.&lt;/p&gt;
&lt;h2&gt;Computing Aggregated Data in Rails&lt;/h2&gt;
&lt;p&gt;Aggregation is used to compute a value based on a set of data. Databases are great at working with big datasets. This is what they do and what we use them for. On the other hand, using Rails to aggregate doesn&amp;#39;t scale since it requires getting all the records from the database, holding them in memory, and then calculating using high-level code.&lt;/p&gt;
&lt;p&gt;We’re doing aggregation in Rails whenever we resort to Ruby functions like &lt;code&gt;max&lt;/code&gt;, &lt;code&gt;min&lt;/code&gt;, or &lt;code&gt;sum&lt;/code&gt; over ActiveRecord elements or other enumerables.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AggregatedColumnsController &amp;lt; ApplicationController
    def index
        @mean = Number.pluck(:number).sum()
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/aggregated-query.png&quot; alt=&quot;Aggregation on Rails&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Fortunately, ActiveRecord models include specific methods that map to aggregation functions in the database. For example, the following query maps to &lt;code&gt;SELECT SUM(number) FROM ...&lt;/code&gt;, which is much faster and cheaper to run than the previous example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# controller

class AggregatedColumnsController &amp;lt; ApplicationController
    def index
        @mean = Number.sum(:number)
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Processing by AggregatedColumnsController#index as */*
  (2.4ms)  SELECT SUM(&amp;quot;numbers&amp;quot;.&amp;quot;number&amp;quot;) FROM &amp;quot;numbers&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need more complex or combined aggregation functions, you may need to include a bit of raw SQL code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;sql = &amp;quot;SELECT AVG(number), STDDEV(number), VAR(number) FROM ...&amp;quot;
@results = ActiveRecord::Base.connection.execute(sql)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Managing Big Transactions&lt;/h2&gt;
&lt;p&gt;SQL transactions ensure consistent and atomic updates. When we use a transaction to make a change, either every row is updated successfully, or the whole thing is rolled back. In any case, the database always remains in a consistent state.&lt;/p&gt;
&lt;p&gt;We can bundle a batch of changes in a single transaction with &lt;code&gt;ActiveRecord::Base.transaction&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class BigTransactionController &amp;lt; ApplicationController
    def index
        ActiveRecord::Base.transaction do
            (1..1000).each do
                Person.create(name: &amp;#39;Les Claypool&amp;#39;)
            end
        end
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a lot of legitimate cases for using large transactions. These, however, run into the risk of slowing the database down. Besides, transactions exceeding certain thresholds will result in the database rejecting them.&lt;/p&gt;
&lt;p&gt;The first sign of transactions being too big is spending a lot of time on &lt;code&gt;commit transaction&lt;/code&gt; events:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/img/bigtran.png&quot; alt=&quot;Commit Event in the Slow Dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Barring configuration issues on the database itself, the solution is to break down the transaction into smaller chunks.&lt;/p&gt;
&lt;h2&gt;Monitoring The Database&lt;/h2&gt;
&lt;p&gt;Sometimes even though we search the code, we can’t find anything wrong with it. Then, there may be a problem in the database itself. Database engines are complex, and many things can go wrong: low memory, default settings, missing indexes, inconveniently-scheduled backup jobs. The picture can’t be complete unless we get information about the machine running the database.&lt;/p&gt;
&lt;p&gt;If we’re running our own databases, we can install the &lt;a href=&quot;https://docs.appsignal.com/standalone-agent/installation.html&quot;&gt;standalone agent&lt;/a&gt; to capture host metrics. To learn more about how to use the standalone agent, read: &lt;a href=&quot;/2020/09/23/monitoring-any-system-with-statsd-and-the-standalone-appsignal-agent.html&quot;&gt;Monitoring Any System with StatsD and AppSignal’s Standalone Agent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The following signs might show something is going on with the database. Go to the &lt;a href=&quot;https://appsignal.com/redirect-to/app?to=host_metrics&quot;&gt;&lt;strong&gt;Inspect&lt;/strong&gt; &amp;gt; &lt;strong&gt;Host Metrics&lt;/strong&gt;&lt;/a&gt; dashboard to see the resource usage in your server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High memory usage&lt;/strong&gt;: databases need a lot of memory — more than most other systems — to run correctly. As the dataset grows, memory requirements usually scale along. We’ll need to either add more memory or split the dataset among different machines from time to time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Swap usage&lt;/strong&gt;: ideally, database machines should not need swap memory at all. Swapping kills database performance. Its presence means there’s a more in-depth configuration or memory problem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High I/O usage&lt;/strong&gt;: peaks in disk activity can be due to maintenance tasks such as re-indexing or backing up the database. Doing these tasks during peak hours will definitely hit performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;👋 If you like this article, there is a lot more we wrote about &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;Ruby (on Rails) performance&lt;/a&gt;, check out our &lt;a href=&quot;https://www.appsignal.com/ruby#ruby-monitoring-checklist&quot;&gt;Ruby performance monitoring checklist&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Diagnosing performance issues is never an easy task. Today, we’ve learned how to use Appsignal to quickly pinpoint the source of the problem.&lt;/p&gt;
&lt;p&gt;Let’s keep learning about AppSignal and Ruby on Rails:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2018/04/24/active-record-performance-the-n-1-queries-antipattern.html&quot;&gt;ActiveRecord performance: the N+1 queries antipattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2020/01/22/rails-is-fast-optimize-your-view-performance.html&quot;&gt;Rails is Fast: Optimize Your View Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2020/08/05/introduction-to-ruby-on-rails-patterns-and-anti-patterns.html&quot;&gt;Introduction to Ruby on Rails Patterns and Anti-patterns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Using Webpacker in Your Ruby on Rails Application — a Deep Dive</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/02/17/using-webpacker-in-your-ruby-on-rails-app-deep-dive.html"/>
    <id>https://blog.appsignal.com/2021/02/17/using-webpacker-in-your-ruby-on-rails-app-deep-dive.html</id>
    <published>2021-02-17T00:00:00+00:00</published>
    <updated>2021-02-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s take a deep dive into Webpacker and see how the tool works under the hood.</summary>
    <content type="html">&lt;p&gt;At the beginning of the internet age, websites were much simpler and not very interactive. With the advancement of technology, devices, and programming languages, they became more complex and consisted of several files, including assets like images and CSS stylesheets.&lt;/p&gt;
&lt;p&gt;The more interactive your website is, the more JavaScript code you have to use. To use such code, you have to include all HTML code files using the script tag. Such an approach is error-prone because you have to remember to include every single file and keep the correct order, otherwise, your code may not work. With the Webpack tool, these problems go away. Webpacker is a bridge between Webpack and a Rails application.&lt;/p&gt;
&lt;p&gt;This article takes a deep dive into Webpacker and offers a detailed explanation that will enable you to understand how this tool works under the hood. To make the content as valuable as possible, I decided to divide it into the following sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High-level overview&lt;/strong&gt; — before we do the deep dive, I will explain the high-level overview of both Webpack and Webpacker. You can treat it as the preparation for more advanced topics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structure explanation&lt;/strong&gt; — since we&amp;#39;ll explore multiple files, it is good to know why the particular structure was used and how it responds to the general purpose.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anatomy explanation&lt;/strong&gt; — this part covers the investigation of the most important files of the Webpack setup. Knowing how they work is essential for the effective and hassle-free development process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment specific setup&lt;/strong&gt; — the last part explains how Webpacker works in development and production environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After reading this article, you&amp;#39;ll know what Webpack and Webpacker are and why we need them in our Rails application. The deep dive into Webpacker’s internals will help you understand how the tool connects with Webpack and how it communicates with the Rails application.&lt;/p&gt;
&lt;h2&gt;Before Diving In&lt;/h2&gt;
&lt;p&gt;Before going out into deep water, we first need to prepare. A high-level overview is a great way to start working with any technology as it helps us understand the purpose of a given tool and the problems it solves without going deep into its internals.&lt;/p&gt;
&lt;h2&gt;Webpack&lt;/h2&gt;
&lt;p&gt;I already mentioned that Webpack helps us organize our JavaScript file code to avoid errors and improve a website’s performance. It organizes the code into bundles.&lt;/p&gt;
&lt;p&gt;A bundle is a file where multiple modules are intelligently placed, respecting the dependency graph that is first created. Thanks to this process, we can be sure that our code will work as expected and before we invoke a given library’s code, it will have been loaded.&lt;/p&gt;
&lt;p&gt;Webpack isn&amp;#39;t the only tool that is used by default in the newest version of Rails. When the project is generated, the configuration files for Babel and PostCSS are created as well. PostCSS is a tool for transforming CSS with JavaScript, and Babel is a JavaScript compiler that enables us to write modern JavaScript without worrying about browser support.&lt;/p&gt;
&lt;h2&gt;Webpacker&lt;/h2&gt;
&lt;p&gt;Webpacker is a tool that integrates Webpack with a Rails application. It makes it easy to configure and develop JavaScript-like applications and optimize them for the production environment.&lt;/p&gt;
&lt;p&gt;The source code is available as a gem, and the library comes with a development server that makes the development of the app very fast as you don’t have to stop and start the server to see the changes introduced in the JavaScript files.&lt;/p&gt;
&lt;h2&gt;The Deep Immersion&lt;/h2&gt;
&lt;p&gt;Now that you know why Webpack was created and what role Wepacker plays in a Rails application, we can focus on Webpacker’s internals to see how it organizes files, speeds up the development process, and optimizes files for the production environment.&lt;/p&gt;
&lt;p&gt;Webpacker is available out-of-the-box in the newest version of Rails. Before creating a new project, ensure that the Node version is greater or equal to 10.17.0 as the &lt;code&gt;webpacker:install&lt;/code&gt; command will be automatically invoked with rails new command.&lt;/p&gt;
&lt;h2&gt;Webpacker File Structure&lt;/h2&gt;
&lt;p&gt;After the install command is executed, the following files are created:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;config/webpacker.yml&lt;/code&gt; — the main configuration file that contains the default configuration and configs for specific environments&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config/webpacker/&lt;/code&gt; — the directory where the JavaScript configuration files for particular environments are created&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bin/webpack&lt;/code&gt; — an executable file that invokes webpack to create bundles&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bin/webpack-dev-server&lt;/code&gt; — an executable file that starts the development server, which reloads webpack every time you make a change inside the JavaScript files included in the bundle&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Anatomy of the Configuration File&lt;/h2&gt;
&lt;p&gt;The main configuration file for Webpacker is located under the config directory, and it’s named &lt;code&gt;webpacker.yml&lt;/code&gt;. Unlike Webpacker, configuration for Webpack is stored separately for each environment under the &lt;code&gt;config/webpack&lt;/code&gt; directory.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Webpacker&lt;/h3&gt;
&lt;p&gt;By default, the configuration file contains many entries — default ones, and sections for each environment where you can overwrite specific settings. Let’s take a look at the most important settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;source_path&lt;/code&gt; — the primary source of javascript files in your application. It’s set to &lt;code&gt;app/javascript&lt;/code&gt; by default, and usually, there&amp;#39;s no need to change this value.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;source_entry_path&lt;/code&gt; — the name of the directory under the source_path where you keep the pack files, aka entry points. By default, this setting is set to the packs’ directory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public_root_path&lt;/code&gt; — path to the directory in your application that is accessible from a browser. In a typical Rails application, it’s a public directory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public_output_path&lt;/code&gt; — when Webpacker compiles your files, it will put all compiled files in this directory under the &lt;code&gt;public_root_path&lt;/code&gt;. By default, the directory is named packs, but you can call it whatever you want.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;webpack_compile_output&lt;/code&gt; — if the flag is set to true, then output messages are displayed when files are compiled. It’s useful when compilation fails, as you&amp;#39;ll be aware of that and can take action.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is worth mentioning that the development section also contains configuration for the dev server that is used to compile files in the development environment without the need for restarting the server.&lt;/p&gt;
&lt;p&gt;If you would like to access the configuration from the rails console or code level, you can call &lt;code&gt;Webpacker.manifest.config&lt;/code&gt;, which will return the configuration class instance.&lt;/p&gt;
&lt;h3&gt;Webpack&lt;/h3&gt;
&lt;p&gt;Each environment has its configuration file, but in every file, the main environment file is imported: &lt;code&gt;config/webpack/environment.js&lt;/code&gt;. In the environment configuration file, there is a place for loading custom plugins that will modify the default behavior of Webpack. We can also add custom rules for compiling our files.&lt;/p&gt;
&lt;h2&gt;The Anatomy of the Pack File&lt;/h2&gt;
&lt;p&gt;Packs are located under the &lt;code&gt;app/javascript/packs&lt;/code&gt; directory. Each pack file is treated by Webpacker as an entry point when the compilation process starts.&lt;/p&gt;
&lt;h3&gt;The Default Pack File&lt;/h3&gt;
&lt;p&gt;When you generate a new Rails project, a default pack file named application.js is created with the following content:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import Rails from &amp;quot;@rails/ujs&amp;quot;;
import Turbolinks from &amp;quot;turbolinks&amp;quot;;
import * as ActiveStorage from &amp;quot;@rails/activestorage&amp;quot;;
import &amp;quot;channels&amp;quot;;

Rails.start();
Turbolinks.start();
ActiveStorage.start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the first step is to import the given library and then call its initialization method when it’s needed. When you call import, the system searches for a given node module or a local library. With the standard approach, you don’t have to explicitly initialize the library as it’s initialized when you include it in the page source with the script tag.&lt;/p&gt;
&lt;h3&gt;Good Practices for a Pack File&lt;/h3&gt;
&lt;p&gt;While you can put regular JavaScript code inside the pack file and execute it, it is not recommended to keep JavaScript code inside the pack file. The best approach is to keep the files clean and only import and initialize libraries here and keep other logic outside the packs directory.&lt;/p&gt;
&lt;h3&gt;Including Pack Files in the Application&lt;/h3&gt;
&lt;p&gt;Pack files are not automatically included in the website’s source. Just like in the asset pipeline case, we have to use a special tag inside our views:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;lt;%= javascript_packs_with_chunks_tag &amp;#39;application&amp;#39; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How does this method work? Under the hood, it calls the Webpacker configuration that holds the data you put in the &lt;code&gt;config/webpacker.yml&lt;/code&gt; file. It looks for an application.js file inside the entry points directory, which, in our case, is &lt;code&gt;app/javascript/packs&lt;/code&gt;. You can verify it by calling the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Webpacker.manifest.config.source_entry_path
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When entries are found, Webpacker calls &lt;code&gt;javascript_include_tag&lt;/code&gt; method, a helper from the &lt;code&gt;ActionView&lt;/code&gt; library available by default in Rails. The method accepts one or more sources and returns script tags that you can apply straight to your view.&lt;/p&gt;
&lt;h2&gt;The Development Server&lt;/h2&gt;
&lt;p&gt;Webpacker development server’s entry point is the executable file placed inside the &lt;code&gt;bin&lt;/code&gt; folder and named &lt;code&gt;webpack-dev-server&lt;/code&gt;. The process of running the server consists of five steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Setting environment&lt;/strong&gt; — the environment name for Node and Rails is set. When the environment is not specified, the program assumes that the server will be executed in the development environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Loading configuration&lt;/strong&gt; — the file &lt;code&gt;config/webpacker.yml&lt;/code&gt; is loaded with settings for the environment set in the previous step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validating command options&lt;/strong&gt; — the program checks if we passed appropriate options to the server command. For example, it will throw an error when the &lt;code&gt;--https&lt;/code&gt; option is given, but we didn’t specify it in the &lt;code&gt;config/webpacker.yml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verifying port’s availability&lt;/strong&gt; — the program checks if the given port is available to use. If another program is already using it, it will throw an error letting you know that you have to update the webpack server configuration inside the &lt;code&gt;config/webpacker.yml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Executing webpack serve command&lt;/strong&gt; — when the node modules directory exists, the program executes the command within the directory, otherwise, it executes it with yarn. The program passes the configuration option to the command that points to one of the files placed inside the &lt;code&gt;config/webpack/&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The server is now running, and it compiles your code on the fly, so you don’t have to restart the server to see the changes and check if webpack was able to create the bundle with the new code.&lt;/p&gt;
&lt;p&gt;The development server’s code is quite simple. It only loads the config from the configuration file in YAML format and passes the proper config in JavaScript format directly to the &lt;code&gt;webpack serve&lt;/code&gt; command. You can also run it by hand, but you don’t have to take care of proper command arguments with the built-in command.&lt;/p&gt;
&lt;h2&gt;Compiling Files for the Production Environment&lt;/h2&gt;
&lt;p&gt;Suppose you want to deploy the application that&amp;#39;s using webpacker. In that case, you can simply invoke the &lt;code&gt;assets:precompile&lt;/code&gt; task as Webpacker automatically hooks up the task &lt;code&gt;webpacker:compile&lt;/code&gt; to it.&lt;/p&gt;
&lt;p&gt;How does the &lt;code&gt;webpacker:compile&lt;/code&gt; task work under the hood? Let’s dig into it and see. It does the following things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It wraps the main call into two blocks ensuring that the &lt;code&gt;NODE&lt;/code&gt; environment is set and Webpacker logs are output to the standard out.&lt;/li&gt;
&lt;li&gt;It calls Webpack and then parses its logs to determine if the action was successful or not.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can now visit the &lt;code&gt;public/packs/js&lt;/code&gt; directory to see the compiled files. They should be deployed to the server.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;We have just learned how Webpacker works under the hood. You can treat it as a bridge between the Webpack library and a Rails application that allows you to configure Webpack with Ruby and easily use javascript code inside your application.&lt;/p&gt;
&lt;p&gt;To summarize the whole article, let’s recall the main elements of Webpacker again:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configuration — it’s placed inside the &lt;code&gt;config/webpacker.yml&lt;/code&gt; file and allows you to point Webpacker to the directory where you keep your javascript files and packs&lt;/li&gt;
&lt;li&gt;Pack — it’s a single entry point from which Webpacker begins compilation. Each library imported and initialized in that file will be automatically compiled.&lt;/li&gt;
&lt;li&gt;Development server — a simple script that allows you to compile files on the fly, so you don’t have to restart the server each time you modify JavaScript files&lt;/li&gt;
&lt;li&gt;Compile task — a simple rake task that invokes Webpack and compiles files under the public directory so you can deploy them to the production server and make them available for end-users&lt;/li&gt;
&lt;li&gt;View helper — helper that allows you to include compiled files inside the views&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For many developers without much experience, Wepacker seems to be a little magical, but in fact, it’s a simple library that helps us use Webpack in a Ruby way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby on Rails View Patterns and Anti-patterns</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/02/10/ruby-on-rails-view-patterns-and-anti-patterns.html"/>
    <id>https://blog.appsignal.com/2021/02/10/ruby-on-rails-view-patterns-and-anti-patterns.html</id>
    <published>2021-02-10T00:00:00+00:00</published>
    <updated>2021-02-10T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Rails views are sometimes amazing and fast, and at other times, they can have all sorts of issues. If you want to increase confidence over how you handle your views, then this blog post is for you.</summary>
    <content type="html">&lt;p&gt;Welcome back to the third installment of the Ruby on Rails Patterns and Anti-Patterns series. In the previous posts, we covered patterns and
anti-patterns in general as well as in relation to Rails Models. In this post, we
are going to go over some patterns and anti-patterns associated with Rails views.&lt;/p&gt;
&lt;p&gt;Rails views can sometimes work perfectly and be fast, and at other times, they can have all sorts of issues.
If you want to increase confidence over how you handle your views or you
just want to learn more on the topic, then this blog post is
for you. Let&amp;#39;s dive right in.&lt;/p&gt;
&lt;p&gt;As you probably know, the Rails framework follows convention over configuration.
And since Rails is big on the Model-View-Controller (MVC) pattern, the motto
naturally applies to the View code as well. This includes your markup (ERB or
Slim files), JavaScript and CSS files. At first glance, you might think that the View layer
is pretty straightforward and easy, but keep in mind that these
days, there is a mix of technologies living in the View layer.&lt;/p&gt;
&lt;p&gt;We use JavaScript, HTML, and CSS in the view. These three can
lead to confusion and disorganization of code — leading to implementation
that doesn&amp;#39;t make much sense in the long run. Luckily, today we are going to go
through some common problems and solutions with the Rails View layer.&lt;/p&gt;
&lt;h2&gt;Powerlifting Views&lt;/h2&gt;
&lt;p&gt;This is a mistake that doesn&amp;#39;t happen that often, but when it does, it&amp;#39;s an
eyesore. Sometimes, people tend to put the domain logic or querying directly
inside the View. This makes the View layer do the heavy-lifting or
powerlifting. What is interesting is that Rails actually allows this to easily happen.
There is no &amp;#39;safety net&amp;#39; when it comes to this, you are allowed to do
whatever you want in the View layer.&lt;/p&gt;
&lt;p&gt;By definition, the View layer of the MVC pattern should contain presentation
logic. It shouldn&amp;#39;t be bothered with domain logic or with querying data. In Rails,
you get ERB files (Embedded Ruby) that allow you to write Ruby code
that will then get evaluated into HTML. If we consider an example of a
website that lists songs on the index page, then the view logic would be in
the &lt;code&gt;app/views/songs/index.html.erb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To illustrate what &amp;quot;powerlifting&amp;quot; means and what not do to, let&amp;#39;s take a look
at the following example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/songs/index.html.erb

&amp;lt;div class=&amp;quot;songs&amp;quot;&amp;gt;
  &amp;lt;% Song.where(published: true).order(:title) do |song| %&amp;gt;
    &amp;lt;section id=&amp;quot;song_&amp;lt;%= song.id %&amp;gt;&amp;quot;&amp;gt;
      &amp;lt;span&amp;gt;&amp;lt;%= song.title %&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span&amp;gt;&amp;lt;%= song.description %&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;a href=&amp;quot;&amp;lt;%= song.download_url %&amp;gt;&amp;quot;&amp;gt;Download&amp;lt;/a&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A huge anti-pattern here is the fetching of songs right in the markup. The
responsibility of fetching the data should be delegated to the controller or a
service that is being called from the controller. I sometimes see people prepare
some data in the controller and later fetch more data in the views. This
is bad design and it makes your website slower because you are
stressing your database with queries more often.&lt;/p&gt;
&lt;p&gt;What you should do instead is to expose a &lt;code&gt;@songs&lt;/code&gt; instance variable from the
controller action and call that in the markup, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class SongsController &amp;lt; ApplicationController
  ...

  def index
    @songs = Song.all.where(published: true).order(:title)
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/songs/index.html.erb

&amp;lt;div class=&amp;quot;songs&amp;quot;&amp;gt;
  &amp;lt;% @songs.each do |song| %&amp;gt;
    &amp;lt;section id=&amp;quot;song_&amp;lt;%= song.id %&amp;gt;&amp;quot;&amp;gt;
      &amp;lt;span&amp;gt;&amp;lt;%= song.title %&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span&amp;gt;&amp;lt;%= song.description %&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;a href=&amp;quot;&amp;lt;%= song.download_url %&amp;gt;&amp;quot;&amp;gt;Download&amp;lt;/a&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These examples are far from perfect. If you want to keep your controller code
more readable and avoid SQL Pasta, I urge you to check out the
&lt;a href=&quot;/2020/11/18/rails-model-patterns-and-anti-patterns.html&quot;&gt;previous blog post&lt;/a&gt;.
Also, leaving out the logic in the View layer increases the
chances that other people will try to build their solutions off of it.&lt;/p&gt;
&lt;h2&gt;Make Use of What Rails Gives You&lt;/h2&gt;
&lt;p&gt;We will keep it short here. Ruby on Rails as a framework comes with a lot of
neat helpers, especially inside the view. These nifty little helpers allow you
to build your View layer quickly and effortlessly. As a beginner user of Rails,
you might be tempted to write the full HTML inside your ERb files like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/songs/new.html.erb

&amp;lt;form action=&amp;quot;/songs&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;field&amp;quot;&amp;gt;
    &amp;lt;label for=&amp;quot;song_title&amp;quot;&amp;gt;Title&amp;lt;/label&amp;gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;song[title]&amp;quot; id=&amp;quot;song_title&amp;quot;&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class=&amp;quot;field&amp;quot;&amp;gt;
    &amp;lt;label for=&amp;quot;song_description&amp;quot;&amp;gt;Description&amp;lt;/label&amp;gt;
    &amp;lt;textarea name=&amp;quot;song[description]&amp;quot; id=&amp;quot;song_description&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class=&amp;quot;field&amp;quot;&amp;gt;
    &amp;lt;label for=&amp;quot;song_download_url&amp;quot;&amp;gt;Download URL&amp;lt;/label&amp;gt;
    &amp;lt;textarea name=&amp;quot;song[download_url]&amp;quot; id=&amp;quot;song_download_url&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;input type=&amp;quot;submit&amp;quot; name=&amp;quot;commit&amp;quot; value=&amp;quot;Create Song&amp;quot;&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;With this HTML, you should get a nice form for a new song as seen in the screenshot below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-02/new-song-form.png&quot; alt=&quot;New song form&quot;/&gt;&lt;/p&gt;
&lt;p&gt;But, with Rails, you don&amp;#39;t need and you shouldn&amp;#39;t write plain HTML like that
since Rails has your back right there. You can use the &lt;code&gt;form_with&lt;/code&gt; view helper that
will generate the HTML for you. &lt;code&gt;form_with&lt;/code&gt; was introduced in Rails 5.1 and it
is there to replace &lt;code&gt;form_tag&lt;/code&gt; and &lt;code&gt;form_for&lt;/code&gt; that might be familiar to some
folk. Let&amp;#39;s see how &lt;code&gt;form_with&lt;/code&gt; can relieve us from writing extra code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= form_with(model: song, local: true) do |form| %&amp;gt;
  &amp;lt;div class=&amp;quot;field&amp;quot;&amp;gt;
    &amp;lt;%= form.label :title %&amp;gt;
    &amp;lt;%= form.text_field :title %&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class=&amp;quot;field&amp;quot;&amp;gt;
    &amp;lt;%= form.label :description %&amp;gt;
    &amp;lt;%= form.text_area :description %&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class=&amp;quot;field&amp;quot;&amp;gt;
    &amp;lt;%= form.label :download_url do %&amp;gt;
      Download URL
    &amp;lt;% end %&amp;gt;
    &amp;lt;%= form.text_area :download_url %&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;%= form.submit %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides generating HTML for us, &lt;code&gt;form_with&lt;/code&gt; also generates an authenticity
token that prevents CSRF attacks. So in almost all cases, you are better off
using designated helpers since they might play well with the Rails framework.
If you tried to submit a plain HTML form, it will fail because there was no
valid authenticity token submitted with the request.&lt;/p&gt;
&lt;p&gt;Besides &lt;code&gt;form_with&lt;/code&gt;, &lt;code&gt;label&lt;/code&gt;, &lt;code&gt;text_area&lt;/code&gt;, and &lt;code&gt;submit&lt;/code&gt; helpers, there are a
bunch more of these view helpers that come out-of-the-box with Rails. They are
there to make your lives easier and you should get to know them better. One of the &amp;quot;all-stars&amp;quot; is definitely &lt;code&gt;link_to&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= link_to &amp;quot;Songs&amp;quot;, songs_path %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which will generate the following HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;a href=&amp;quot;/songs&amp;quot;&amp;gt;Songs&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I won&amp;#39;t go into much detail on each helper, since this post will be too
long and going through all of them is not part of today&amp;#39;s topic. I suggest you go
through
&lt;a href=&quot;https://guides.rubyonrails.org/action_view_helpers.html&quot;&gt;Rails Action View helpers guide&lt;/a&gt;
and pick what you need for your website.&lt;/p&gt;
&lt;h2&gt;Reusing and Organizing View Code&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s imagine the perfect web application. In the perfect use-case, there are
no if-else statements, just pure code that takes data from the controller and
puts it between HTML tags. That kind of application exists maybe in hackathons
and dreams, but real-world applications have a bunch of branches and
conditions when rendering views.&lt;/p&gt;
&lt;p&gt;What should you do when the logic for showing parts of a page gets too complex? Where do you go from
there? A general answer would be to perhaps reach for a modern JavaScript
library or framework and build something complex. But, since this post
is about Rails Views, let&amp;#39;s look at the options we have inside them.&lt;/p&gt;
&lt;h2&gt;After-Market (Custom) Helpers&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s say you want to show a call-to-action (CTA) button below a song. But,
there is a catch — a Song can either have a download URL or, for whatever reason,
it can be missing. We might be tempted to code something similar to the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;app/views/songs/show.html.erb

...

&amp;lt;div class=&amp;quot;song-cta&amp;quot;&amp;gt;
  &amp;lt;% if @song.download_url %&amp;gt;
    &amp;lt;%= link_to &amp;quot;Download&amp;quot;, download_url %&amp;gt;
  &amp;lt;% else %&amp;gt;
    &amp;lt;%= link_to &amp;quot;Subscribe to artists updates&amp;quot;,
                artist_updates_path(@song.artist) %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we look at the example above as an isolated presentational logic, it doesn&amp;#39;t
look too bad, right? But, if there are more of these conditional
renders, then the code becomes less readable. It also increases the chances
of something, somewhere not getting rendered properly, especially if there
are more conditions.&lt;/p&gt;
&lt;p&gt;One way to fight these is to extract them to a separate helper. Luckily, Rails
provides us a way to easily write custom helpers. In the &lt;code&gt;app/helpers&lt;/code&gt; we can create a &lt;code&gt;SongsHelper&lt;/code&gt;, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module SongsHelper
  def song_cta_link
    content_tag(:div, class: &amp;#39;song-cta&amp;#39;) do
      if @song.download_url
        link_to &amp;quot;Download&amp;quot;, @song.download_url
      else
        link_to &amp;quot;Subscribe to artists updates&amp;quot;,
                artist_updates_path(@song.artist)
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we open up the show page of a song, we will still get the same results.
However, we can make this example a bit better. In the example above, we used an
instance variable &lt;code&gt;@song&lt;/code&gt;. This might not be available if we decide to use this
helper at a place where &lt;code&gt;@song&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;. So to cut off an external dependency in the form of an instance variable, we can pass in an argument to the helper like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module SongsHelper
  def song_cta_link(song)
    content_tag(:div, class: &amp;#39;song-cta&amp;#39;) do
      if song.download_url
        link_to &amp;quot;Download&amp;quot;, song.download_url
      else
        link_to &amp;quot;Subscribe to artists updates&amp;quot;,
                artist_updates_path(song.artist)
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in the view, we can call the helper like below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;app/views/songs/show.html.erb

...

&amp;lt;%= song_cta_link(@song) %&amp;gt;

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, we should get the same results in the view as we did before. The good thing
about using helpers is that you can write tests for them ensuring that no
regression happens regarding them in the future. A con is that they are
globally defined and you have to ensure that helper names are unique across
your app.&lt;/p&gt;
&lt;p&gt;If you are not a big fan of writing Rails custom helpers, you can always opt-in
for a View Model pattern with the
&lt;a href=&quot;https://github.com/drapergem/draper&quot;&gt;Draper gem&lt;/a&gt;.
Or you can roll your own View Model pattern here, it shouldn&amp;#39;t be that
complicated. If you are just starting out with your web app, I suggest starting
slowly by writing custom helpers and if that brings pain, turn to other
solutions.&lt;/p&gt;
&lt;h2&gt;DRY up Your Views&lt;/h2&gt;
&lt;p&gt;What I really liked when I started with Rails was the ability to easily DRY up your
markup that it was almost unbelievable to me. Rails gives you the ability
to create partials — reusable code pieces that you can include anywhere. For
example, if you are rendering songs in multiple places, and you have the same
code across multiple files, it makes sense to create a song partial.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say you show your song as shown below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/songs/show.html.erb

&amp;lt;p id=&amp;quot;notice&amp;quot;&amp;gt;&amp;lt;%= notice %&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;
  &amp;lt;strong&amp;gt;Title:&amp;lt;/strong&amp;gt;
  &amp;lt;%= @song.title %&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;
  &amp;lt;strong&amp;gt;Description:&amp;lt;/strong&amp;gt;
  &amp;lt;%= @song.description %&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;%= song_cta_link %&amp;gt;

&amp;lt;%= link_to &amp;#39;Edit&amp;#39;, edit_song_path(@song) %&amp;gt; |
&amp;lt;%= link_to &amp;#39;Back&amp;#39;, songs_path %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But, you also want to show it on another page with the same markup. Then you can
create a new file with an underscore prefix like &lt;code&gt;app/views/songs/_song.html.erb&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/songs/_song.html.erb

&amp;lt;p&amp;gt;
  &amp;lt;strong&amp;gt;Title:&amp;lt;/strong&amp;gt;
  &amp;lt;%= @song.title %&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;
  &amp;lt;strong&amp;gt;Description:&amp;lt;/strong&amp;gt;
  &amp;lt;%= @song.description %&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;%= song_cta_link(@song) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then wherever you want to include the song partial, you just do the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;...

&amp;lt;%= render &amp;quot;song&amp;quot; %&amp;gt;

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails will do an auto-lookup of whether the &lt;code&gt;_song&lt;/code&gt; partial exists and it will
render it. Similar to an example with custom helpers, it is best if we get rid of the instance variable &lt;code&gt;@song&lt;/code&gt; in our partial.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;
# app/views/songs/_song.html.erb
&amp;lt;p&amp;gt;
  &amp;lt;strong&amp;gt;Title:&amp;lt;/strong&amp;gt;
  &amp;lt;%= song.title %&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;
  &amp;lt;strong&amp;gt;Description:&amp;lt;/strong&amp;gt;
  &amp;lt;%= song.description %&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;%= song_cta_link(song) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we will need to pass in the song variable to the partial, making it more
reusable and suitable to being included in other places.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;...

&amp;lt;%= render &amp;quot;song&amp;quot;, song: @song %&amp;gt;

...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;That&amp;#39;s all folks for this post. To summarize, we went through a few
patterns and anti-patterns that you can come across in the Rails View realm. Here are a few
takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid complex logic in the UI (do not make the View do lots of powerlifting)&lt;/li&gt;
&lt;li&gt;Learn what Rails gives you out-of-the-box in terms of View helpers.&lt;/li&gt;
&lt;li&gt;Structure and reuse your code with custom helpers and partials&lt;/li&gt;
&lt;li&gt;Do not depend on instance variables too much.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the next post, we will cover Rails Controller patterns and anti-patterns
where things can get pretty messy. Stay tuned for that.&lt;/p&gt;
&lt;p&gt;Until the next one, cheers!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>RBS: A New Ruby 3 Typing Language in Action</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/01/27/rbs-the-new-ruby-3-typing-language-in-action.html"/>
    <id>https://blog.appsignal.com/2021/01/27/rbs-the-new-ruby-3-typing-language-in-action.html</id>
    <published>2021-01-27T00:00:00+00:00</published>
    <updated>2021-01-27T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s discover the differences and similarities between Sorbet and the recently released RBS through some practical examples.</summary>
    <content type="html">&lt;p&gt;The long-awaited version 3.0.0 of Ruby has finally been &lt;a href=&quot;https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/&quot;&gt;released&lt;/a&gt;. Along with many great improvements, such as a 3x faster performance boost compared to the previous version, concurrency-parallel experimental features, etc., the Ruby team also introduced a new syntax language for dynamic typing in Ruby: &lt;a href=&quot;https://github.com/ruby/rbs&quot;&gt;RBS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That was something the team &lt;a href=&quot;https://developer.squareup.com/blog/the-state-of-ruby-3-typing/&quot;&gt;had been discussing&lt;/a&gt; for years, based on the success of community-developed tools for static type checking such as &lt;a href=&quot;https://sorbet.org/&quot;&gt;Sorbet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sorbet is a powerful type checker backed by Stripe. It checks your code by both annotating and/or defining &lt;a href=&quot;https://sorbet.org/docs/rbi&quot;&gt;RBI&lt;/a&gt; files. RBI files, in turn, work as interfaces between static and dynamic components providing a &amp;quot;description&amp;quot; of them (constants, ancestors, metaprogramming code, and more).&lt;/p&gt;
&lt;p&gt;So, if Sorbet mostly deals with static checking and RBS was made to address dynamic typing, what&amp;#39;s the difference between them? How will they both coexist? When should I use one instead of the other?&lt;/p&gt;
&lt;p&gt;Those are fairly common questions about the major role of RBS. That&amp;#39;s why we decided to write this piece. To clarify, in practice, why you should consider adopting it based on what it&amp;#39;s capable of. Let&amp;#39;s dive right in!&lt;/p&gt;
&lt;h2&gt;Starting with the Basics&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s start with a clear understanding of the difference between &lt;em&gt;static typing _and&lt;/em&gt; dynamic typing_. Although it&amp;#39;s basic, it is a key concept to grasp in order to understand the role of RBS.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take a code snippet from a statically typed language as a reference:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;➜
String str = &amp;quot;&amp;quot;;
str = 2.4;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s no news that such a language cares for the types of its objects and variables. That being said, code like the one above will throw an error.&lt;/p&gt;
&lt;p&gt;Ruby, like many other languages such as JavaScript, Python, and Objective-C, doesn&amp;#39;t give that much attention to which types you&amp;#39;re targeting for your objects.&lt;/p&gt;
&lt;p&gt;The same code in Ruby will just successfully run, as seen below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;➜  irb
str = &amp;quot;&amp;quot;
str = 2.4
puts str # prints 2.4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is possible because Ruby&amp;#39;s interpreter knows how to &lt;strong&gt;dynamically&lt;/strong&gt; switch from one type to another.&lt;/p&gt;
&lt;p&gt;However, there&amp;#39;s a limit to what the interpreter allows. For example, take the following code change:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;➜  irb
val = &amp;quot;6.0&amp;quot;
result = val + 2.0
puts result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will, in turn, produce the following error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-01/Figure01.png&quot; alt=&quot;Error&quot;/&gt;
&lt;em&gt;Error: no implicit conversion of Float into String&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Running the same code with JavaScript, for instance, would run just fine.&lt;/p&gt;
&lt;p&gt;Moral of the story: Ruby does infer types dynamically, indeed; but, unlike other major dynamic languages, it won&amp;#39;t accept everything. Be aware of that!&lt;/p&gt;
&lt;p&gt;And that&amp;#39;s where type checkers (whether static or dynamic) become useful.&lt;/p&gt;
&lt;h2&gt;RBS vs Sorbet&lt;/h2&gt;
&lt;p&gt;Right, I&amp;#39;ve got your point about the dynamic vs static thing. But, what about Sorbet? Will it get deprecated?&lt;/p&gt;
&lt;p&gt;Not at all. The primary (and perhaps the most important) difference between RBS and Sorbet is that the former is just a language, while the latter is a complete type checker itself.&lt;/p&gt;
&lt;p&gt;The Ruby team asserts RBS&amp;#39; main goal as to &lt;em&gt;describe the structure&lt;/em&gt; of your code. It won&amp;#39;t perform the type checking, but rather, define the structure that type checkers (like Sorbet or any other) can use to, well, type check. The code structure is stored within a new file extension — &lt;em&gt;.rbs&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To check it out, let&amp;#39;s take the following Ruby class as an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Super
    def initialize(val)
      @val = val
    end


    def val?
      @val
    end
end

class Test &amp;lt; Super
  def initialize(val, flag)
    super(val)
    @flag = flag
  end

  def flag?
    @flag
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It represents a simple inheritance in Ruby. The interesting thing to note here is that we can&amp;#39;t guess the types of each attribute used in the class, except for &lt;code&gt;flag&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;flag&lt;/code&gt; comes initialized with a default value, both the developer and a type checker can infer the type to prevent further misuse.&lt;/p&gt;
&lt;p&gt;The following will be a proper representation of the above class in RBS format:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Super
  attr_reader val : untyped

  def initialize : (val: untyped) -&amp;gt; void
end

class Test &amp;lt; Super
  attr_reader flag : bool

  def initialize : (val: untyped, ?flag: bool) -&amp;gt; void
  def flag? : () -&amp;gt; bool
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take some time to digest this. It&amp;#39;s a declaration language, so only signatures may appear in an RBS file. Simple, isn’t it?&lt;/p&gt;
&lt;p&gt;Whether it was autogenerated by a CLI tool (more on that later) or by you, it&amp;#39;s safer to annotate a type as &lt;code&gt;untyped&lt;/code&gt; when it can&amp;#39;t be guessed.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re sure about the type of &lt;code&gt;val&lt;/code&gt;, for example, your RBS mapping could be switched to the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Super
  attr_reader val : Integer

  def initialize : (val: Integer) -&amp;gt; void
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s also important to note that both the Ruby and Sorbet teams were working (and still are) towards the creation and improvement of RBS. It was the Sorbet team&amp;#39;s experience with type checking, for years, that helped the Ruby team to fine-tune a lot of stuff with this project.&lt;/p&gt;
&lt;p&gt;The interoperability between RBS and RBI files is still under development. The goal is for Sorbet and any other checker tools to have an official and centralized basis to follow.&lt;/p&gt;
&lt;h2&gt;The RBS CLI Tool&lt;/h2&gt;
&lt;p&gt;One important consideration the Ruby team had when developing RBS was to ship a CLI tool that could help developers to try it out and learn how to use it. It&amp;#39;s called &lt;em&gt;rbs&lt;/em&gt; and comes by default with Ruby 3. If you still haven&amp;#39;t upgraded your Ruby version, you can add its gem directly to your project, as well:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  gem install rbs
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The command &lt;code&gt;rbs help&lt;/code&gt; will show the command usage along with the available commands.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2021-01/Figure02.png&quot; alt=&quot;List of available commands&quot;/&gt;
&lt;em&gt;List of available commands&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Most of these commands focus on parsing and analyzing the Ruby code structures. For example, the command &lt;code&gt;ancestors&lt;/code&gt; sweeps the hierarchical structure of a given class to check for its ancestors:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;➜  rbs ancestors ::String
::String
::Comparable
::Object
::Kernel
::BasicObject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command &lt;code&gt;methods&lt;/code&gt; displays all the method structures of a given class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;➜  rbs methods ::String
! (public)
!= (public)
!~ (public)
...
Array (private)
Complex (private)
Float (private)
...
autoload? (private)
b (public)
between? (public)
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Want to see a specific method structure? Go for &lt;code&gt;method&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;➜  rbs method ::String split
::String#split
  defined_in: ::String
  implementation: ::String
  accessibility: public
  types:
      (?::Regexp | ::string pattern, ?::int limit) -&amp;gt; ::Array[::String]
    | (?::Regexp | ::string pattern, ?::int limit) { (::String) -&amp;gt; void } -&amp;gt; self
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For those starting with RBS today, the command &lt;code&gt;prototype&lt;/code&gt; can help a lot with scaffolding types for classes that already exist. The command generates prototypes of RBS files.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take the previous &lt;code&gt;Test &amp;lt; Super&lt;/code&gt; inheritance example and save the code into a file called &lt;em&gt;appsignal.rb&lt;/em&gt;. Then, run the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  rbs prototype rb appsignal.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the command allows for &lt;em&gt;rb&lt;/em&gt;, &lt;em&gt;rbi&lt;/em&gt;, and &lt;em&gt;runtime&lt;/em&gt; generators, you need to provide the specific type of file you&amp;#39;re scaffolding right after the &lt;code&gt;prototype&lt;/code&gt; command, followed by the file pathname.&lt;/p&gt;
&lt;p&gt;The following is the result of the execution:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Super
  def initialize: (untyped val) -&amp;gt; untyped

  def val?: () -&amp;gt; untyped
end

class Test &amp;lt; Super
  def initialize: (untyped val, ?flag: bool flag) -&amp;gt; untyped

  def flag?: () -&amp;gt; untyped
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty similar to our first RBS version. As mentioned earlier, the tool marks as &lt;code&gt;untyped&lt;/code&gt; any type that couldn&amp;#39;t be guessed.&lt;/p&gt;
&lt;p&gt;It also counts for method returns. Notice the return type of the &lt;code&gt;flag&lt;/code&gt; definition. As a developer, you&amp;#39;re probably sure that the method always returns a boolean, but due to Ruby’s dynamic nature, the tool is unable to 100% say that it is so.&lt;/p&gt;
&lt;p&gt;And that&amp;#39;s when another Ruby 3 child comes to the rescue: the &lt;a href=&quot;https://github.com/ruby/typeprof&quot;&gt;TypeProf&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The TypeProf Tool&lt;/h2&gt;
&lt;p&gt;TypeProf is a type analysis tool for Ruby that was created on top of some &lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;syntax tree&lt;/a&gt; interpretation.&lt;/p&gt;
&lt;p&gt;Despite still being experimental, it has proved to be very powerful when it comes to understanding what your code is trying to do.&lt;/p&gt;
&lt;p&gt;If you don&amp;#39;t have Ruby 3 yet, simply add the gem to your project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  gem install typeprof
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&amp;#39;s run the same &lt;em&gt;appsignal.rb&lt;/em&gt; file against it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  typeprof appsignal.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Classes
class Super
  @val: untyped
  def initialize: (untyped) -&amp;gt; untyped
  def val?: -&amp;gt; untyped
end

class Test &amp;lt; Super
  @val: untyped
  @flag: true

  def initialize: (untyped, ?flag: true) -&amp;gt; true
  def flag?: -&amp;gt; true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note how the &lt;code&gt;flag&lt;/code&gt; is mapped now. This is only possible because, unlike what the RBS prototype does, the TypeProf scans the method&amp;#39;s body trying to understand what actions are being performed over that specific variable. Since it couldn&amp;#39;t identify any direct change to this variable, TypeProf safely mapped the method return as a boolean.&lt;/p&gt;
&lt;p&gt;Consider, for example, that TypeProf will have access to other classes that instantiate and use the &lt;code&gt;Test&lt;/code&gt; class. With that in hand, it can go even deeper into your code and fine-tune its predictions. Say that the following code snippet is added at the end of the &lt;em&gt;appsignal.rb&lt;/em&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;testSub = Test.new(&amp;quot;My value&amp;quot;, &amp;quot;My value&amp;quot; == &amp;quot;&amp;quot;)
testSup = Super.new(&amp;quot;My value&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that you changed the &lt;code&gt;initialize&lt;/code&gt; method signature to the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def initialize(val, flag)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you re-run the command, this should be the output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Classes
class Super
  @val: String

  def initialize: (String) -&amp;gt; String
  def val?: -&amp;gt; String
end

class Test &amp;lt; Super
  @val: String
  @flag: bool

  def initialize: (String val, bool flag) -&amp;gt; bool
  def flag?: -&amp;gt; bool
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Super cool!&lt;/p&gt;
&lt;p&gt;TypeProf can&amp;#39;t deal with inherited attributes very well. That&amp;#39;s why we&amp;#39;re instantiating a new &lt;code&gt;Super&lt;/code&gt; object. Otherwise, it wouldn&amp;#39;t get that &lt;code&gt;val&lt;/code&gt; is a &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The major pro of TypeProf is its safety. Whenever it&amp;#39;s unable to figure something out for sure, then &lt;code&gt;untyped&lt;/code&gt; will be returned.&lt;/p&gt;
&lt;h2&gt;Partial RBS Specification&lt;/h2&gt;
&lt;p&gt;One important warning from the official docs states that, although TypeProf is very powerful, you should be aware of its limitations regarding what it can and cannot generate in terms of RBS code.&lt;/p&gt;
&lt;p&gt;For example, a common practice among Ruby developers is method overloading in which you invoke different behavior of a method depending on its arguments.&lt;/p&gt;
&lt;p&gt;Consider that a new method &lt;code&gt;spell&lt;/code&gt; is added to the &lt;code&gt;Super&lt;/code&gt; class, which returns an &lt;code&gt;Integer&lt;/code&gt; or a &lt;code&gt;String&lt;/code&gt; based on the parameter type:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def spell(val)
  if val.is_a?(String)
    &amp;quot;&amp;quot;
  else
    0
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;RBS embraces this practice by allowing you to deal with overloading through the &lt;a href=&quot;https://sorbet.org/docs/union-types&quot;&gt;union type&lt;/a&gt; (a value that represents multiple possible types) syntax:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def spell: (String) -&amp;gt; String | (Integer) -&amp;gt; Integer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeProf can&amp;#39;t infer this just by analyzing the method&amp;#39;s body. To help it out, you can manually add such a definition to your RBS file and TypeProf will always check there first for instructions.&lt;/p&gt;
&lt;p&gt;For this, you must add the RBS file path at the end of the command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;typeprof appsignal.rb appsignal.rbs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See below the new output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Super
  ...
  def spell: (untyped val) -&amp;gt; (Integer | String)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Plus, we can also verify the real types during runtime via &lt;code&gt;Kernel#p&lt;/code&gt; to test if the overloading is working by adding the next two lines to the end of the &lt;em&gt;appsignal.rb&lt;/em&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;p testSup.spell(42)
p testSup.spell(&amp;quot;str&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should be the output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Revealed types
#  appsignal.rb:11 #=&amp;gt; Integer
#  appsignal.rb:12 #=&amp;gt; String

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to refer to the &lt;a href=&quot;https://github.com/ruby/typeprof/blob/master/doc/doc.md&quot;&gt;official docs&lt;/a&gt; for more information, especially the section concerning TypeProf &lt;a href=&quot;https://github.com/ruby/typeprof/blob/master/doc/doc.md#limitations&quot;&gt;limitations&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Duck Typing&lt;/h2&gt;
&lt;p&gt;You&amp;#39;ve heard of that before. If a Ruby object does everything a duck does, then it is a duck.&lt;/p&gt;
&lt;p&gt;As we&amp;#39;ve seen, Ruby doesn&amp;#39;t care about what your objects are meant to be. Types can change dynamically as well as object references.&lt;/p&gt;
&lt;p&gt;Although helpful, duck typing can be tricky. Let&amp;#39;s see an example.&lt;/p&gt;
&lt;p&gt;Suppose that, from now on, the &lt;code&gt;val&lt;/code&gt; attribute you&amp;#39;ve declared for the &lt;code&gt;Super&lt;/code&gt; class, which is a &lt;code&gt;String&lt;/code&gt;, must always be convertible to an integer.&lt;/p&gt;
&lt;p&gt;Rather than trusting that developers will always guarantee the conversion (perhaps, throwing an error otherwise), you can create an &lt;strong&gt;interface&lt;/strong&gt; stating that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;interface _IntegerConvertible
   def to_int: () -&amp;gt; Integer
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Interface types provide one or more methods that are detached from concrete classes and modules. This way, when you want a certain type to be passed on to the Super instantiation, you can simply do the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Super
  attr_reader val : _IntegerConvertible

  def initialize : (val: _IntegerConvertible) -&amp;gt; void
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The concrete class or module that implements this interface will have to make sure the proper validation is done.&lt;/p&gt;
&lt;h2&gt;Metaprogramming&lt;/h2&gt;
&lt;p&gt;Perhaps one of the most dynamic features of Ruby is the capability of creating code that creates code by itself during runtime. That&amp;#39;s metaprogramming.&lt;/p&gt;
&lt;p&gt;Because of the uncertain nature of things, the RBS CLI tool isn&amp;#39;t able to generate RBS out of metaprogramming code.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s take the following snippet as an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Test
    define_method :multiply do |*args|
        args.inject(1, :*)
    end
end

p Test.new.multiply(2, 3, 5)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This class defines a method called &lt;code&gt;multiply&lt;/code&gt; at runtime and instructs it to inject the arguments and multiply each one with the previous result.&lt;/p&gt;
&lt;p&gt;Once you run the RBS &lt;code&gt;prototype&lt;/code&gt; command, this should be the output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Test
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on the complexity of your metaprogramming code, TypeProf will still try its best to extract something from it. But it&amp;#39;s not always guaranteed.&lt;/p&gt;
&lt;p&gt;Remember, you can always add your own type mappings to the RBS file and TypeProf will obey them in advance. That&amp;#39;s also valid for metaprogramming.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s also important to keep updated with the latest repository changes since the team is constantly releasing new features, which may well include updates on metaprogramming.&lt;/p&gt;
&lt;p&gt;That being said, if your codebase includes some type of metaprogramming, be careful with these tools. Don&amp;#39;t use them blindly!&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;There are plenty more details about what we&amp;#39;ve discussed so far, as well as edge use cases for both &lt;a href=&quot;https://github.com/ruby/rbs/blob/master/docs/syntax.md&quot;&gt;RBS&lt;/a&gt; and &lt;a href=&quot;https://github.com/ruby/typeprof/blob/master/doc/doc.md&quot;&gt;TypeProf&lt;/a&gt; that you should be aware of.&lt;/p&gt;
&lt;p&gt;So, make sure to refer to the official docs for more on that.&lt;/p&gt;
&lt;p&gt;RBS is still so fresh but has already caused a huge impact on Rubyists that are used to type checking their codebases with other tools.&lt;/p&gt;
&lt;p&gt;What about you? Have you tried it out? What are your thoughts on RBS?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Use Mixins and Modules in Your Ruby on Rails Application</title>
    <link rel="alternate" href="https://blog.appsignal.com/2021/01/13/using-mixins-and-modules-in-your-ruby-on-rails-application.html"/>
    <id>https://blog.appsignal.com/2021/01/13/using-mixins-and-modules-in-your-ruby-on-rails-application.html</id>
    <published>2021-01-13T00:00:00+00:00</published>
    <updated>2021-01-13T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out how to create and mix modules into other classes and discover what are the benefits from using them in a Ruby on Rails application.</summary>
    <content type="html">&lt;p&gt;Modules and mixins are, without doubt, great resources that make Ruby so attractive. They give the application the ability to share the code that can be used with ease in other places. It also helps us organize our code by grouping functionalities and concerns, which improves the readability and maintainability of our code.&lt;/p&gt;
&lt;p&gt;In this article, we will go through the concepts behind modules and mixins. We&amp;#39;ll learn how to create and mix modules into other classes and discuss the benefits of using them in a Ruby on Rails application.&lt;/p&gt;
&lt;p&gt;I hope you&amp;#39;ll enjoy the journey!&lt;/p&gt;
&lt;h2&gt;What are Modules&lt;/h2&gt;
&lt;p&gt;Modules are one of the shiniest resources of Ruby because they provide two great benefits: we can create namespaces to prevent name clashes and we can use them as mixins to share code across the application.&lt;/p&gt;
&lt;p&gt;In structural terms, a module is pretty similar to any Ruby class. In fact, for Ruby, a &lt;code&gt;Class&lt;/code&gt; is a &lt;code&gt;Module&lt;/code&gt;, as we can see below in an &lt;code&gt;irb&lt;/code&gt; console:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;gt; Class.is_a?(Module)
 =&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similar to classes, with modules we also group methods and constants and share code. However, there are a few differences between a module and a plain Ruby class:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We start the definition with the &lt;code&gt;module&lt;/code&gt; keyword instead of &lt;code&gt;class&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;We can&amp;#39;t instantiate modules, so no objects can be created from it;&lt;/li&gt;
&lt;li&gt;We can&amp;#39;t inherit from modules, so we use them as mixins instead;&lt;/li&gt;
&lt;li&gt;Modules are standalone code, so there&amp;#39;s no inheritance hierarchy of modules;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Modules are great places to have services, concerns, constants and any other code that, by having the same responsibility they should stay together.&lt;/p&gt;
&lt;p&gt;This is how a module should look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/modules/invoice_creator.rb
module InvoiceCreator
  TAX_FEE = 0.5

  def self.generate
    puts &amp;quot;Don&amp;#39;t worry! I&amp;#39;ll generate the invoice for you at #{TAX_FEE}%&amp;quot;
  end

  def invoice_total
    puts &amp;quot;I&amp;#39;ll return the invoice total&amp;quot;
    1000
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we can observe that a module can provide two kinds of methods: module methods and instance methods.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;self.generate&lt;/code&gt; is a module method, which means that we can use it without having to include (or extend) the module in any other object. This is very common when we are creating service objects, for example. We can call our module method like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;2.5.3 :006 &amp;gt; InvoiceCreator.generate
Don&amp;#39;t worry! I&amp;#39;ll generate the invoice for you
=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;invoice_total&lt;/code&gt; is an instance method and to be able to use it we need to include the module to a class, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/invoice.rb
class Invoice &amp;lt; ApplicationRecord
  include InvoiceCreator # This includes our module in the Invoice class

  def calculate_tax
    total = invoice_total # included method from our module
    tax = total * InvoiceCreator::TAX_FEE
    puts &amp;quot;This is the invoice tax: #{tax}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;All instance methods from &lt;code&gt;InvoiceCreator&lt;/code&gt; becomes available to the &lt;code&gt;Invoice&lt;/code&gt; instances, so we can call the &lt;code&gt;calculate_tax&lt;/code&gt; method pretty easily:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;2.5.3 :008 &amp;gt; Invoice.new.calculate_tax
&amp;quot;I&amp;#39;ll return the invoice total&amp;quot;
&amp;quot;This is the invoice tax: 500.0&amp;quot;
 =&amp;gt; nil
2.5.3 :009 &amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition, inside the &lt;code&gt;calculate_tax&lt;/code&gt; you notice that we are using a constant that was defined inside our module. As I mentioned before, modules are great constant keepers!&lt;/p&gt;
&lt;p&gt;Imagine now a scenario where we need to have two kinds of &lt;code&gt;InvoiceCreator&lt;/code&gt; to generate totally different invoices for suppliers and customers. We would end up having a name classing, and to avoid this, we make use of the other great benefit of modules: namespaces. Let&amp;#39;s take a look in the next section.&lt;/p&gt;
&lt;h2&gt;Namespaces Everywhere&lt;/h2&gt;
&lt;p&gt;Namespaces can be defined as a way to organize our code when we want to create a local context for a given functionality, which is what we need for the scenario I just described: a different context for the customer&amp;#39;s invoice creator and another one for the supplier&amp;#39;s invoice creator.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get back to our code. We&amp;#39;ll need to create two different modules:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/modules/customer/invoice_creator.rb
module Customer
  module InvoiceCreator
    def self.generate
      puts &amp;quot;Don&amp;#39;t worry! I&amp;#39;ll generate the customer invoice for you&amp;quot;
    end
  end
end

# lib/modules/supplier/invoice_creator.rb
module Supplier
  module InvoiceCreator
    def self.generate
      puts &amp;quot;Don&amp;#39;t worry! I&amp;#39;ll generate the supplier invoice for you&amp;quot;
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can use &lt;code&gt;Customer::InvoiceCreator&lt;/code&gt; or &lt;code&gt;Supplier::InvoiceCreator&lt;/code&gt; where we need it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;2.5.3 :014 &amp;gt; Customer::InvoiceCreator.generate
Don&amp;#39;t worry! I&amp;#39;ll generate the customer invoice for you
 =&amp;gt; nil
2.5.3 :015 &amp;gt; Supplier::InvoiceCreator.generate
Don&amp;#39;t worry! I&amp;#39;ll generate the supplier invoice for you
 =&amp;gt; nil
2.5.3 :016 &amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That way each specific code will be wrapped up inside its own module, respecting the &lt;a href=&quot;https://deviq.com/separation-of-concerns/&quot;&gt;separation of concerns&lt;/a&gt; principle. Besides that, namespacing everywhere is also a great way to keep our code well organized.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s see now how we can make use of the other benefits of Ruby modules: mixins.&lt;/p&gt;
&lt;h2&gt;The Magic of Using Mixins&lt;/h2&gt;
&lt;p&gt;As you might already know, one characteristic of Ruby is that it implements the single inheritance mechanism, which means that a class can only inherit from one other class. We may often need to inherit from more classes. In Ruby, we can cover that need by using the &lt;a href=&quot;https://thoughtbot.com/blog/reusable-oo-composition-vs-inheritance&quot;&gt;composition over inheritance&lt;/a&gt; pattern.&lt;/p&gt;
&lt;p&gt;This is doable by using the mixins. When we mix in a piece of code in another Ruby class we are adding to this class more behavior without using inheritance and this is amazing. Therefore, in Ruby, mixins are modules that we include in classes where they are needed. With that we gain by keeping our code clean and the responsibilities separated, as they should be.&lt;/p&gt;
&lt;p&gt;For example, let&amp;#39;s say that our &lt;code&gt;InvoiceCreator&lt;/code&gt; module is a service that needs to use functionalities provided by several other modules, such as &lt;code&gt;InvoiceCalculator&lt;/code&gt;, &lt;code&gt;InvoiceRenderer&lt;/code&gt; and &lt;code&gt;InvoiceSender&lt;/code&gt;, they will be necessary to fulfill the invoice creation process.&lt;/p&gt;
&lt;p&gt;We can achieve this by including a chain of modules as mixins in our code, so we can use the methods directly, like the example below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/modules/invoice_calculator.rb
module InvoiceCalculator
  def calculate_items_total
    puts &amp;quot;imagine some math here&amp;quot;
  end
end

# lib/modules/invoice_renderer.rb
module InvoiceRenderer
  def generate_invoice_pdf
    puts &amp;quot;imagine that we are using some PDF generation magic here&amp;quot;
  end
end

# lib/modules/invoice_sender.rb
module InvoiceSender
  def send_invoice
    puts &amp;quot;imagine your favorite mail service being used here&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we can&amp;#39;t make the &lt;code&gt;InvoiceCreator&lt;/code&gt; inherit from all the other three modules, we are including them instead. This way the &lt;code&gt;InvoiceCreator&lt;/code&gt; includes all the methods from the other Invoice modules, and we can call these in any class/module the &lt;code&gt;InvoiceCreator&lt;/code&gt; module gets included in. Be careful though! If any of the modules have methods with the same name, they will overwrite each other.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# lib/modules/customer/invoice_creator.rb
module Customer
  module InvoiceCreator
    include InvoiceCalculator
    include InvoiceRenderer
    include InvoiceSender

    def generate_invoice
      calculate_items_total # from InvoiceCalculator
      generate_invoice_pdf # from InvoiceRenderer
      send_invoice # from InvoiceSender
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can call our service methods anywhere we include it by doing this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/invoice.rb
class Invoice &amp;lt; ApplicationRecord
  include Customer::InvoiceCreator

  def send_invoice_to_customer
    puts &amp;quot;Don&amp;#39;t worry! I&amp;#39;ll generate the customer invoice for you&amp;quot;
    generate_invoice
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will be the result, calling the methods from the included Invoice modules through the &lt;code&gt;InvoiceCreator&lt;/code&gt; module:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;2.5.3 :051 &amp;gt; Invoice.new.generate_invoice
Don&amp;#39;t worry! I&amp;#39;ll generate the supplier invoice for you
imagine some math here
imagine that we are using some PDF generation magic here
imagine your favorite mail service being used here
 =&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that this is how we use composition over inheritance principle. This principle stands that you should prefer to use composition whenever you can. In composition, we create classes that are responsible to provide specific functionalities to others, which is exactly what we are doing.&lt;/p&gt;
&lt;h2&gt;That&amp;#39;s all Folks!&lt;/h2&gt;
&lt;p&gt;As Ruby developers, we love to use its facilities and syntax sugar when planning, writing, maintaining and refactoring our code. I hope that through this article you can appreciate why modules are such a great resource to help improve the readability of our code, to keep things with only one responsibility and to keep our codebase clean and easy to maintain. And also that we can use the mixins magic to include modules in our code in cases where we would need to inherit from multiple sources.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll keep talking about the greatest resources of Ruby and Rails here, so stay tuned!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Top 5 Ruby Blog Posts in 2020 from AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/12/16/top-5-ruby-posts-in-2020-from-appsignal.html"/>
    <id>https://blog.appsignal.com/2020/12/16/top-5-ruby-posts-in-2020-from-appsignal.html</id>
    <published>2020-12-16T00:00:00+00:00</published>
    <updated>2020-12-16T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Check out the most appreciated Ruby posts on our blog in 2020.</summary>
    <content type="html">&lt;p&gt;There&amp;#39;s no doubt that 2020 is a year we&amp;#39;re all looking forward to leave behind. Let&amp;#39;s grab a stroopwafel and think a bit about nice things and the people who spent their time sharing their knowledge with you, even in a year like this.&lt;/p&gt;
&lt;p&gt;For today&amp;#39;s post, we&amp;#39;ve compiled a Ruby list with top 5 posts that received the most attention from you. Let&amp;#39;s hit it off with &lt;a href=&quot;https://www.youtube.com/watch?v=cCqEyJc-wdk&quot;&gt;some nice tunes in the background&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Top 5 Ruby Blog Posts in 2020 💎&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;/2020/06/17/using-service-objects-in-ruby-on-rails.html&quot;&gt;Using Service Objects in Ruby on Rails&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There’s a lot of functionality that your app needs to handle, but that logic doesn’t necessarily belong in the controller or even the model. Some examples include checking out with a cart, registering for the site, or starting a subscription. This post will show you what service objects are and why you should use them in cases like these.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;/2020/01/22/rails-is-fast-optimize-your-view-performance.html&quot;&gt;Rails is Fast: Optimize Your View Performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this post, we’ll look into tried and true methods of improving Rails view performance. Specifically, we will focus on database efficiency, view manipulation, and caching.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;/2020/03/04/building-a-rails-app-with-multiple-subdomains.html&quot;&gt;Building a Rails App With Multiple Subdomains&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Learn how to build a Rails app that can support multiple subdomains. Let’s assume that we have a gaming website &lt;code&gt;funkygames.co&lt;/code&gt; and we want to support multiple subdomains such as &lt;code&gt;app.funkygames.co&lt;/code&gt;, &lt;code&gt;api.funkygames.co&lt;/code&gt;, and &lt;code&gt;dev.funkygames.co&lt;/code&gt; with a single Rails application.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;&lt;a href=&quot;/2020/09/16/rails-concers-to-concern-or-not-to-concern.html&quot;&gt;Rails Concerns: To Concern Or Not To Concern&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you’ve ever used Ruby on Rails, you’ve probably come across the concept of concerns. Whenever you jumpstart a new Rails project, you get a directory &lt;code&gt;app/controllers/concerns&lt;/code&gt; and &lt;code&gt;app/models/concerns&lt;/code&gt;. But what are concerns? And why do people from the Rails community sometimes talk badly about them?&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;/2020/08/05/introduction-to-ruby-on-rails-patterns-and-anti-patterns.html&quot;&gt;Introduction to Ruby on Rails Patterns and Anti-patterns&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is the first post in &lt;a href=&quot;/category/rails-patterns-and-anti-patterns.html&quot;&gt;our series about Ruby on Rails Patterns and Anti-patterns&lt;/a&gt;. In each of the posts, we’ll take a deep dive into all sorts of patterns you might come across while working with Rails apps.&lt;/p&gt;
&lt;h2&gt;Holiday Season is Approaching 🎊🎉🎄❄️&lt;/h2&gt;
&lt;p&gt;That was all for this roundup of favorite articles of 2020! The whole AppSignal team wishes you all the best for the coming year, with little errors and many amazing insights ☃️&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. Don&amp;#39;t forget to subscribe to our &lt;a href=&quot;/ruby-magic&quot;&gt;Ruby Magic&lt;/a&gt; newsletter!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Setting Up AppSignal Monitoring for a Ruby on Rails Application</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/12/10/setting-up-appsignal-monitoring-for-a-ruby-on-rails-application.html"/>
    <id>https://blog.appsignal.com/2020/12/10/setting-up-appsignal-monitoring-for-a-ruby-on-rails-application.html</id>
    <published>2020-12-10T00:00:00+00:00</published>
    <updated>2020-12-10T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out how to integrate AppSignal into a Rails application in our latest video.</summary>
    <content type="html">&lt;p&gt;As we&amp;#39;re doing our best to make monitoring easy, we&amp;#39;ve wanted to make more content that would really &lt;strong&gt;show&lt;/strong&gt; and not just tell you how easy it is to set up monitoring with AppSignal.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve been collaborating with &lt;a href=&quot;https://www.youtube.com/user/leighhalliday&quot;&gt;Leigh Hallday&lt;/a&gt; on making this video for you that will show you everything you need to know about the setup process. We&amp;#39;ll be posting more videos like this on &lt;a href=&quot;https://www.youtube.com/channel/UCaiU0x1JVGBpZPXypwkAGbw&quot;&gt;our YouTube channel&lt;/a&gt;, so you should subscribe to it if you&amp;#39;d like to get notifications about future videos. You know how that works 😉&lt;/p&gt;
&lt;h2&gt;What&amp;#39;s In the Video&lt;/h2&gt;
&lt;p&gt;In this video, Leigh will show you how easy it is to integrate AppSignal into a Rails application. He covers installation and setup, how to troubleshoot and fix performance issues, specifically showing a common use-case with N+1 queries.&lt;/p&gt;
&lt;p&gt;The N+1 query anti-pattern happens when a query is executed for every single result of a previous query. The query count is N + 1, where N is the number of queries for every result of the initial query. Now if that initial query has one result, then N+1 = 2. If it has 1000 results, N+1 = 1001 queries 🙀. It&amp;#39;s easy to spot them, Leigh will show you how.&lt;/p&gt;
&lt;p&gt;He&amp;#39;ll then show you AppSignal&amp;#39;s Sidekiq integration along with its magic dashboard, which can really help pinpoint performance issues. The whole process is automated as Sidekiq is detected during setup, and things like throughput, duration per job and queue length are plotted on their own graphs. That way you can see when peak, and set up alerting and fix issues with as little work as possible.&lt;/p&gt;
&lt;iframe
  width=&quot;560&quot;
  height=&quot;315&quot;
  src=&quot;https://www.youtube.com/embed/hzOxE-ii9Do&quot;
  frameborder=&quot;0&quot;
  allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
  allowfullscreen
&gt;&lt;/iframe&gt;

&lt;br /&gt;

&lt;h2&gt;Monitoring Made Easy And Sweet 🍪&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed the video! If you&amp;#39;d like us to cover more topics in this format feel free to &lt;a href=&quot;mailto:support@appsignal.com&quot;&gt;reach out and let us know&lt;/a&gt;. We can promise you a free box of &lt;a href=&quot;https://www.appsignal.com/waffles&quot;&gt;stroopwafels&lt;/a&gt; if your suggestion gets accepted.&lt;/p&gt;
&lt;p&gt;If you haven&amp;#39;t used AppSignal before, now you know how we save you time by making monitoring easier. You can &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;try us out&lt;/a&gt; for 30 days free of charge 😀&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Building a Multi-tenant Ruby on Rails App With Subdomains</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/12/02/building-a-multi-tenant-ruby-on-rails-app-with-subdomains.html"/>
    <id>https://blog.appsignal.com/2020/12/02/building-a-multi-tenant-ruby-on-rails-app-with-subdomains.html</id>
    <published>2020-12-02T00:00:00+00:00</published>
    <updated>2020-12-02T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover different approaches to multitenancy and build a simple app with multiple tenants where each tenant will have its subdomain.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;This post was updated on 4 August 2023 with code updates and to reference the latest version of Rails, Rails 7.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;According to a definition of multitenancy, when an app serves multiple tenants, it means that there are a few groups of users who share common access to the software instance. An excellent example of an app that supports multitenancy is the Jira platform, where each company has its subdomain to access the software, for example, &lt;code&gt;mycompany.atlassian.net&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this article, I’m going to familiarize you with both theoretical and practical aspects of multitenancy. After discussing a few popular types of approaches to implement support for multiple tenants in your application, I will show you how you can implement two of them in your Rails application. We will build together a simple app with multiple tenants where each tenant will have its subdomain.&lt;/p&gt;
&lt;p&gt;After reading this article, you will be able to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Discuss different types of multi-tenant apps and tell when the given approach is the right choice,&lt;/li&gt;
&lt;li&gt;Create a Ruby on Rails application with support for multiple tenants, and&lt;/li&gt;
&lt;li&gt;Use custom subdomains for your customers inside the Ruby on Rails application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A base knowledge about Rails is required to follow this article and get most of its benefits.&lt;/p&gt;
&lt;h2&gt;Multitenancy in Theory&lt;/h2&gt;
&lt;p&gt;As I mentioned before, we can say that the app supports multitenancy when it serves a few groups of users who share common access to the application’s features. A good example of such an app is a blogging platform where every user gets a separate subdomain in the main domain. Each blog has its subdomain but shares the same features as other blogs: comments, articles, and administration dashboard.&lt;/p&gt;
&lt;p&gt;Though it might look like multi-tenant apps work similarly on the surface, they can implement different types of multitenancy architecture under the hood. Let’s take a look at the most popular and efficient types of them.&lt;/p&gt;
&lt;h2&gt;Database-row Level&lt;/h2&gt;
&lt;p&gt;If you don’t want to use multiple databases in your application, you can select an approach that operates on one central database. Each table in the database consists of the &lt;code&gt;tenant_id&lt;/code&gt; column used in every query performed inside the application to pull data that belongs to the given tenant.&lt;/p&gt;
&lt;p&gt;The tenant’s setup time in such structure is fast, there are no additional costs, and implementation of this variant is possible everywhere. However, we should be careful as we can lead to a data leak if we forget to include the tenant’s id in the query. Database structure modification would be difficult to maintain for given groups of users, so we should have this disadvantage in mind.&lt;/p&gt;
&lt;h2&gt;Schema Level&lt;/h2&gt;
&lt;p&gt;You can still have one central database but with separate tables and schema for each tenant. In contrast to the database-row level approach, you have to change the search path to the tables when switching between tenants instead of modifying the query. In PostgreSQL, you can use the set &lt;code&gt;search_path&lt;/code&gt; statement, and in MySQL &lt;code&gt;use&lt;/code&gt; statement to switch between schemas.&lt;/p&gt;
&lt;p&gt;Before selecting this approach, ensure that it’s possible to use the mentioned statements on your DB server. Keep also in mind that adding new tenants is relatively slow as you have to create a new schema and create tables in the database.&lt;/p&gt;
&lt;h2&gt;Database Level&lt;/h2&gt;
&lt;p&gt;If you can afford the cost of a new database each time you add a new tenant to your application, this approach might be right for you. This approach is the slowest from all discussed architectures when it comes to adding new tenants.&lt;/p&gt;
&lt;p&gt;It is most likely tough to lead to a data leak because to switch to another tenant, you have to establish a new connection to the database. You can also easily modify the database structure of every group of users if you would like to.&lt;/p&gt;
&lt;h2&gt;Creating an Application Skeleton&lt;/h2&gt;
&lt;p&gt;We will create two separate applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first one will be a Rails app with multi-tenancy supported by one database.&lt;/li&gt;
&lt;li&gt;The second app will be a Rails app with multi-tenancy supported by multiple databases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each app represents a different approach to dealing with numerous tenants, but the configuration phase is the same.
We&amp;#39;ll be using the latest version of Ruby on Rails as of this writing which is &lt;a href=&quot;https://rubyonrails.org/&quot;&gt;Rails version 7.0.6&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also make sure you have the right version of Ruby installed in your system. For this tutorial, we&amp;#39;ll be using Ruby version 3.2.2:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;gem install rails -v 7.0.6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, we are now ready to get started on the first app.&lt;/p&gt;
&lt;h2&gt;Multi-tenant Rails Application with One Database&lt;/h2&gt;
&lt;p&gt;This part of the article will show you how to implement multitenancy in your Rails application. I’m going to use the approach with one database and one schema.&lt;/p&gt;
&lt;p&gt;The plan is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We will create a skeleton for a Rails application using the newest available version of Ruby and Ruby on Rails framework, &lt;a href=&quot;https://rubyonrails.org/&quot;&gt;Rails 7&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We will scaffold some models to have the data we can operate on&lt;/li&gt;
&lt;li&gt;To the functionality we created in the previous step, we will add support for multitenancy by updating the database tables and code in our application&lt;/li&gt;
&lt;li&gt;The last step would be to add support for custom subdomains to allow users to access their tenants easily&lt;/li&gt;
&lt;li&gt;We will explore ideas for some features that you can implement later on your own&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Generating a New Rails App&lt;/h2&gt;
&lt;p&gt;In the terminal, generate a new Rails app with the command below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails new multi_tenant_one_db --javascript=esbuild --css=bulma
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Preparing the Test Data&lt;/h2&gt;
&lt;p&gt;Let’s assume that we are building a blog platform where users can view articles published by multiple authors.&lt;/p&gt;
&lt;p&gt;I will create the author model first, which will store the author’s data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g scaffold author slug:string name:string description:text
rails db:create &amp;amp;&amp;amp; rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now visit &lt;a href=&quot;http://localhost:3000/authors&quot;&gt;http://localhost:3000/authors&lt;/a&gt; address and add a few authors so we can later assign them to the articles.&lt;/p&gt;
&lt;p&gt;The next step is to create articles:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g scaffold article title:string content:text
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Adding Multitenancy Support&lt;/h2&gt;
&lt;p&gt;In the application we are creating, authors are our tenants. Each author should have access only to his articles. As I mentioned before, when we want to implement multitenancy support in one database and one schema, the critical requirement is to add the &lt;code&gt;tenant_id&lt;/code&gt; field to every model that is going to be managed by our tenants.&lt;/p&gt;
&lt;h3&gt;Create proper migrations in the database&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g migration AddTenantIdToArticle tenant_id:integer
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above migration will add the new column to our &lt;code&gt;Article&lt;/code&gt; model. This column will be used in any query to pull access only data assigned to the current tenant.&lt;/p&gt;
&lt;h3&gt;Assign article to the tenant&lt;/h3&gt;
&lt;p&gt;In this step, I’m going to update the code so we can assign the given author to the article and later render articles only for the selected author. Open &lt;code&gt;app/controllers/articles_controller.rb&lt;/code&gt; and add the following changes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ArticlesController &amp;lt; ApplicationController
 before_action :set_article, only: [:show, :edit, :update, :destroy]
 before_action :set_authors, only: [:edit, :update, :new, :create]

 # ...

 private
   # Only allow a list of trusted parameters through.
   def article_params
     params.require(:article).permit(:title, :content, :tenant_id)
   end

   def set_authors
     @authors = Author.all
   end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our view we can now use the &lt;code&gt;@authors&lt;/code&gt; variable which contains the collection of authors added in our app. We can now add the select field that will contain authors’ names and assign a proper &lt;code&gt;tenant_id&lt;/code&gt;. Open &lt;code&gt;app/views/articles/_form.html.erb&lt;/code&gt; and add the following section:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;
&amp;lt;div class=&amp;quot;field&amp;quot;&amp;gt;
  &amp;lt;label class=&amp;quot;label&amp;quot;&amp;gt;Author&amp;lt;/label&amp;gt;
  &amp;lt;div class=&amp;quot;control&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;select&amp;quot;&amp;gt;
      &amp;lt;%= form.select :tenant_id, options_from_collection_for_select(@authors, &amp;#39;id&amp;#39;, &amp;#39;name&amp;#39;, article.tenant_id), include_blank: true %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go ahead and create a few authors and then some articles to later render articles assigned only to the given author.&lt;/p&gt;
&lt;h2&gt;Adding Support for Custom Subdomains&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s assume we&amp;#39;ve created an author with the name John Doe and set the slug value to &lt;code&gt;johndoe&lt;/code&gt;. Our goal is to visit &lt;a href=&quot;http://johndoe.localhost:3000/&quot;&gt;http://johndoe.localhost:3000/&lt;/a&gt; address and see the data related only to the given tenant, John Doe, in this case.&lt;/p&gt;
&lt;h3&gt;Subdomain configuration&lt;/h3&gt;
&lt;p&gt;We would like to manage and visit articles when the tenant is set. We can achieve this by updating the &lt;code&gt;config/routes.rb&lt;/code&gt; file and wrapping the definition of the articles resource into the constraints block:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;
Rails.application.routes.draw do
 constraints subdomain: /.*/ do
   resources :articles
 end

 resources :authors
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, Rails set the top-level domain length to 1, but we want to use localhost to set this setting to 0. We can do this by adding the following line to the file &lt;code&gt;config/environments/development.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;config.action_dispatch.tld_length = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Making subdomains work with the multitenancy&lt;/h3&gt;
&lt;p&gt;Now, it’s possible to assign any author to the article. It shouldn’t be possible when the subdomain is used. We have to alter the behavior of &lt;code&gt;ArticlesController&lt;/code&gt;, and instead of setting all authors, we have to set the author for which the subdomain was requested:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;
class ArticlesController &amp;lt; ApplicationController
 before_action :set_author
 before_action :set_article, only: [:show, :edit, :update, :destroy]

 def index
   @articles = Article.where(tenant_id: @author.id)
 end

 # ...

 private
   def set_article
     @article = Article.find_by!(id: params[:id], tenant_id: @author.id)
   end

   # ...

   def set_author
     @author = Author.find_by!(slug: request.subdomain)
   end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I made a few changes to the controller:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instead of the &lt;code&gt;set_authors&lt;/code&gt; method, I defined the &lt;code&gt;set_author&lt;/code&gt; method, which set the author requested via the subdomain. It is crucial to call this method in &lt;code&gt;before_filter&lt;/code&gt; before the &lt;code&gt;set_article&lt;/code&gt; is called. The author has to be assigned before we attempt to set the article.&lt;/li&gt;
&lt;li&gt;I updated the &lt;code&gt;set_article&lt;/code&gt; method to look for the article with the given id and the assigned author. We don’t want to render articles created by Tom while our current tenant is John.&lt;/li&gt;
&lt;li&gt;I updated the index action to select only articles assigned to our current tenant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you work on a multitenant app and use one database with one schema, you always have to remember to scope any query by &lt;code&gt;tenant_id&lt;/code&gt; column; otherwise, you will provide data that is not assigned to the requested tenant.&lt;/p&gt;
&lt;p&gt;The controller is updated, so the next step is to update the form view. Open &lt;code&gt;app/views/articles/_form.html.erb&lt;/code&gt; file and replace the previous section with the simple hidden field:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;lt;%= form.hidden_field :tenant_id, value: @author.id %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before those changes, the user was able to select the author of the article using the form. The user can choose the author for all actions by using a given subdomain in the website address.&lt;/p&gt;
&lt;p&gt;You can now test the code we created. Create a new author, for example, with the name John Doe and slug &lt;code&gt;johndoe&lt;/code&gt;. Visit &lt;a href=&quot;http://johndoe.localhost:3000/articles/new&quot;&gt;http://johndoe.localhost:3000/articles/new&lt;/a&gt; address and add a new article. After adding a new article, you can view it on the list that is available under &lt;a href=&quot;http://johndoe.localhost:3000/articles&quot;&gt;http://johndoe.localhost:3000/articles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Congratulations! You have just created a multi-tenant app where each author gets their own subdomain.&lt;/p&gt;
&lt;h2&gt;Further Improvements&lt;/h2&gt;
&lt;p&gt;You can now extend the code of the application by adding new features. Our blog platform is straightforward; maybe it’s time to add some comments section? No matter what functionality you add, you must remember to add the &lt;code&gt;tenant_id&lt;/code&gt; column to the new model and use it when querying the resource.&lt;/p&gt;
&lt;p&gt;You can create a special scope and wrap it into a concern:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/concerns

module Tenantable
 extend ActiveSupport::Concern

 included do
   scope :for_author, -&amp;gt; (author) { where(tenant_id: author.id) }
 end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then use it in every model that contains data for tenants:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Article &amp;lt; ApplicationRecord
 include Tenantable
end

author = Author.find(1)
Article.for_author(author)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the above approach, you would have to update only one place in case of renaming the &lt;code&gt;tenant_id&lt;/code&gt; column or introducing more conditions when it comes to querying tenant-related data.&lt;/p&gt;
&lt;p&gt;And with that, you have a working multi-tenant Rails app using a single database.&lt;/p&gt;
&lt;h2&gt;Multi-tenant Rails Application with Multiple Databases&lt;/h2&gt;
&lt;p&gt;In the previous section, we built a Rails application that supports multiple tenants with one database and one schema.
The most significant disadvantage of such an approach is the high possibility of a data leak since all tenant data is within a shared database/schema.&lt;/p&gt;
&lt;p&gt;The good news is that the newest version of the Ruby on Rails framework has built-in support for managing multiple databases. I will explore it and show you how you can use it to build a multi-tenant Rails application with numerous databases and custom subdomains.&lt;/p&gt;
&lt;p&gt;The plan is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We will create a skeleton for a Rails application using the newest available version of Ruby and Ruby on Rails framework&lt;/li&gt;
&lt;li&gt;We will scaffold some models to have the data we can operate on&lt;/li&gt;
&lt;li&gt;We will add support for custom subdomains.&lt;/li&gt;
&lt;li&gt;We will learn how to add new tenants by creating and configuring new databases.&lt;/li&gt;
&lt;li&gt;The last step would be to update the application to switch the database when the given subdomain is requested.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Generating a New Rails App&lt;/h2&gt;
&lt;p&gt;In the terminal, generate a new Rails app with the command below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails new multi_db_multi_tenant_app --javascript=esbuild --css=bulma
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Preparing the Test Data&lt;/h2&gt;
&lt;p&gt;Let’s assume that we are building a blog platform where users can view articles published by multiple authors. Each author will have a dedicated database.&lt;/p&gt;
&lt;p&gt;Start with creating the &lt;code&gt;Author&lt;/code&gt; model along with scaffolded features that will help us to view and create new authors:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g scaffold author slug:string name:string description:text
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could run the migration to create the database right away but since we want separate databases for each author, we&amp;#39;ll hold on for now and first configure the multiple database connections.&lt;/p&gt;
&lt;h3&gt;Adding a new author&lt;/h3&gt;
&lt;p&gt;Because each author will have a separate database, we have to add manually new database for each author. Open &lt;code&gt;config/database.yml&lt;/code&gt; and add the following changes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;development:
 primary:
   &amp;lt;&amp;lt;: *default
   database: db/dev_multitenant_app.sqlite3

 primary_johndoe:
   &amp;lt;&amp;lt;: *default
   database: db/johndoe_dev_multitenant_app.sqlite3
   migrations_paths: db/tenants_migrations
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails db:create
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, go ahead and add a new author with the name John Doe and slug &lt;code&gt;johndoe&lt;/code&gt;. The slug value will be used later to detect which author we should display the information using the subdomain.
Next, let&amp;#39;s deal with the articles.&lt;/p&gt;
&lt;h3&gt;Adding articles&lt;/h3&gt;
&lt;p&gt;We can now scaffold the &lt;code&gt;Article&lt;/code&gt; model along with the controller and views:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails g scaffold article title:string content:text --database primary_johndoe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I passed the &lt;code&gt;--database&lt;/code&gt; param to let Rails know that the migration shouldn’t be placed in the default &lt;code&gt;db/migrations&lt;/code&gt; directory used by the primary database. We can now run the migrate command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right now, we have two schemas: &lt;code&gt;db/schema.rb&lt;/code&gt; and &lt;code&gt;db/primary_johndoe_schema.rb&lt;/code&gt;. If you would like to create different tables for tenants, you can achieve this by setting a unique &lt;code&gt;migrations_path&lt;/code&gt; value in the &lt;code&gt;config/database.yml&lt;/code&gt; file for the given tenant. In this article, we want to have the same tables for all tenants, so the path to migrations will be the same.&lt;/p&gt;
&lt;h2&gt;Adding Multi-tenancy Support&lt;/h2&gt;
&lt;p&gt;In the application where multi-tenancy is supported by one database, to get data for the given tenant, we just have to update the query to the database with the proper &lt;code&gt;tenant_id&lt;/code&gt; value.
In our case, each tenant has their database, so we have to switch between databases.&lt;/p&gt;
&lt;p&gt;Rails 7 has built-in support for horizontal sharding. Shard is a horizontal data partition that contains a subset of the total dataset.
We could store articles for all authors in one table in one database, but thanks to the sharding, we can split the data into multiple tables with the same structure but placed in separate databases.&lt;/p&gt;
&lt;p&gt;Let’s define our shards in the parent class for all models. Open up &lt;code&gt;app/models/application_record.rb&lt;/code&gt; and edit it to look like the code below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/application_record.rb

class ApplicationRecord &amp;lt; ActiveRecord::Base
  self.abstract_class = true

  ActiveRecord::Base.connected_to(role: :reading, shard: :johndoe) do
    Article.all # get all articles created by John
  end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without wrapping our call into the &lt;code&gt;connected_to&lt;/code&gt; block, the default shard will be used. Before we move forward, we have to introduce one more change. Since all shards share the same data structure, we can delete the &lt;code&gt;app/models/primary_johndoe_record.rb&lt;/code&gt; model created automatically when we were scaffolding articles.&lt;/p&gt;
&lt;p&gt;We also have to edit the &lt;code&gt;app/models/article.rb&lt;/code&gt; model and change the parent class from &lt;code&gt;PrimaryJohndoeRecord&lt;/code&gt; to &lt;code&gt;ApplicationRecord&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Article &amp;lt; ApplicationRecord
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Adding more tenants&lt;/h3&gt;
&lt;p&gt;Currently, we have only one author in our database. To test the functionality of switching between our tenants (databases), we have to add one more author.
Open &lt;a href=&quot;http://localhost:3000/authors/new&quot;&gt;http://localhost:3000/authors/new&lt;/a&gt; address and add a new author. I added the author with the name Tim Doe and slug &lt;code&gt;timdoe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We have a record for the new author, so we have to define a new database:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;development:
 primary:
   &amp;lt;&amp;lt;: *default
   database: db/dev_multitenant_app.sqlite3

 primary_johndoe:
   &amp;lt;&amp;lt;: *default
   database: db/johndoe_dev_multitenant_app.sqlite3
   migrations_paths: db/tenants_migrations

 primary_timdoe:
   &amp;lt;&amp;lt;: *default
   database: db/timdoe_dev_multitenant_app.sqlite3
   migrations_paths: db/tenants_migrations
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create a new database and run migrations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails db:create
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last step is to update &lt;code&gt;ApplicationRecord&lt;/code&gt; model and define a new shard:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ApplicationRecord &amp;lt; ActiveRecord::Base
 self.abstract_class = true

 connects_to shards: {
   default: { writing: :primary, reading: :primary },
   johndoe: { writing: :primary_johndoe, reading: :primary_johndoe },
   timdoe: { writing: :primary_timdoe, reading: :primary_timdoe },
 }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now create articles for each author:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Base.connected_to(role: :writing, shard: :johndoe) do
  Article.create!(title: &amp;#39;Article from John&amp;#39;, content: &amp;#39;content&amp;#39;)
end

ActiveRecord::Base.connected_to(role: :writing, shard: :timdoe) do
  Article.create!(title: &amp;#39;Article from Tim&amp;#39;, content: &amp;#39;content&amp;#39;)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Adding Support for a Custom Subdomain&lt;/h2&gt;
&lt;p&gt;We can’t visit the articles list for the given author yet because we don’t know when we should show articles from John and Tim.
We will solve this problem by implementing custom subdomains. When visiting &lt;a href=&quot;http://johndoe.localhost:3000/articles&quot;&gt;http://johndoe.localhost:3000/articles&lt;/a&gt;,
we should see articles from John, and articles from Tim when visiting &lt;a href=&quot;http://timdoe.localhost:3000/articles&quot;&gt;http://timdoe.localhost:3000/articles&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Subdomain configuration&lt;/h3&gt;
&lt;p&gt;We would like to manage and visit articles when the tenant is set. We can achieve this by updating the &lt;code&gt;config/routes.rb&lt;/code&gt; file and wrapping the definition of the articles resource into the constraints block:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.routes.draw do
 constraints subdomain: /.*/ do
   resources :articles
 end

 resources :authors
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, Rails set the top-level domain length to 1, but we want to use localhost to set this setting to 0. We can do this by adding the following line to the file &lt;code&gt;config/environments/development.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;config.action_dispatch.tld_length = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Making subdomains work with the multitenancy&lt;/h3&gt;
&lt;p&gt;To standardize reading the database for the current tenant, I will create a controller concern called &lt;code&gt;Tenantable&lt;/code&gt;. It provides the &lt;code&gt;read_with_tenant&lt;/code&gt; method, which accepts a block and executes it in the context of the requested tenant:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/concerns/tenantable.rb

module Tenantable
 extend ActiveSupport::Concern

 private

 def read_with_tenant(&amp;amp;block)
   author = Author.find_by!(slug: request.subdomain)

   ActiveRecord::Base.connected_to(role: :reading, shard: author.slug.to_sym) do
     block.call
   end
 end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save this file as &lt;code&gt;app/controllers/concerns/tenantable.rb&lt;/code&gt; and include in &lt;code&gt;ArticlesController&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ArticlesController &amp;lt; ApplicationController
 include Tenantable

 before_action :set_article, only: [:show, :edit, :update, :destroy]

 def index
   read_with_tenant do
     @articles = Article.all
   end
 end
 # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can visit &lt;a href=&quot;http://johndoe.localhost:3000/articles&quot;&gt;http://johndoe.localhost:3000/articles&lt;/a&gt; or &lt;a href=&quot;http://timdoe.localhost:3000/articles&quot;&gt;http://timdoe.localhost:3000/articles&lt;/a&gt;, and you will see different articles displayed on the list.&lt;/p&gt;
&lt;p&gt;If you would like to create new articles using the form, you have to define a new method called &lt;code&gt;write_with_tenant&lt;/code&gt; and update the Tenantable concern and methods inside the &lt;code&gt;ArticlesController&lt;/code&gt; accordingly.&lt;/p&gt;
&lt;h2&gt;Further Improvements&lt;/h2&gt;
&lt;p&gt;The approach presented above is just a simple wrapper method that executes code wrapped into a block within a given connection to the database. To make it more universal, you can create a middleware that will parse the subdomain and establish a connection before executing any code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Base.establish_connection(:primary_timdoe)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final solution depends on your needs and the number of places where you want to use the assigned data to a specific tenant.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Congratulations, you just built two versions of a multi-tenant Rails application and gained knowledge about the different ways of dealing with multiple tenants in a modern web application.&lt;/p&gt;
&lt;p&gt;Let’s just quickly summarize what we have learned during this article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are three primary multi-tenancy levels in a web application - database-row, schema, and database level.&lt;/li&gt;
&lt;li&gt;Each approach has its advantages and disadvantages, and your choice depends on the hardware possibilities and the level of isolation of the tenant information.&lt;/li&gt;
&lt;li&gt;It is possible to implement all multi-tenancy levels within the Ruby on Rails framework without any external libraries.&lt;/li&gt;
&lt;li&gt;Ruby on Rails supports custom subdomain out of the box, so it’s a perfect addition for a multi-tenant application where each tenant can have its subdomain assigned.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope you enjoyed reading this article and building multi-tenant Ruby on Rails applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby on Rails Model Patterns and Anti-patterns</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/11/18/rails-model-patterns-and-anti-patterns.html"/>
    <id>https://blog.appsignal.com/2020/11/18/rails-model-patterns-and-anti-patterns.html</id>
    <published>2020-11-18T00:00:00+00:00</published>
    <updated>2020-11-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">If you&#039;re struggling with models, this blog post is for you. We will quickly go through the process of putting your models on a diet and finish strongly with some things to avoid when writing migrations.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;This post was updated on 4 August 2023 to reference the latest version of ActiveRecord.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Welcome back to the second post in the Ruby on Rails Patterns and Anti-patterns series.
In the &lt;a href=&quot;/2020/08/05/introduction-to-ruby-on-rails-patterns-and-anti-patterns.html&quot;&gt;last blog post&lt;/a&gt;, we went over what patterns and
anti-patterns are in general. We also mentioned some of the most famous patterns
and anti-patterns in the Rails world. In this blog post, we&amp;#39;ll go through a
couple of Rails model anti-patterns and patterns.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re struggling with models, this blog post is for you. We
will quickly go through the process of putting your models on a diet and finish
strongly with some things to avoid when writing migrations. Let&amp;#39;s jump right in.&lt;/p&gt;
&lt;h2&gt;&lt;del&gt;Fat&lt;/del&gt; Overweight Models&lt;/h2&gt;
&lt;p&gt;When developing a Rails application, whether it&amp;#39;s a full-blown Rails website
or an API, people tend to store most of the logic in the model. In the
last blog post, we had an example of a &lt;code&gt;Song&lt;/code&gt; class that did many things.
Keeping a lot of things in the model breaks the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Single-responsibility_principle&quot;&gt;Single Responsibility Principle&lt;/a&gt; (SRP).&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s have a look.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  belongs_to :album
  belongs_to :artist
  belongs_to :publisher

  has_one :text
  has_many :downloads

  validates :artist_id, presence: true
  validates :publisher_id, presence: true

  after_update :alert_artist_followers
  after_update :alert_publisher

  def alert_artist_followers
    return if unreleased?

    artist.followers.each { |follower| follower.notify(self) }
  end

  def alert_publisher
    PublisherMailer.song_email(publisher, self).deliver_now
  end

  def includes_profanities?
    text.scan_for_profanities.any?
  end

  def user_downloaded?(user)
    user.library.has_song?(self)
  end

  def find_published_from_artist_with_albums
    ...
  end

  def find_published_with_albums
    ...
  end

  def to_wav
    ...
  end

  def to_mp3
    ...
  end

  def to_flac
    ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem with models like these is that they become a dumping ground for the
different logic related to a song. Methods start piling up as they
get added slowly, one by one over time.&lt;/p&gt;
&lt;p&gt;I suggested splitting the code inside the model into smaller modules.
But by doing that, you are simply moving code from one place to another.
Nonetheless, moving code around allows you to organize code better and
avoid obese models with reduced readability.&lt;/p&gt;
&lt;p&gt;Some people even resort to using
&lt;a href=&quot;/2020/09/16/rails-concers-to-concern-or-not-to-concern.html&quot;&gt;Rails concerns&lt;/a&gt;
and find that the logic can be reused across models. I previously wrote about it
and some people loved it, others didn&amp;#39;t. Anyway, the story with concerns is
similar to modules. You should be aware that you are just moving code to a
module that can be included anywhere.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Another alternative is to create small classes and then call them whenever needed.
For example, we can extract the song converting code to a separate class.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class SongConverter
  attr_reader :song

  def initialize(song)
    @song = song
  end

  def to_wav
    ...
  end

  def to_mp3
    ...
  end

  def to_flac
    ...
  end
end

class Song
  ...

  def converter
    SongConverter.new(self)
  end

  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have the &lt;code&gt;SongConverter&lt;/code&gt; that has the purpose of converting songs to a
different format. It can have its own tests and future logic about converting.
And, if we want to convert a song to MP3, we can do the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;@song.converter.to_mp3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To me, this looks a bit clearer than using a module or a concern. Maybe because
I prefer to use composition over inheritance. I consider it more intuitive and readable.
I suggest you review both cases before deciding which way to go. Or you
can choose both if you want, nobody is stopping you.&lt;/p&gt;
&lt;h2&gt;SQL Pasta Parmesan&lt;/h2&gt;
&lt;p&gt;Who doesn&amp;#39;t love some good pasta in real life? On the other hand, when it comes
to code pasta, almost no one is a fan. And for good reasons. In Rails
models, you can quickly turn your Active Record usage into spaghetti, swirling
around all over the codebase. How do you avoid this?&lt;/p&gt;
&lt;p&gt;There are a few ideas out there that seem to keep those long queries
from turning into lines of spaghetti. Let&amp;#39;s first see how database-related
code can be everywhere. Let&amp;#39;s go back to our &lt;code&gt;Song&lt;/code&gt; model. Specifically, to when we
try to fetch something from it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class SongReportService
  def gather_songs_from_artist(artist_id)
    songs = Song.where(status: :published)
                .where(artist_id: artist_id)
                .order(:title)

    ...
  end
end

class SongController &amp;lt; ApplicationController
  def index
    @songs = Song.where(status: :published)
                 .order(:release_date)

    ...
  end
end

class SongRefreshJob &amp;lt; ApplicationJob
  def perform
    songs = Song.where(status: :published)

    ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, we have three use-cases where the &lt;code&gt;Song&lt;/code&gt; model is being
queried. In the &lt;code&gt;SongReporterService&lt;/code&gt; that is used for reporting data about
songs, we try to get published songs from a concrete artist. Then, in the
&lt;code&gt;SongController&lt;/code&gt;, we get published songs and order them by the release date.
And finally, in the &lt;code&gt;SongRefreshJob&lt;/code&gt; we get only published songs and do
something with them.&lt;/p&gt;
&lt;p&gt;This is all fine, but what if we suddenly decide to change the status name to
&lt;code&gt;released&lt;/code&gt; or make some other changes to the way we fetch songs? We would have
to go and edit all occurrences separately. Also, the code above is not DRY. It
repeats itself across the application. Don&amp;#39;t let this get you down. Luckily, there
are solutions to this problem.&lt;/p&gt;
&lt;p&gt;We can use &lt;strong&gt;Rails scopes&lt;/strong&gt; to DRY this code out. Scoping allows you to define
commonly-used queries, which can be called on associations and objects. This makes
our code readable and easier to change. But, maybe the most important thing is that
scopes allow us to chain other Active Record methods such as &lt;code&gt;joins&lt;/code&gt;, &lt;code&gt;where&lt;/code&gt;,
etc. Let&amp;#39;s see how our code looks with scopes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  ...

  scope :published, -&amp;gt;            { where(published: true) }
  scope :by_artist, -&amp;gt;(artist_id) { where(artist_id: artist_id) }
  scope :sorted_by_title,         { order(:title) }
  scope :sorted_by_release_date,  { order(:release_date) }

  ...
end

class SongReportService
  def gather_songs_from_artist(artist_id)
    songs = Song.published.by_artist(artist_id).sorted_by_title

    ...
  end
end

class SongController &amp;lt; ApplicationController
  def index
    @songs = Song.published.sorted_by_release_date

    ...
  end
end

class SongRefreshJob &amp;lt; ApplicationJob
  def perform
    songs = Song.published

    ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you go. We managed to cut the repeating code and put it in the model. But
this doesn&amp;#39;t always work out for the best, especially if you are diagnosed with the
case of a fat model or a &lt;a href=&quot;https://en.wikipedia.org/wiki/God_object&quot;&gt;God Object&lt;/a&gt;.
Adding more and more methods and responsibilities to the model might not be such
a great idea.&lt;/p&gt;
&lt;p&gt;My advice here is to keep scope usage to a minimum and only extract the
common queries there. In our case, maybe the &lt;code&gt;where(published: true)&lt;/code&gt; would be
a perfect scope since it is used everywhere. For other SQL related code, you could use
something called a Repository pattern. Let&amp;#39;s find out what it is.&lt;/p&gt;
&lt;h2&gt;Repository Pattern&lt;/h2&gt;
&lt;p&gt;What we are about to show is not a 1:1 Repository pattern as defined in the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Domain-driven_design&quot;&gt;Domain-Driven Design&lt;/a&gt; book.
The idea behind ours and the Rails Repository pattern is to separate database logic
from business logic. We could also go full-on and create a repository class
that does the raw SQL calls for us instead of Active Record, but I wouldn&amp;#39;t
recommend such things unless you really need it.&lt;/p&gt;
&lt;p&gt;What we can do is create a &lt;code&gt;SongRepository&lt;/code&gt; and put the database logic in there.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class SongRepository
  class &amp;lt;&amp;lt; self
    def find(id)
      Song.find(id)
    rescue ActiveRecord::RecordNotFound =&amp;gt; e
      raise RecordNotFoundError, e
    end

    def destroy(id)
      find(id).destroy
    end

    def recently_published_by_artist(artist_id)
      Song.where(published: true)
          .where(artist_id: artist_id)
          .order(:release_date)
    end
  end
end

class SongReportService
  def gather_songs_from_artist(artist_id)
    songs = SongRepository.recently_published_by_artist(artist_id)

    ...
  end
end

class SongController &amp;lt; ApplicationController
  def destroy
    ...

    SongRepository.destroy(params[:id])

    ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What we did here is we isolated the querying logic into a testable class.
Also, the model is no longer concerned with scopes and logic. The controller
and models are thin, and everyone&amp;#39;s happy. Right? Well, there is still Active
Record doing all the heavy pulling there. In our scenario, we use &lt;code&gt;find&lt;/code&gt;, which
generates the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT &amp;quot;songs&amp;quot;.* FROM &amp;quot;songs&amp;quot; WHERE &amp;quot;songs&amp;quot;.&amp;quot;id&amp;quot; = $1 LIMIT $2  [[&amp;quot;id&amp;quot;, 1], [&amp;quot;LIMIT&amp;quot;, 1]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &amp;quot;right&amp;quot; way would be to have all this defined inside the
&lt;code&gt;SongRepository&lt;/code&gt;. As I said, I would not recommend that. You don&amp;#39;t need it and you
want to have full control. A use case for going away from Active Record would
be that you need some complex tricks inside SQL that are not easily supported
by Active Record.&lt;/p&gt;
&lt;p&gt;Talking about raw SQL and Active Record, I also have to bring up one topic. The
topic of migrations and how to do them properly. Let&amp;#39;s dive in.&lt;/p&gt;
&lt;h2&gt;Migrations — Who Cares?&lt;/h2&gt;
&lt;p&gt;I often hear an argument when writing migrations that the code there should not
be as good as it is in the rest of the application. And that argument doesn&amp;#39;t
sit well with me. People tend to use this excuse to set up smelly code in the
migrations since it will only be run once and forgotten. Maybe
this is true if you are working with a couple of people and everyone is in
constant sync all the time.&lt;/p&gt;
&lt;p&gt;The reality is often different. The application can be used by a larger number of
people not knowing what happens with different application parts. And if you
put some questionable one-off code there, you might break someone&amp;#39;s development
environment for a couple of hours because of the corrupted database state or
just a weird migration. Not sure if this is an anti-pattern, but you should be aware of it.&lt;/p&gt;
&lt;p&gt;How to make migrations more convenient for other people? Let&amp;#39;s go through a list
that will make migrations easier for everyone on the project.&lt;/p&gt;
&lt;h3&gt;Make Sure You Always Provide a Down Method&lt;/h3&gt;
&lt;p&gt;You never know when something is going to be rolled back. If your migration is
not reversible, make sure to raise &lt;code&gt;ActiveRecord::IrreversibleMigration&lt;/code&gt;
exception like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def down
  raise ActiveRecord::IrreversibleMigration
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Try to Avoid Active Record in Migrations&lt;/h3&gt;
&lt;p&gt;The idea here is to minimize external dependencies except for the state of the
database at the time when the migration should be executed. So there will be no
Active Record validations to ruin (or maybe save) your day. You are left
with plain SQL. For example, let&amp;#39;s write a migration that will publish all
songs from a certain artist.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class UpdateArtistsSongsToPublished &amp;lt; ActiveRecord::Migration[6.0]
  def up
    execute &amp;lt;&amp;lt;-SQL
      UPDATE songs
      SET published = true
      WHERE artist_id = 46
    SQL
  end

  def down
    execute &amp;lt;&amp;lt;-SQL
      UPDATE songs
      SET published = false
      WHERE artist_id = 46
    SQL
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have a great need for the &lt;code&gt;Song&lt;/code&gt; model, a suggestion would be to define it
inside the migration. That way, you can bulletproof your migration from any
potential changes in the actual Active Record model inside the &lt;code&gt;app/models&lt;/code&gt;.
But, is this all fine and dandy? Let&amp;#39;s go to our next point.&lt;/p&gt;
&lt;h3&gt;Separate Schema Migrations From Data Migrations&lt;/h3&gt;
&lt;p&gt;Going through the &lt;a href=&quot;https://edgeguides.rubyonrails.org/active_record_migrations.html&quot;&gt;Rails Guides on migrations&lt;/a&gt;, you&amp;#39;ll read the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Migrations are a feature of Active Record that allows you to evolve your &lt;strong&gt;database schema&lt;/strong&gt; over time. Rather than write schema modifications in pure SQL, migrations allow you to use a Ruby DSL to describe changes to your tables.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the summary of the guide, there is no mention of editing the actual data of
the database table, only the structure. So, the fact that we used the regular migration
to update songs in the second point is not completely right.&lt;/p&gt;
&lt;p&gt;If you need to regularly do something similar in your project, consider using
the &lt;a href=&quot;https://github.com/ilyakatz/data-migrate&quot;&gt;&lt;code&gt;data_migrate&lt;/code&gt; gem&lt;/a&gt;. It is a nice way
of separating data migrations from schema migrations. We can easily rewrite our previous example
with it. To generate the data migration, we can do the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bin/rails generate data_migration update_artists_songs_to_published
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then add the migration logic there:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class UpdateArtistsSongsToPublished &amp;lt; ActiveRecord::Migration[7.0]
  def up
    execute &amp;lt;&amp;lt;-SQL
      UPDATE songs
      SET published = true
      WHERE artist_id = 46
    SQL
  end

  def down
    execute &amp;lt;&amp;lt;-SQL
      UPDATE songs
      SET published = false
      WHERE artist_id = 46
    SQL
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, you are keeping all your schema migrations inside the &lt;code&gt;db/migrate&lt;/code&gt;
directory and all the migrations that deal with the data inside the &lt;code&gt;db/data&lt;/code&gt;
directory.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Dealing with models and keeping them readably in Rails is a constant struggle.
Hopefully, in this blog post, you got to see the possible pitfalls and solutions
to common problems. The list of model anti-patterns and patterns is far from
complete in this post, but these are the most notable ones I found recently.&lt;/p&gt;
&lt;p&gt;If you are interested in more Rails patterns and anti-patterns, stay tuned for
the next installment in the series. In upcoming posts, we&amp;#39;ll go through
common problems and solutions to the view and controller side of the Rails MVC.&lt;/p&gt;
&lt;p&gt;Until next time, cheers!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Structuring Monitoring Data in Monolithic Applications With Namespaces</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/11/04/structuring-monitoring-data-in-monolith-applications-through-namespaces.html"/>
    <id>https://blog.appsignal.com/2020/11/04/structuring-monitoring-data-in-monolith-applications-through-namespaces.html</id>
    <published>2020-11-04T00:00:00+00:00</published>
    <updated>2020-11-04T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Namespaces help you make sense of what’s happening in your application. Knowing how they work is especially useful for large monolithic projects.</summary>
    <content type="html">&lt;h2&gt;What Are Namespaces?&lt;/h2&gt;
&lt;p&gt;Everything that happens in an AppSignal-monitored application is logged under a namespace. Namespaces work like folders, grouping events, issues, and monitoring data into manageable chunks.&lt;/p&gt;
&lt;p&gt;By default, every application starts with three default namespaces: &lt;code&gt;web&lt;/code&gt;, &lt;code&gt;background&lt;/code&gt;, and &lt;code&gt;frontend&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The web namespace holds all your HTTP requests. In MVC-oriented frameworks such as Rails or Sinatra, this includes controller actions.&lt;/li&gt;
&lt;li&gt;The background namespace holds activity from background jobs, libraries, and tasks.&lt;/li&gt;
&lt;li&gt;The frontend namespace logs the events sent by the &lt;a href=&quot;https://docs.appsignal.com/front-end/&quot;&gt;AppSignal for JavaScript&lt;/a&gt; integration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AppSignal maps incoming events using built-in per-application and per-integration rules. However, you can change these mappings at any time and even create new namespaces to model your application structure.&lt;/p&gt;
&lt;h2&gt;Trying Out Namespaces in Ruby&lt;/h2&gt;
&lt;p&gt;Let’s try out namespaces on a Ruby on Rails (ROR) application. After creating a fresh Rails project with &lt;code&gt;rails new&lt;/code&gt; and setting up the &lt;a href=&quot;https://docs.appsignal.com/ruby/integrations/rails.html&quot;&gt;rails integration&lt;/a&gt;, you’ll find the &lt;code&gt;web&lt;/code&gt; namespace in your dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/web1.png&quot; alt=&quot;Dashboard showing web namespace&quot;/&gt;&lt;/p&gt;
&lt;p&gt;AppSignal shows the namespace as soon as it receives transactions from any of the controllers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/web1-controllers.png&quot; alt=&quot;Most recent actions. Shows controller events.&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The rest of the default namespaces won’t appear until there is some activity in them. Let’s add something in the background namespace to make things more interesting. This is how the dashboard looks after adding &lt;a href=&quot;https://sidekiq.org/&quot;&gt;Sidekiq&lt;/a&gt; to the project (check the code in &lt;a href=&quot;https://github.com/appsignal/appsignal-examples/tree/rails-5+sidekiq&quot;&gt;examples repository&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/bg1a.png&quot; alt=&quot;Dashboard showing the background namespace&quot;/&gt;&lt;/p&gt;
&lt;p&gt;AppSignal assigns the action to the background namespace because it recognizes Sidekiq as a job processor. AppSignal integrates with most of the popular background processors out there, but if yours is omitted, you can always &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/background-jobs.html&quot;&gt;add instrumentation&lt;/a&gt; to your jobs manually.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/bg1b.png&quot; alt=&quot;Last events in the background namespace&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Creating Custom Namespaces&lt;/h2&gt;
&lt;p&gt;On large monolithic applications, the default namespaces can feel too generic. A big website typically serves static content, dynamic pages, API endpoints, among other web services. Most of this would all end up on the &lt;code&gt;web&lt;/code&gt; namespace.&lt;/p&gt;
&lt;p&gt;Also, every part of the application has a different priority. A login page problem is a lot more urgent than one in the internal administration panel. Yet AppSignal treats all issues within a namespace as equal. When there is a lot of activity, it can be hard to identify the most critical issues.&lt;/p&gt;
&lt;p&gt;So, we should organize namespaces by priority and areas of responsibility. Then we can attach separate notification policies and alert only the interested parties. Following this reasoning, we could create custom namespaces for the &lt;code&gt;login_page&lt;/code&gt;, &lt;code&gt;api_endpoints&lt;/code&gt;, and &lt;code&gt;admin_panel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/namespace-comparison.png&quot; alt=&quot;Custom namespaces let us create fine-grained zones for the application&quot;/&gt;&lt;/p&gt;
&lt;p&gt;To create a &lt;a href=&quot;https://docs.appsignal.com/guides/namespaces.html&quot;&gt;new namespace in Ruby&lt;/a&gt;, we’ll use &lt;code&gt;Appsignal.set_namespace&lt;/code&gt;. Take a look at the following code, which creates a job in a namespace called &lt;code&gt;urgent_background&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class FetchPricesWorker
    include Sidekiq::Worker

    def perform
        Appsignal.set_namespace(&amp;quot;urgent_background&amp;quot;)

        # worker code ...

    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we made this change and restarted the app, these new jobs will appear in the newly-created namespace:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/ubg1a.png&quot; alt=&quot;Dashboard showing the urgent_background namespace&quot;/&gt;&lt;/p&gt;
&lt;p&gt;We can confirm that the actual job has been logged by checking the action name in the dashboard:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/ubg1b.png&quot; alt=&quot;Last events in the urgent_background namespace&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Custom namespaces also work in &lt;a href=&quot;https://docs.appsignal.com/guides/namespaces.html&quot;&gt;all integrations&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Ignoring Namespaces&lt;/h2&gt;
&lt;p&gt;Another benefit of custom namespaces is that they let us disregard events from parts of the application we don’t care about. For instance, we may choose to ignore events from the &lt;code&gt;admin_panel&lt;/code&gt; completely.&lt;/p&gt;
&lt;p&gt;Ignoring a namespace takes three steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Assign the parts we don’t want to monitor to a custom namespace.&lt;/li&gt;
&lt;li&gt;Configure the integration to ignore the namespace.&lt;/li&gt;
&lt;li&gt;Restart your app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For Ruby, add the &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/namespaces.html#ignore-by-namespace&quot;&gt;ignore_namespaces&lt;/a&gt; option in the AppSignal config file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;production:
  ignore_namespaces:
    - &amp;quot;admin_panel&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ignoring a namespace skips all the transaction and span data at the source. Custom metric data is still reported.&lt;/p&gt;
&lt;p&gt;The Elixir and JavaScript integrations have similar options. For more details check the &lt;a href=&quot;https://docs.appsignal.com/guides/filter-data/ignore-namespaces.html&quot;&gt;ignoring namespaces guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Namespaces for Monolithic Applications&lt;/h2&gt;
&lt;p&gt;Now that we know how namespaces work, let’s examine a few ways we can use them to partition a monolithic application.&lt;/p&gt;
&lt;p&gt;While there are no set rules, partitioning boils down to two strategies. You may choose one of them or a mix of both as a starting point:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;By role&lt;/strong&gt;: we assign namespaces to functional or logical units within the project. Thus, we may find it sensible to define namespaces such as &lt;code&gt;billing&lt;/code&gt;, &lt;code&gt;sign_in&lt;/code&gt; or &lt;code&gt;sign_up&lt;/code&gt;, &lt;code&gt;admin_panel&lt;/code&gt;, and &lt;code&gt;homepage&lt;/code&gt;. One glance at the AppSignal dashboard and you will understand what’s going on in each part of the application. This approach works well when the code can be nicely broken up by clear boundaries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;By severity&lt;/strong&gt;: here we use namespaces as a prioritizing device. It’s up to you to establish which parts of the code are &lt;code&gt;critical&lt;/code&gt;, &lt;code&gt;important&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, or &lt;code&gt;low&lt;/code&gt;. This approach lets you immediately sort out what problems you want to address first.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Suppose that we have a controllers that handle user sign in and registration. When choosing to partition by role, we could map them to the &lt;code&gt;user_login&lt;/code&gt; namespace.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# in Rails we use before_action callback to change
# the namespace before the request starts
class LoginController &amp;lt; ApplicationController
    before_action :set_appsignal_namespace

    def set_appsignal_namespace
        # Sets the namespace
        Appsignal.set_namespace(&amp;quot;user_login&amp;quot;)
    end

    # controller actions ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if you prefer using priority namespaces, a controller in charge of billing would probably go in the &lt;code&gt;critical&lt;/code&gt; namespace.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class BillingPageController &amp;lt; ApplicationController
    before_action :set_appsignal_namespace

    def set_appsignal_namespace
        Appsignal.set_namespace(&amp;quot;critical&amp;quot;)
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Controllers that inherit from these share the same namespace as their parents:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# any controllers that inherit from LoginController
# are also part of the &amp;quot;user_login&amp;quot; namespace
class RegistrationController &amp;lt; LoginController

    # there’s no need for before_action here
    # this controller already reports to the parent’s namespace

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we’ve seen, jobs and tasks are automatically assigned to the &lt;code&gt;background&lt;/code&gt; namespace. Whenever possible, we should assign them into more specific ones. For instance, a database cleanup job could go into the &lt;code&gt;database&lt;/code&gt; namespace, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class ActiveJobDatabaseCleanupJob &amp;lt; ActiveJob::Base
  queue_as :default

  def perform(argument = nil, options = {})
    Appsignal.set_namespace(&amp;quot;database&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Priorities also work for jobs. We could assign unimportant tasks to &lt;code&gt;low&lt;/code&gt; for instance, as in this Rake task:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;task :unimportant_job do

  # Run this rake job in the low namespace
  Appsignal.set_namespace(&amp;quot;low&amp;quot;)

  # job code ...

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In some cases, you will want to log actions using a manual transaction. You can define the namespace while creating it, like in the following example, which codes a custom mailer job:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Job
    def perform

        # Create a transaction for this job and set the namespace
        transaction = Appsignal::Transaction.create(
            SecureRandom.uuid,
            &amp;quot;mailer&amp;quot;,
            Appsignal::Transaction::GenericRequest.new(ENV.to_hash)
        )

        # job code ...

    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Namespaces and Notifications&lt;/h2&gt;
&lt;p&gt;Not everyone in the team needs to be notified about every problem. Frontend specialists don’t care about background jobs as much as backend developers. Still, they may want to know when there’s a problem in the backend. Backend developers will surely like to be notified of performance issues on the &lt;code&gt;web&lt;/code&gt; namespace. Namespaces let us route notifications to the right people.&lt;/p&gt;
&lt;h3&gt;Setting Up Per-Namespace Notifiers&lt;/h3&gt;
&lt;p&gt;We can create notification groups that are only active for specific namespaces. For instance, we could send emails for errors in the &lt;code&gt;web&lt;/code&gt; namespace, or send a message into the #frontend Slack channel for issues in the &lt;code&gt;frontend&lt;/code&gt; namespace.&lt;/p&gt;
&lt;p&gt;To create per-namespace notification groups, go to App Settings &amp;gt; Notifications &amp;gt; Notifiers and click on &lt;strong&gt;Add Integration&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/add-integration.png&quot; alt=&quot;Adding an integration&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Select one of the integrations and type its name. Choose which type of messages to send and for which namespace. For example, let’s create a Slack notification for the &lt;code&gt;#frontend&lt;/code&gt; channel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/frontend-slack.png&quot; alt=&quot;slack integration&quot;/&gt;&lt;/p&gt;
&lt;p&gt;While we’re still here, create a second notification for the backend developers:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/notification-web.png&quot; alt=&quot;New notification group for web&quot;/&gt;&lt;/p&gt;
&lt;p&gt;You can configure as many notifiers as you need to keep the team up to date with everything that’s happening.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/notifiers.png&quot; alt=&quot;Notifiers for different parts of the application&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Changing Per-Namespace Notifications&lt;/h3&gt;
&lt;p&gt;When an incident is created, AppSignal will apply a notification policy. This policy is based on the namespace the error comes from. We can define separate policies for each namespace.&lt;/p&gt;
&lt;p&gt;To see the &lt;a href=&quot;https://docs.appsignal.com/application/notification-settings.html#organization-and-app-namespace-defaults&quot;&gt;namespace defaults&lt;/a&gt; for your application, go to &lt;a href=&quot;https://appsignal.com/redirect-to/app?to=notifications&quot;&gt;App Settings &amp;gt; Notifications &amp;gt; &lt;strong&gt;Namespace defaults&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/namespace-defaults.png&quot; alt=&quot;Namespace defaults for web, background and urgent_background&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Here, you’ll find options to customize error and performance notifications for every namespace:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Every occurrence&lt;/strong&gt;: sends notifications every time an incident is triggered, with a 5-minute cooldown.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First on deploy&lt;/strong&gt;: notifies you of the first error after deploying the application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First after close&lt;/strong&gt;: sends a notification the first time a closed issue is reopened.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never notify&lt;/strong&gt;: disables notifications altogether.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Creating Per-Namespace Triggers&lt;/h3&gt;
&lt;p&gt;Triggers tell AppSignal to create an incident and send notifications when a metric goes over or below a predefined value. Since different parts of an application may have different thresholds, we should create separate triggers for each namespace. The classic example is a trigger that alerts us when throughput is too low in the &lt;code&gt;web&lt;/code&gt; namespace.&lt;/p&gt;
&lt;p&gt;To create a trigger, go to &lt;a href=&quot;https://appsignal.com/redirect-to/app?to=triggers&quot;&gt;Anomaly Detection &amp;gt; Triggers&lt;/a&gt;, and click on &lt;strong&gt;Add your first trigger&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Select an Actions trigger type on the left menu and choose the relevant namespace. Then, set the thresholds that trigger the alert.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/create-trigger-web.png&quot; alt=&quot;Creating a new trigger for the web namespace&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Here you can also define which groups should be notified. To finalize, click on &lt;strong&gt;Save Trigger&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-11/img/create-trigger-web-notify.png&quot; alt=&quot;This alert will send a notification to backend developers&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Namespaces let make sense of your application’s monitoring data. They are also indispensable for firing notifications and incidents on a fine-grained level, limiting noise, and avoiding false positives.&lt;/p&gt;
&lt;p&gt;After checking how custom namespaces work on &lt;a href=&quot;https://docs.appsignal.com/ruby/instrumentation/namespaces.html&quot;&gt;Ruby&lt;/a&gt;, &lt;a href=&quot;https://docs.appsignal.com/nodejs/2.x/tracing/namespaces.html&quot;&gt;Node.js&lt;/a&gt;, and &lt;a href=&quot;https://docs.appsignal.com/elixir/instrumentation/namespaces.html&quot;&gt;Elixir&lt;/a&gt;, read these next to continue learning how to use namespaces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.appsignal.com/application/namespaces.html&quot;&gt;Namespaces in AppSignal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.appsignal.com/guides/namespaces.html&quot;&gt;Grouping with namespaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2020/03/25/whats-the-difference-between-monitoring-webhooks-and-background-jobs.html&quot;&gt;What&amp;#39;s The Difference Between Monitoring Webhooks and Background Jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2017/05/03/gem-2-2.html&quot;&gt;Gem 2.2 - Custom namespaces!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Monitoring Any System with StatsD and AppSignal&#039;s Standalone Agent</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/09/23/monitoring-any-system-with-statsd-and-the-standalone-appsignal-agent.html"/>
    <id>https://blog.appsignal.com/2020/09/23/monitoring-any-system-with-statsd-and-the-standalone-appsignal-agent.html</id>
    <published>2020-09-23T00:00:00+00:00</published>
    <updated>2020-09-23T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">To extend monitoring everywhere and have all data in a single place, check out our standalone agent.</summary>
    <content type="html">&lt;p&gt;Monitoring your application alone is not always enough to get the full picture of your systems. Frequently, services running in satellite apps (or supporting apps) can have an acute impact on your day-to-day operations. Database servers are well-known examples of this. Backup scripts and other background jobs can also slow systems and are often overlooked.&lt;/p&gt;
&lt;p&gt;The AppSignal &lt;a href=&quot;https://www.appsignal.com/nodejs&quot;&gt;APM for Node.js&lt;/a&gt;, and the &lt;a href=&quot;https://www.appsignal.com/ruby/&quot;&gt;Ruby APM&lt;/a&gt; and &lt;a href=&quot;https://www.appsignal.com/elixir/&quot;&gt;Elixir APM&lt;/a&gt; automatically instrument your app itself. But AppSignal does not watch these satellite processes by default. To extend monitoring everywhere and have all your data in a single app, you can install AppSignal’s &lt;a href=&quot;https://docs.appsignal.com/standalone-agent/installation.html&quot;&gt;standalone agent&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;AppSignal&amp;#39;s Standalone Agent&lt;/h2&gt;
&lt;p&gt;The standalone agent is based on the &lt;a href=&quot;https://docs.appsignal.com/appsignal/how-appsignal-operates.html#agent&quot;&gt;same software&lt;/a&gt; with which we usually instrument Ruby, Elixir, or JavaScript applications. This software can also run in standalone mode.&lt;/p&gt;
&lt;p&gt;The standalone agent can be used to monitor:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;: machines that are part of our system but do not run application code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Background jobs&lt;/strong&gt;: such as intensive cron jobs or long-running data-processing scripts. If these background jobs are written in a supported language (Ruby, Elixir, or Node.js) you can use the standard integration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More languages&lt;/strong&gt;: programs written in languages other than those supported out of the box.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For instance, with the standalone agent, we could track a machine learning model written in Python, instrument backup scripts, &lt;a href=&quot;/2020/07/07/how-appsignal-monitors-their-own-kafka-brokers.html&quot;&gt;monitor Kafka brokers&lt;/a&gt;, or collect &lt;a href=&quot;https://docs.appsignal.com/metrics/host-metrics/index.html&quot;&gt;host metrics&lt;/a&gt; in our web farms. You can view all this information on AppSignal to complement the metrics you already have for your main applications.&lt;/p&gt;
&lt;h2&gt;How It Works&lt;/h2&gt;
&lt;p&gt;The agent is shipped as a deb or rpm package and doesn’t have any language dependencies. It runs in any Debian/Ubuntu or Red Hat-based systems. For detailed installation instructions, check the &lt;a href=&quot;https://docs.appsignal.com/standalone-agent/installation.html#installation&quot;&gt;agent documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once installed, the agent is configured in a few minutes and keeps running forever as a daemon, silently monitoring your infrastructure. What is more, the agent includes a &lt;a href=&quot;https://docs.appsignal.com/standalone-agent/statsd.html&quot;&gt;StatsD&lt;/a&gt; server that relays any custom data you log into your AppSignal dashboard.&lt;/p&gt;
&lt;h3&gt;What Is StatsD?&lt;/h3&gt;
&lt;p&gt;StatsD is a standard for collecting and aggregating arbitrary data. It focuses on logging metric and performance information. It uses a lightweight text protocol over UDP connections that have a small footprint in your machine.&lt;/p&gt;
&lt;p&gt;A StatsD message looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;KEY:VALUE|TYPE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;KEY&lt;/code&gt; is any arbitrary string, and &lt;code&gt;VALUE&lt;/code&gt; is a number. The type value defines how the number is processed.&lt;/p&gt;
&lt;p&gt;We support three types of metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;c&lt;/strong&gt;: this is a &lt;em&gt;counter&lt;/em&gt; that increments each time it is called. For instance, &lt;code&gt;active_users:1|c&lt;/code&gt; adds 1 to the &lt;code&gt;active_users&lt;/code&gt; counter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;g&lt;/strong&gt;: a &lt;em&gt;gauge&lt;/em&gt; takes a numeric value and maintains it until updated. This is useful to record values that change up and down over time, such as throughput, number of active users, or number of pending tasks in a queue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;t&lt;/strong&gt;: stores &lt;em&gt;timing&lt;/em&gt; values. This type is ideal for tracking durations. AppSignal calculates means, count, and percentiles for all the logged timings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other unsupported metric types will be silently ignored.&lt;/p&gt;
&lt;h2&gt;Sending Data to StatsD&lt;/h2&gt;
&lt;p&gt;The standalone agent listens for UDP packets on port 8125. We can send StatsD-formatted strings from the command line using &lt;a href=&quot;http://netcat.sourceforge.net/&quot;&gt;netcat&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo -n &amp;quot;myscript.myevent.counter:1|c&amp;quot; | nc -4u -w0 localhost 8125
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we’re using UDP, we don’t have to wait for a response.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.dest-unreach.org/socat/&quot;&gt;Socat&lt;/a&gt; also works:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo -n &amp;quot;myscript.myevent.counter:1|c&amp;quot; | socat - udp:localhost:8125
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes it easy to instrument any batch or cron jobs. For instance, the following lines use a gauge to log how much data a backup job has generated:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;backup_size=$(du -m /backups | cut -f1)
echo -n &amp;quot;backup.data:$backup_size|g&amp;quot; | nc -4u -w0 localhost 8125
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re not limited to integers. StatsD also works with floating-point numbers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo -n &amp;quot;network.latency:0.2|g&amp;quot; | nc -4u -w0 localhost 8125
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Using Tags&lt;/h2&gt;
&lt;p&gt;You can add tags to your metrics. The StatsD server supports optional tags at the end of the message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;KEY:VALUE|TYPE|#TAGS
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can apply several tags in the same message and assign values for filtering later:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo -n &amp;quot;backup.data:$backup_size|g|#backups,env:production&amp;quot; | nc -4u -w0 localhost 8125
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll learn how to see the data in AppSignal in a bit.&lt;/p&gt;
&lt;h2&gt;Instrumenting Languages&lt;/h2&gt;
&lt;p&gt;The StatsD server is compatible with any language that can send UDP packets. Let’s see a few examples.&lt;/p&gt;
&lt;p&gt;Python is a popular language for data mining and machine learning. We can add instrumentation into Python applications using the build-in &lt;code&gt;socket&lt;/code&gt; library:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import time

# measure time taken for function
start = time.process_time()
train_my_model()
training_time = time.process_time() - start

# send value to statsd
import socket

UDP_IP = &amp;quot;127.0.0.1&amp;quot;
UDP_PORT = 8125
MESSAGE = b&amp;quot;model.training.time:&amp;quot; + bytes(str(training_time), &amp;#39;utf-8&amp;#39;) + b&amp;quot;|t&amp;quot;

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could achieve similar results in Java with the &lt;code&gt;java.net&lt;/code&gt; library:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;byte[] buffer = &amp;quot;mybatch.exception.counter:1|c&amp;quot;.getBytes();
InetAddress address = InetAddress.getByName(&amp;quot;127.0.0.1&amp;quot;);
DatagramPacket packet = new DatagramPacket(
    buffer, buffer.length, address, 8125
);
DatagramSocket datagramSocket = new DatagramSocket();
datagramSocket.send(packet);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHP is another example of an incredibly popular language. We can send UDP packets with &lt;code&gt;socket_sendto&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;$msg = &amp;quot;mywebsite.active_users:$ACTIVE_USERS|g&amp;quot;;
$len = strlen($msg);

$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_sendto($sock, $msg, $len, 0, &amp;#39;127.0.0.1&amp;#39;, 8125);
socket_close($sock);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;StatsD Clients&lt;/h3&gt;
&lt;p&gt;Thus far, we used the built-in networking capabilities in every language. But there’s more. Many languages and products include third-party StatsD clients or addons. Any StatsD-compliant client should work, at least for the supported data types. You can find a list of clients at the &lt;a href=&quot;https://github.com/statsd/statsd/wiki&quot;&gt;StatsD project wiki&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Viewing Host Data in AppSignal&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Host Metrics&lt;/strong&gt; dashboard shows your machine’s resource utilization:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/hosts-dashboard.png&quot; alt=&quot;Hosts metrics dashboard location&quot;/&gt;&lt;/p&gt;
&lt;p&gt;AppSignal adds an entry for every machine running the standalone agent.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/host-ov.png&quot; alt=&quot;Available hosts&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The dashboard shows the load and CPU averages disk, network, memory, and swap usage:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/hosts1.png&quot; alt=&quot;Host dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/hosts2.png&quot; alt=&quot;Host 2&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Creating Dashboards&lt;/h2&gt;
&lt;p&gt;AppSignal doesn’t automatically generate dashboards for the StatsD value you sent—you’ll need to create custom dashboards for this.&lt;/p&gt;
&lt;p&gt;First, click on &lt;strong&gt;Add dashboard&lt;/strong&gt; under the dashboard menu:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/dash0.png&quot; alt=&quot;Location of the create dashboard button&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Give a name to the dashboard:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/dash1.png&quot; alt=&quot;Creating a dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Clicking the &lt;strong&gt;Add graph&lt;/strong&gt; button shows the graph builder:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/dash2.png&quot; alt=&quot;Empty custom dashboard&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Give a name to the graph, and click on &lt;strong&gt;Add metric&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/create1.png&quot; alt=&quot;Creating a graph&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Pick the metric you’re interested in from the menu.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/backup1.png&quot; alt=&quot;Adding metrics to the graph&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Optionally, use tags for filtering:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/backup2.png&quot; alt=&quot;Filtering by tags&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Next, click &lt;strong&gt;Back to overview&lt;/strong&gt;. You might want to try different graph types and value units to find out which one best suits the data you want to represent.&lt;/p&gt;
&lt;p&gt;Timing data looks better with area graphs because the mean and percentiles are shown more clearly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/stats-timer.png&quot; alt=&quot;Changing display settings&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Line graphs work great for counters and gauges.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/create2.png&quot; alt=&quot;Create 2&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Once you’re happy with the result, click on &lt;strong&gt;Create graph&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-09/backup3.png&quot; alt=&quot;New graph created&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Note that you can also add a dashbaord for any other automatically instrumented or measured metrics if you have AppSignal set of as your &lt;a href=&quot;https://www.appsignal.com/nodejs&quot;&gt;performance monitoring tool (APM) for Node.js&lt;/a&gt;, or your &lt;a href=&quot;https://www.appsignal.com/ruby/&quot;&gt;Ruby (on Rails) APM&lt;/a&gt; or &lt;a href=&quot;https://www.appsignal.com/elixir/&quot;&gt;Elixir APM&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;We’ve learned how to use AppSignal’s standalone server to watch your machines and satellite code. With its built-in StatsD server, you can record arbitrary performance data and instrument any process.&lt;/p&gt;
&lt;p&gt;Check the following links to learn more about using the standalone agent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://appsignal.com&quot;&gt;StatsD reference&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.appsignal.com/metrics/custom.html&quot;&gt;How to send custom metrics&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.appsignal.com/metrics/dashboards.html&quot;&gt;Creating dashboards in AppSignal&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2020/08/17/identifying-and-resolving-a-kafka-issue-with-appsignal.html&quot;&gt;Identifying and Resolving a Kafka Issue the Standalone Agent&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Rails Concerns: To Concern Or Not To Concern</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/09/16/rails-concers-to-concern-or-not-to-concern.html"/>
    <id>https://blog.appsignal.com/2020/09/16/rails-concers-to-concern-or-not-to-concern.html</id>
    <published>2020-09-16T00:00:00+00:00</published>
    <updated>2020-09-16T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Should you be concerned about Rails concerns?</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;This post was updated on 4 August 2023 to reference a discussion thread on concerns.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;#39;ve ever used Ruby on Rails, you&amp;#39;ve probably come across the
concept of concerns. Whenever you jumpstart a new Rails project, you get a
directory &lt;code&gt;app/controllers/concerns&lt;/code&gt; and &lt;code&gt;app/models/concerns&lt;/code&gt;. But what are
concerns? And why do people from the Rails community sometimes talk badly about them?&lt;/p&gt;
&lt;h2&gt;Quick Overview&lt;/h2&gt;
&lt;p&gt;A Rails Concern is any module that extends &lt;code&gt;ActiveSupport::Concern&lt;/code&gt; module. You
might ask — how are concerns so different from modules? The main difference is
that Rails concerns allow you to do a bit of magic, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# app/models/concerns/trashable.rb

module Trashable
  extend ActiveSupport::Concern

  included do
    scope :existing, -&amp;gt; { where(trashed: false) }
    scope :trashed, -&amp;gt; { where(trashed: true) }
  end

  def trash
    update_attribute :trashed, true
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You see that word included. It is a bit of Rails carbohydrates sprinkled upon
a Ruby module. What &lt;code&gt;ActiveSupport::Concern&lt;/code&gt; does for you is it allows you to put
code that you want evaluated inside the included block. For example, you want
to extract the trashing logic out of your model. The &lt;code&gt;included&lt;/code&gt; allows you to
do what we did and later include your model&amp;#39;s concern like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  include Trashable

  has_many :authors

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty handy and naive at this point, right? The Model lost a bit of weight and
trashing can now be reused throughout other models, not just our Song model.
Well, things can get complicated. Let&amp;#39;s dive in to find out.&lt;/p&gt;
&lt;h2&gt;A Classic Example of a Mixin&lt;/h2&gt;
&lt;p&gt;Before we embark further into the depths of concerns, let&amp;#39;s add another explanation of them.
When you see &lt;code&gt;include SomeModule&lt;/code&gt; or &lt;code&gt;extend AnotherModule&lt;/code&gt;, these are called &lt;a href=&quot;https://en.wikipedia.org/wiki/Mixin&quot;&gt;mixins&lt;/a&gt;.
A mixin is a set of code that can be added to other classes. And, as we all know from the
&lt;a href=&quot;https://ruby-doc.org/core-2.2.0/Module.html&quot;&gt;Ruby documentation&lt;/a&gt;, a module is
a collection of methods and constants. So what we are doing here is including
modules with methods and constants into different classes so that they can use them.&lt;/p&gt;
&lt;p&gt;That is exactly what we did with the &lt;code&gt;Trashable&lt;/code&gt; concern. We extracted common logic
around trashing a model object into a module. This module can later be included in
other places. So, &lt;strong&gt;mixin&lt;/strong&gt; is a design pattern used not only in Ruby and Rails.
But, wherever it&amp;#39;s used, people either like it and think it is good, or they hate it
and think it can easily spin out of control.&lt;/p&gt;
&lt;p&gt;To better understand this, we&amp;#39;ll go through a couple of pros and cons of using
them. Hopefully, by doing this, we can gain an understanding of when or whether to
use concerns.&lt;/p&gt;
&lt;h2&gt;I Have It All&lt;/h2&gt;
&lt;p&gt;When you decide to extract something to a concern, like &lt;code&gt;Trashable&lt;/code&gt; concern, you
now have access to all of the functionality of wherever &lt;code&gt;Trashable&lt;/code&gt; is included. This
brings great power, but as Richard Schneeman said in &lt;a href=&quot;https://rollout.io/blog/when-to-be-concerned-about-concerns/&quot;&gt;his blog post&lt;/a&gt;
on the topic — &amp;quot;with great power comes great ability to make complicated code&amp;quot;.
He meant complicating code that you might rely on, something that is
&lt;em&gt;supposed&lt;/em&gt; to be there in your concerns.&lt;/p&gt;
&lt;p&gt;If we take a look at the &lt;code&gt;Trashable&lt;/code&gt; once more:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module Trashable
  extend ActiveSupport::Concern

  included do
    scope :existing, -&amp;gt; { where(trashed: false) }
    scope :trashed, -&amp;gt; { where(trashed: true) }
  end

  def trash
    update_attribute :trashed, true
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The logic of the concern relies on the fact that the &lt;code&gt;trashed&lt;/code&gt; field exists
wherever the concern is included. Right? No biggie, this is what we want after
all. But, what I see happen is that people get tempted to pull in other stuff
from the model into the concern. To paint a picture of how this can happen,
let&amp;#39;s imagine that the &lt;code&gt;Song&lt;/code&gt; model has another method &lt;code&gt;featured_authors&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  include Trashable

  has_many :authors

  def featured_authors
    authors.where(featured: true)
  end

  # ...
end

class Album &amp;lt; ApplicationRecord
  include Trashable

  has_many :authors

  def featured_authors
    authors.where(featured: true)
  end

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To better illustrate, I added an &lt;code&gt;Album&lt;/code&gt; model that also includes &lt;code&gt;Trashable&lt;/code&gt;.
Let&amp;#39;s then say we want to notify featured authors of the song and the album
when they get trashed. People will get tempted to put this logic inside
the concern like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module Trashable
  extend ActiveSupport::Concern

  included do
    scope :existing, -&amp;gt; { where(trashed: false) }
    scope :trashed, -&amp;gt; { where(trashed: true) }
  end

  def trash
    update_attribute :trashed, true

    notify(featured_authors)
  end

  def notify(authors)
    # ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right here, things are starting to get complicated a bit. Since we have
trashing logic outside our Song model, we might be tempted to put notifying in
the &lt;code&gt;Trashable&lt;/code&gt; concern. In there, something &amp;quot;wrong&amp;quot; happens. The
&lt;code&gt;featured_authors&lt;/code&gt; is taken from the &lt;code&gt;Song&lt;/code&gt; model. OK, let&amp;#39;s say this passes
pull request review and CI checks.&lt;/p&gt;
&lt;p&gt;Then, a couple of months down the road, a new requirement is set where the
developer needs to change the way we present &lt;code&gt;featured_authors&lt;/code&gt; for songs. For
example, a new requirement wants to show only featured authors from Europe.
Naturally, the developer will find where featured authors are defined and edit
them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  include Trashable

  has_many :authors

  def featured_authors
    authors.where(featured: true).where(region: &amp;#39;Europe&amp;#39;)
  end

  # ...
end

class Album &amp;lt; ApplicationRecord
  include Trashable

  has_many :authors

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works nicely wherever we show authors, but after we deploy to production,
the folks from other parts of the world won&amp;#39;t get notified anymore about
their songs. Mistakes like these are easy to make when using concerns. The
example above is a simple and artificial one, but the ones that are &amp;quot;in
the wild&amp;quot; can be super tricky.&lt;/p&gt;
&lt;p&gt;What is risky here is that the concern (mixin) knows a lot about the model it gets
included in. It is what is called a &lt;strong&gt;circular dependency&lt;/strong&gt;. &lt;code&gt;Song&lt;/code&gt; and &lt;code&gt;Album&lt;/code&gt;
depend on &lt;code&gt;Trashable&lt;/code&gt; for trashing, &lt;code&gt;Trashable&lt;/code&gt; depends on both of them for
&lt;code&gt;featured_authors&lt;/code&gt; definition. The same can be said for the fact that a &lt;code&gt;trashed&lt;/code&gt; field
needs to exist in both models in order to have the &lt;code&gt;Trashable&lt;/code&gt; concern working.&lt;/p&gt;
&lt;p&gt;This is why a no-concern club might be against, and the pro-concern
club is for. I&amp;#39;d say, the &lt;em&gt;first&lt;/em&gt; version of &lt;code&gt;Trashable&lt;/code&gt; is the one I&amp;#39;d go
with in my codebase. Let&amp;#39;s see how we can make the second version with
notifying better.&lt;/p&gt;
&lt;h2&gt;Where Do Y&amp;#39;all Come From&lt;/h2&gt;
&lt;p&gt;Looking back at our &lt;code&gt;Trashable&lt;/code&gt; with notifying, we have to do something about it.
Another thing that happens when using concerns is that we tend to over-DRY things.
Let&amp;#39;s try to do that, for demonstration purposes, to our existing models by creating
another concern (bear with me on this one):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module Authorable
  has_many :authors

  def featured_authors
    authors.where(featured: true)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, our &lt;code&gt;Song&lt;/code&gt; and &lt;code&gt;Album&lt;/code&gt; will look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  include Trashable
  include Authorable

  # ...
end

class Album &amp;lt; ApplicationRecord
  include Trashable
  include Authorable

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We dried everything up, but now the requirement for featured authors from
Europe is not fulfilled. To make things worse, now the &lt;code&gt;Trashable&lt;/code&gt; concern and
the models depend on the &lt;code&gt;Authorable&lt;/code&gt;. What the hell? Exactly my question when
I was dealing with concerns some time ago. It&amp;#39;s hard to track down where
methods are coming from.&lt;/p&gt;
&lt;p&gt;My solution to all of this would be to keep &lt;code&gt;featured_authors&lt;/code&gt; as close to the
models as possible. The &lt;code&gt;notify&lt;/code&gt; method should &lt;strong&gt;not&lt;/strong&gt; be a part of &lt;code&gt;Trashable&lt;/code&gt;
concern at all. Each model should take care of that on its own, especially if
they tend to notify different subgroups. Let&amp;#39;s see how to do it less painfully:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;
# Concerns
module Trashable
  extend ActiveSupport::Concern

  included do
    scope :existing, -&amp;gt; { where(trashed: false) }
    scope :trashed, -&amp;gt; { where(trashed: true) }
  end

  def trash
    update_attribute :trashed, true
  end
end

module Authorable
  has_many :authors

  # Other useful methods that relate to authors across models.
  # If there are none, ditch the concern.
end

# Models
class Song &amp;lt; ApplicationRecord
  include Trashable
  include Authorable

  def featured_authors
    authors.where(featured: true).where(region: &amp;#39;Europe&amp;#39;)
  end

  # ...
end

class Album &amp;lt; ApplicationRecord
  include Trashable
  include Authorable

  def featured_authors
    authors.where(featured: true)
  end

  # ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Concerns like these are manageable and not too complex. I skipped the &lt;code&gt;notify&lt;/code&gt;
functionality I described earlier since that can be a topic for another day.&lt;/p&gt;
&lt;h1&gt;The Final Boss&lt;/h1&gt;
&lt;p&gt;As DHH, Rails&amp;#39; creator, so perfectly illustrated in &lt;a href=&quot;https://discuss.rubyonrails.org/t/helping-devs-understand-concerns-faster/74619/30&quot;&gt;this discussion thread&lt;/a&gt;, it is completely fine to reference concerns within other concerns. For example,
take a look at the code snippet which is referenced in the discussion and extracted
from Basecamp, a Rails project built and run by DHH and his team:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Identity &amp;lt; ApplicationRecord
  include Accessor, Authenticable, Boxes, ClearancePreapproval, Clipper, Creator, Eventable,
    Examiner, Filer, Member, PasswordReset, Poster, Reaching, Regional, TopicMerger
end

module Identity::Accessor
  def accessible_topics
    Topic.joins(:accesses).where(accesses: { contact: contacts })
  end

  def accessible_entries
    Entry.joins(:topic).merge(accessible_topics)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking at the code snippet, you are either opening your mouth in
awe or you are completely appalled. I feel there is no in-between here. If I got a chance to edit
this code, I would envision it as the &amp;quot;Final Concern Boss Fight&amp;quot;. But jokes
aside, the interesting thing here is that there are comments that say which concern
depends on which. Take a look at:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;  # ...

  include Subscribable # Depends on Readable
  include Eventable    # Depends on Recordables

  # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Putting comments like these can be helpful, but it&amp;#39;s still set up for doing
something sketchy, especially if you are new to the codebase. Being new and not
being aware of all the &amp;quot;gotchas&amp;quot; a code has can certainly send you on a
concern downward spiral.&lt;/p&gt;
&lt;p&gt;So how do you deal with the &amp;quot;final boss of concerns&amp;quot; fight? DHH offers this workaround &lt;a href=&quot;https://discuss.rubyonrails.org/t/helping-devs-understand-concerns-faster/74619/22&quot;&gt;in the same thread&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;...It’s a writing style. Like using subheads to explain subservient ideas within a broader context.
You &lt;em&gt;could&lt;/em&gt; probably extract all those subheads out, and turn them into little essays of their own.
But that’s often just not the right level of extraction. As you saw, the Accessor role starts life as
literally just two methods! It doesn’t warrant being turned into it’s own class.
It doesn’t have an independent center of gravity.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&amp;#39;s my feeling that taking this into consideration can help you do your concerns better.
But at the end of the day, when it comes to how you use concerns, it will really
depend on whether you are more comfortable with multiple inheritances from modules, or if you prefer composition. It&amp;#39;s your call.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;As we&amp;#39;ve seen, concerns are nothing more than modules that provide some useful syntax
sugar to extract and DRY up your code. If you have more useful tools under your
belt, maybe you shouldn&amp;#39;t reach out for concerns right away. Behavior like
handling file attachments and the trashing logic we showed in the examples
might be good candidates to extract into modules (concerns).&lt;/p&gt;
&lt;p&gt;Hopefully, you get to see the possible good and bad things when dealing with
concerns and modules in general. Bear in mind that no code is perfect. And in
the end, how can you learn what is good and what is bad for you if you don&amp;#39;t
try and possibly fail or succeed?&lt;/p&gt;
&lt;p&gt;No solution is perfect, and I hope you got to understand the Rails concerns way
of doing things in the blog post. As always, use your judgment and be aware of the
pros and cons.&lt;/p&gt;
&lt;p&gt;Until the next one, cheers!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Introduction to Ruby on Rails Patterns and Anti-patterns</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/08/05/introduction-to-ruby-on-rails-patterns-and-anti-patterns.html"/>
    <id>https://blog.appsignal.com/2020/08/05/introduction-to-ruby-on-rails-patterns-and-anti-patterns.html</id>
    <published>2020-08-05T00:00:00+00:00</published>
    <updated>2020-08-05T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Dig into the basics of design patterns and anti-patterns.</summary>
    <content type="html">&lt;p&gt;Welcome to the first post in our series about Ruby on Rails Patterns and Anti-patterns. In each of the posts, we&amp;#39;ll take a deep dive into all sorts of patterns you might come across while working with Rails apps.&lt;/p&gt;
&lt;p&gt;Today, we&amp;#39;ll show what a (design) pattern is and then try
to explain what an anti-pattern is as well. To better illustrate explanations, we will use the
Ruby on Rails framework that has been around for quite some time. If Rails isn&amp;#39;t
your cup of tea for some reason, hang on, the ideas (or patterns) described
here might resonate with whatever technology you wind up using.&lt;/p&gt;
&lt;p&gt;But before we jump into explaining what patterns and anti-patterns are, how did
we get to the point where we need them? Why do we need to have all these things
for our software? Why do we need to &lt;strong&gt;design&lt;/strong&gt; our solution?&lt;/p&gt;
&lt;h2&gt;Yes, You Are a Designer&lt;/h2&gt;
&lt;p&gt;Even from early computer programming days, people had to deal with the design
of the programs they were writing. To write a program (or software) is to
design a solution for a problem. When you write software, you are a designer—feel
free to append that to your job title. Designing good solutions
is important because the software we write will be read and/or edited by others.
Also, the solutions we come up with will be built on by others
in the future.&lt;/p&gt;
&lt;p&gt;Having all this in mind, generations of engineers started seeing similar
designs in code and architecture throughout their careers. Folks started
extracting and documenting standard solutions to problems. Some would say it&amp;#39;s
a natural way of how we as humans function. We like to &lt;a href=&quot;https://en.wikipedia.org/wiki/Principles_of_grouping&quot;&gt;categorize&lt;/a&gt;
and &lt;a href=&quot;https://en.wikipedia.org/wiki/Gestalt_psychology#Pr%C3%A4gnanz&quot;&gt;find patterns&lt;/a&gt;
in everything, and software is no exception to that.&lt;/p&gt;
&lt;p&gt;Being human, as we are, patterns started emerging more and more as software
engineering got more complex. Software design patterns began to develop and
cement themselves with engineers around the world. Books, essays, and talks were
given, further spreading ideas of well thought out and battle-tested solutions.
Those solutions saved a lot of people time and money, so let&amp;#39;s
go over the term design pattern, and see what it truly is.&lt;/p&gt;
&lt;h2&gt;What Is a Design Pattern?&lt;/h2&gt;
&lt;p&gt;In software engineering, a pattern is described as a solution that can be
reused to solve a common problem. The pattern is something that is considered a
good practice among software engineers. Since software engineers set them, they
can quickly go from patterns to their opposite—anti-patterns—but we&amp;#39;ll get
to that later.&lt;/p&gt;
&lt;p&gt;A design pattern will show you the way to the solution but it won&amp;#39;t give you a
piece of code ready to be plugged into the rest of your software. Think of a
pattern as a guide for writing well-designed code, but you have to come up
with the implementation. Using patterns in day-to-day coding emerged in
the late &amp;#39;80s, where Kent Beck and Ward Cunningham came up with an idea of
using a &lt;a href=&quot;http://c2.com/doc/oopsla87.html&quot;&gt;&amp;#39;pattern language&amp;#39;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The idea of pattern languages came in the late &amp;#39;70s by Christopher Alexander in his book
&lt;a href=&quot;https://www.goodreads.com/book/show/79766.A_Pattern_Language&quot;&gt;A Pattern Language&lt;/a&gt;.
You might be surprised, but the book is not about software engineering but the
architecture of buildings. The pattern language is an organized and coherent
set of patterns, each of which describes a problem and the core of a solution
that can be used in many ways. Sounds familiar? (Hint: frameworks, another
hint: Rails)&lt;/p&gt;
&lt;p&gt;Later on, design patterns in software engineering became famous with large audiences after
the legendary book &lt;a href=&quot;https://www.goodreads.com/book/show/85009.Design_Patterns&quot;&gt;Design Patterns&lt;/a&gt; by the
&lt;a href=&quot;http://wiki.c2.com/?GangOfFour&quot;&gt;Gang Of Four&lt;/a&gt; published in 1994. In the
book, there are explanations and definitions of patterns that are used nowadays
— Factory, Singleton, Decorator, just to name a few.&lt;/p&gt;
&lt;p&gt;Great, now that we got acquainted or refreshed our knowledge on design
and patterns, let&amp;#39;s find out what anti-patterns are.&lt;/p&gt;
&lt;h2&gt;What Is a Design Anti-Pattern?&lt;/h2&gt;
&lt;p&gt;If you think of patterns as the good guys, the anti-patterns are the bad ones.
To be more precise, a software anti-pattern is a pattern that may be commonly
used but is considered ineffective or counterproductive. Typical examples of
anti-patterns are God objects that contain many functions and dependencies,
which could be extracted and separated into different objects.&lt;/p&gt;
&lt;p&gt;Common causes of anti-patterns in code are many. For example, a good one
is when the good guy (pattern) becomes the bad guy (an anti-pattern). Let&amp;#39;s say
you got used to using a particular technology at your previous company, and you
gained a high level of competence in it. For the sake of the example, let&amp;#39;s use Docker. You know how to efficiently pack applications into Docker
containers, orchestrate them in the cloud, and pull their logs down from the
cloud. Suddenly, you get a new job where you need to ship front end
applications. Since you know a lot about Docker and how to ship apps with it,
your first decision is to package everything up and deploy it to the cloud.&lt;/p&gt;
&lt;p&gt;But, little did you know, the front end apps are not that complex at your
current job, and putting them into containers might not be the most effective
solution. It first sounds like a good idea, but later down the road, it proves
as counterproductive. This anti-pattern is called
&lt;a href=&quot;https://en.wikipedia.org/wiki/Law_of_the_instrument&quot;&gt;&amp;quot;Golden Hammer&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It can be summed up with the saying, &amp;quot;If you have a hammer, everything looks
like a nail&amp;quot;. If you are really good with Docker and orchestration of services,
everything is a Docker service made to be orchestrated in the cloud.&lt;/p&gt;
&lt;p&gt;These things happen and will happen. Good guys turn to bad buys, and
vice-versa. But where do Ruby and Rails fit into this picture?&lt;/p&gt;
&lt;h2&gt;Ruby First, Then Rails&lt;/h2&gt;
&lt;p&gt;Most folks were introduced to Ruby by using Ruby on Rails, a popular framework
for building websites quickly. I got acquainted with Ruby in the same way, nothing
wrong with that. Rails is based on this well-established software pattern
called Model-View-Controller, or MVC for short. But before we dive into details
of the MVC pattern in Rails, one big fallacy that often happens is using Rails
without learning Ruby properly.&lt;/p&gt;
&lt;p&gt;The Rails framework was one of the go-to frameworks when you had an idea and
wanted to build it fast. Nowadays, it&amp;#39;s a whole different story, Rails is still
used, but not to the extent it was in its prime. Being so easy to use and run, a lot
of beginners set out to build their web apps using rails new command. What
happened then, along the road, problems started occurring. As a beginner, you
are lured by the speed and simplicity of development with Rails, and everything
works so magically and smoothly at first. Then you see you&amp;#39;ve taken a lot of
&amp;#39;magic&amp;#39; for granted, and you don&amp;#39;t understand what is going on behind the
curtain.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;I had this problem, and I&amp;#39;m sure many beginners and advanced beginners are
suffering from it. You start with a framework in hand, you build on it, and
when you try to add something highly custom, you can&amp;#39;t, because you&amp;#39;ve used up
all the magic points from that framework. At that point, you have to go back to
the beginning and learn the basics. Going back is no biggie, happens to the
best of us. But the problem grows more significant if you move on without
learning the essential things, like in Ruby. One good book that can help you in
this regard is &lt;a href=&quot;https://www.goodreads.com/book/show/3892688-the-well-grounded-rubyist&quot;&gt;The Well-Grounded Rubyist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As a beginner, you don&amp;#39;t have to read it from start to end. But keep it by your
side so you can consult it quickly. I am not saying that you should suddenly
stop whatever you were doing and read the whole book, but stop from time to
time and refresh your knowledge of the Ruby basics, it might open some new
horizons for you.&lt;/p&gt;
&lt;h2&gt;MVC: Rails&amp;#39; Bread &amp;amp; Butter&lt;/h2&gt;
&lt;p&gt;OK, but what about MVC? The Model-View-Controller pattern has been around for
ages. It&amp;#39;s been adopted by many frameworks across a plethora of languages like
Ruby (Rails), Python (Django), Java (Play, Spring MVC). The idea is to have
separate components that each do their job:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Model handles data and business logic.&lt;/li&gt;
&lt;li&gt;The View is for the presentation of the data and the user interface.&lt;/li&gt;
&lt;li&gt;The Controller ties the two together by getting data from the Model and showing the View to the user.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sounds great in theory, and it&amp;#39;s excellent when the logic is minimal and
your website doesn&amp;#39;t hold complex logic. That is where things get tricky, but
we&amp;#39;ll get to that in a second.&lt;/p&gt;
&lt;p&gt;MVC spread out like wildfire throughout the web development community. Even
libraries like React, which is insanely popular these days is explained as the
view layer of your web app. No other pattern has been popularized so much that
it cannot be shaken off. Rails added the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern&quot;&gt;Publish-Subscribe&lt;/a&gt;
with ActionCable, where the concept of
&lt;a href=&quot;https://guides.rubyonrails.org/action_cable_overview.html#terminology&quot;&gt;channels is described as the controller&lt;/a&gt;
of the MVC pattern.&lt;/p&gt;
&lt;p&gt;But what are the anti-patterns there, in the so widely used pattern? Let&amp;#39;s go
over some of the most common anti-patterns for each part of the MVC pattern.&lt;/p&gt;
&lt;h3&gt;Model Problems&lt;/h3&gt;
&lt;p&gt;As an application grows and business logic gets expanded, folks tend to
overcrowd their models. Constant growth can lead to an anti-pattern called the
Fat Model.&lt;/p&gt;
&lt;p&gt;The famous &amp;#39;Fat Model, Skinny Controller&amp;#39; pattern identifies as a bad guy, some
as the good guy. We will say that having any of the fat is an anti-pattern. To
better understand it, let&amp;#39;s get into an example. Imagine we have a streaming
service like Spotify or Deezer. Inside it, we have a model for songs like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  belongs_to :album
  belongs_to :artist
  belongs_to :publisher

  has_one :text
  has_many :downloads

  validates :artist_id, presence: true
  validates :publisher_id, presence: true

  after_update :alert_artist_followers
  after_update :alert_publisher

  def alert_artist_followers
    return if unreleased?

    artist.followers.each { |follower| follower.notify(self) }
  end

  def alert_publisher
    PublisherMailer.song_email(publisher, self).deliver_now
  end

  def includes_profanities?
    text.scan_for_profanities.any?
  end

  def user_downloaded?(user)
    user.library.has_song?(self)
  end

  def find_published_from_artist_with_albums
    ...
  end

  def find_published_with_albums
    ...
  end

  def to_wav
    ...
  end

  def to_mp3
    ...
  end

  def to_flac
    ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem with models like these is that they become a dumping ground for the
different logic that might be related to a song. This happens as methods get
added slowly one-by-one over time. The whole model then seems large and
complex, and splitting the logic into a couple of other places could prove
beneficial in the future.&lt;/p&gt;
&lt;p&gt;Right off the bat, you can see that there are some recommended practices that this
model is breaking. It is breaking the &lt;a href=&quot;https://en.wikipedia.org/wiki/Single-responsibility_principle&quot;&gt;Single Responsibility Principle&lt;/a&gt;
(SRP). It deals with notifying followers and the publisher. It checks the text
for profanities, has methods for exporting the song to different audio
formats, and so on. Having all this adds to the model&amp;#39;s complexity, and I
cannot even imagine the test file for this model.&lt;/p&gt;
&lt;p&gt;How to refactor this model majorly depends on how methods are called and used
in other places. I will present some general ideas of how we can handle these,
and you can choose the one that fits your case the best.&lt;/p&gt;
&lt;p&gt;The callbacks that notify followers and the publisher could be extracted to
jobs. The jobs will get enqueued and the logic kept out of the model,
like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class NotifyFollowers &amp;lt; ApplicationJob
  def perform(followers)
    followers.each { |follower| follower.notify }
  end
end

class NotifyPublisher &amp;lt; ApplicationJob
  def perform(publisher, song)
    PublisherMailer.song_email(publisher, self).deliver_now
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jobs will run on their own in the separate process, away from the model. Now you
can test your job logic separately and just check whether the proper job was
enqueued from your model.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say that checking for profanities and whether the user has downloaded the
song is all happening in the view part of our app. In that case, we could use a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Decorator_pattern&quot;&gt;Decorator pattern&lt;/a&gt;. One popular solution
that can get you started quickly is &lt;a href=&quot;https://github.com/drapergem/draper&quot;&gt;Draper gem&lt;/a&gt;. With it,
you could write a decorator similar to this one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class SongDecorator &amp;lt; Draper::Decorator
  delegate_all

  def includes_profanities?
    object.text.scan_for_profanities.any?
  end

  def user_downloaded?(user)
    object.user.library.has_song?(self)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, you would call &lt;code&gt;decorate&lt;/code&gt; in your controller, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def show
  @song = Song.find(params[:id]).decorate
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And use it in your views like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= @song.includes_profanities? %&amp;gt;
&amp;lt;%= @song.user_downloaded?(user) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don&amp;#39;t like using a dependency, you can roll your decorator,
but we&amp;#39;ll talk about this in another blog post. Now that you&amp;#39;ve got the
majority of your model concerns separated, let&amp;#39;s deal with the methods for
finding songs and converting a song. We can use modules to separate them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;module SongFinders
  def find_published_from_artist_with_albums
    ...
  end

  def find_published_with_albums
    ...
  end
end

module SongConverter
  def to_wav
    ...
  end

  def to_mp3
    ...
  end

  def to_flac
    ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Song model will extend the &lt;code&gt;SongFinders&lt;/code&gt; module, so its methods are available
as class methods. The Song model will include the &lt;code&gt;SongConverter&lt;/code&gt; module, so its
methods are available on the model instances.&lt;/p&gt;
&lt;p&gt;All of this should make our Song model pretty slim and on point:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class Song &amp;lt; ApplicationRecord
  extend SongFinders
  include SongConverter

  belongs_to :album
  belongs_to :artist
  belongs_to :publisher

  has_one :text
  has_many :downloads

  validates :artist_id, presence: true
  validates :publisher_id, presence: true

  after_update :alert_artist_followers, if: :published?
  after_update :alert_publisher

  def alert_artist_followers
    NotifyFollowers.perform_later(self)
  end

  def alert_publisher
    NotifyPublisher.perform_later(publisher, self)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are many more model anti-patterns, and this is just one example of what
can go south with models. Stay tuned for another blog post in this series,
where we&amp;#39;ll go into details about more model anti-patterns. For now,
let&amp;#39;s see what can go wrong with views.&lt;/p&gt;
&lt;h2&gt;View Problems&lt;/h2&gt;
&lt;p&gt;Besides model problems, Rails folks can sometimes struggle with the complexity of their views.
Back in the day, HTML and CSS were the kings of the view part of web
applications. Slowly over time, JavaScript came to reign, and almost all
aspects of the front end were written in JavaScript. Rails follows a bit
different paradigm regarding this. Instead of having everything in JavaScript
in view, you should only &amp;quot;sprinkle&amp;quot; JS onto it.&lt;/p&gt;
&lt;p&gt;In any case, having to deal
with HTML, CSS, JS, and Ruby at the same place can get messy. What&amp;#39;s tricky
with building Rails views is that the domain logic can sometimes be found
inside the view. This is a no-no since it breaks the MVC pattern, for a start.&lt;/p&gt;
&lt;p&gt;Another case could be using too much embedded Ruby in your views and partials.
Maybe some of the logic could go inside a helper or a decorator (also known as
the view model or a presenter). We will get into the examples of it in some
of the next posts in the series, so stay tuned.&lt;/p&gt;
&lt;h2&gt;Controller Problems&lt;/h2&gt;
&lt;p&gt;Rails controllers can also suffer from a variety of different problems. One of
them is a Fat Controller anti-pattern.&lt;/p&gt;
&lt;p&gt;Before, our model was fat, but it lost some weight, and now we notice that the
controller has added some extra weight in the process. Usually, this happens
when the business logic is put inside the Controller, but its actual place is
in the model or elsewhere. Some of the ideas shared in the large Model section
can still apply to the controller — extracting code to presenters, using
ActiveRecord callbacks, resorting to
&lt;a href=&quot;/2020/06/17/using-service-objects-in-ruby-on-rails.html&quot;&gt;Service objects&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some folks even resort to using gems like
&lt;a href=&quot;https://github.com/trailblazer/trailblazer&quot;&gt;Trailblazer&lt;/a&gt; or
&lt;a href=&quot;https://dry-rb.org/gems/dry-transaction/&quot;&gt;dry-transaction&lt;/a&gt;.
The idea here is to create classes that deal with specific transactions. Moving
everything out of the controller and keeping the model skinny, you store and
test logic inside these separate classes, which some call services,
transactions, actions, and similar.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There are many more anti-patterns and even more solutions for them. To try to
cover everything in this post will take too much space and time and it
will make our post look fat (like the model and controller we talked
about). Be sure to follow our series, where we&amp;#39;ll deep dive into every
aspect of the MVC pattern in Rails. There, you&amp;#39;ll find out how to deal with
the most famous anti-patterns. Until then, I hope you enjoyed this overview of
what patterns and anti-patterns are and the most common ones in the Ruby on Rails
framework.&lt;/p&gt;
&lt;p&gt;Until the next one, cheers!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Scaling Queue Workers Efficiently with AppSignal Metrics</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/07/08/scaling-queue-workers-efficiently-with-appsignal-metrics.html"/>
    <id>https://blog.appsignal.com/2020/07/08/scaling-queue-workers-efficiently-with-appsignal-metrics.html</id>
    <published>2020-07-08T00:00:00+00:00</published>
    <updated>2020-07-08T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s use some simple math and performance data to find our optimal worker count, based on the work waiting in the queue.</summary>
    <content type="html">&lt;p&gt;Most web apps can benefit from a background queue, often used to process error-prone or time-consuming side jobs.
These background jobs can vary from sending emails, to updating caches, to performing core business logic.&lt;/p&gt;
&lt;p&gt;As any background queueing system scales the number of jobs it needs to process, the pool of workers processing those jobs needs to scale as well.
In cases where the rate of jobs being enqueued varies, scaling the number of queue workers up becomes a key aspect in maintaining processing speed.
Additionally, scaling down workers during low queue throughput can provide significant savings!&lt;/p&gt;
&lt;p&gt;Unfortunately, many queueing backends don&amp;#39;t come equipped with scaling logic to turn workers on or off.
But we can use some simple math and performance data to find our optimal worker count based on the work waiting in the queue.&lt;/p&gt;
&lt;h2&gt;Queueing Rule of Thumb&lt;/h2&gt;
&lt;p&gt;If jobs are enqueued at a higher rate than they are processed by the queue workers, the depth of the queue will grow and the time that each job spends in the queue will also grow.
Generally, we want the wait time (amount of time in the queue) for each job to be as low as possible — from 0 seconds up to some acceptable limit.
To estimate the number of workers required to satisfy a desired wait time, we can use the &lt;a href=&quot;https://en.m.wikipedia.org/wiki/Queuing_Rule_of_Thumb&quot;&gt;Queueing Rule of Thumb&lt;/a&gt; (QROT).
Usually, the QROT is expressed as an inequality describing the number of servers required to service a queue of jobs, but one form can be written as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;workers = (number_of_jobs * avg_service_time_per_job) / time_to_finish_queue
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, if we want to figure out the number of workers required to service our queue in a desired time of, say, 30 seconds, we just need to know the number of jobs (size of the queue) and the average time it takes to execute each job.
For example, if we have a queue of 7500 jobs and each job takes an average of 0.3 seconds to execute, then we can finish that queue in 30 seconds with 75 workers.&lt;/p&gt;
&lt;h2&gt;Accessing Performance Metrics&lt;/h2&gt;
&lt;p&gt;In order to estimate the average service time for jobs in the queue, we need access to performance metrics for each job class.
Luckily, &lt;a href=&quot;https://appsignal.com&quot;&gt;AppSignal&lt;/a&gt; records the performance data for common queueing backends out-of-the-box, recording metrics for each time a job has been executed.&lt;/p&gt;
&lt;p&gt;We can use the upcoming AppSignal GraphQL API to get the average duration of each job type over the last 24 hours.
This API is not fully public yet, though it is currently used for AppSignal&amp;#39;s &lt;a href=&quot;https://appsignal.com/redirect-to/app?to=performance/graphs&quot;&gt;Performance graphs&lt;/a&gt; and other data displays.
Luckily, &lt;a href=&quot;https://graphql.org&quot;&gt;GraphQL&lt;/a&gt; APIs are intended to be self-documenting, and we can use a tool like &lt;a href=&quot;https://github.com/graphql/graphiql&quot;&gt;GraphiQL&lt;/a&gt; to introspect the API and find out what data objects it exposes.&lt;/p&gt;
&lt;p&gt;The process of building a GraphQL query is outside the scope of this post, but below is an example Ruby class that connects to the AppSignal GraphQL API using the popular &lt;a href=&quot;https://lostisland.github.io/faraday/&quot;&gt;Faraday&lt;/a&gt; HTTP client library to query for a basic metrics aggregation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;require &amp;#39;json&amp;#39;
require &amp;#39;faraday&amp;#39;

class AppsignalClient
  BASE_URL = &amp;#39;https://appsignal.com/&amp;#39;
  DEFAULT_APP_ID = ENV[&amp;#39;APPSIGNAL_APP_ID&amp;#39;]
  DEFAULT_TOKEN = ENV[&amp;#39;APPSIGNAL_API_TOKEN&amp;#39;]
  # GraphQL query to fetch the &amp;quot;mean&amp;quot; metric for the selected app.
  METRICS_QUERY = &amp;lt;&amp;lt;~GRAPHQL.freeze
    query($appId: String!, $query: [MetricAggregation!]!, $timeframe: TimeframeEnum!) {
      app(id: $appId) {
        metrics {
          list(timeframe: $timeframe, query: $query) {
            start
            end
            rows {
              fields {
                key
                value
              }
            }
          }
        }
      }
    }
  GRAPHQL

  def initialize(app_id: DEFAULT_APP_ID, client_secret: DEFAULT_TOKEN)
    @app_id = app_id
    @client_secret = client_secret
  end

  # Fetch the average duration for a job class&amp;#39;s perform action
  # Default timeframe is last 24 hours
  def average_job_duration(job_class, timeframe: &amp;#39;R24H&amp;#39;)
    response =
      connection.post(
        &amp;#39;graphql&amp;#39;,
        JSON.dump(
          query: METRICS_QUERY,
          variables: {
            appId: @app_id,
            timeframe: timeframe,
            query: [
              name: &amp;#39;transaction_duration&amp;#39;,
              headerType: legacy
tags: [
                { key: &amp;#39;namespace&amp;#39;, value: &amp;#39;background&amp;#39; },
                { key: &amp;#39;action&amp;#39;, value: &amp;quot;#{job_class.name}#perform&amp;quot; },
              ],
              fields: [{ field: &amp;#39;MEAN&amp;#39;, aggregate: &amp;#39;AVG&amp;#39; }],
            ],
          }
        )
      )
    data = JSON.parse(response.body, symbolize_names: true)
    rows = data.dig(:data, :app, :metrics, :list, :rows)
    # There may be no metrics in the selected timeframe
    return 0.0 if rows.empty?

    rows.first[:fields].first[:value]
  end

  private

  def connection
    @connection ||= Faraday.new(
      url: BASE_URL,
      params: { token: @client_secret },
      headers: { &amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;application/json&amp;#39; },
      request: { timeout: 10 }
    ) do |faraday|
      faraday.response :raise_error
      faraday.adapter Faraday.default_adapter
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this class, we can get an average job duration for a given ActiveJob class, returned to us in milliseconds:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;AppsignalClient.new.average_job_duration(MyMailerJob)
# =&amp;gt; 233.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, this calls for the average transaction duration of the job over the last 24 hours of data.
If our job(s) are executed much more frequently than that, we may want to shorten that window, weighing recent executions more heavily in our average.
For example, if we have jobs that run hundreds of times an hour, we may want to change our &lt;code&gt;timeframe&lt;/code&gt; to one hour (&lt;code&gt;R1H&lt;/code&gt;) to better estimate the duration of one such job if executed right now.&lt;/p&gt;
&lt;p&gt;Note that this performance data is separate from our server utilization data.
This data tells us how long it&amp;#39;ll actually take to do the work required for each job.
This will be more useful to us in scaling our workers than external measurements like utilization metrics.&lt;/p&gt;
&lt;h2&gt;Introspecting the Queue&lt;/h2&gt;
&lt;p&gt;Next, we need to introspect our queue to determine the jobs to be serviced.
A common Ruby queueing backend is &lt;a href=&quot;https://github.com/resque/resque&quot;&gt;Resque&lt;/a&gt;, which also integrates nicely with ActiveJob.
We can access the enqueued jobs for a given queue in Resque and then estimate the execution time for each job based on its class, using our &lt;code&gt;AppsignalClient&lt;/code&gt; class from above.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;require &amp;#39;resque&amp;#39;

class ResqueEstimator
  def initialize(queue: &amp;#39;default&amp;#39;)
    @queue = queue
    @cache = {}
    @appsignal_client = AppsignalClient.new
  end

  def enqueued_duration_estimate
    Resque.data_store.everything_in_queue(queue).map do |job|
      estimate_job_duration decode_activejob_args(job)
    end.sum
  end

  def estimate_job_duration(job)
    @cache[job[&amp;#39;job_class&amp;#39;]] ||= @appsignal_client
                                 .average_job_duration job[&amp;#39;job_class&amp;#39;]
  end

  private

  # ActiveJob-specific method for parsing job arguments
  # for ActiveJob+Resque integration
  def decode_activejob_args(job)
    decoded_job = job
    decoded_job = Resque.decode(job) if job.is_a? String
    decoded_job[&amp;#39;args&amp;#39;].first
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using this class is as simple as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;ResqueEstimator.new(queue: &amp;#39;my_queue&amp;#39;).enqueued_duration_estimate
# =&amp;gt; 23000 (ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that we use a simple memoization of the job durations in our &lt;code&gt;estimate_job_duration&lt;/code&gt; method to avoid duplicate calls to the AppSignal API.
Most likely, our queue will contain many jobs of the same class and we can reduce our overhead by only estimating the execution of each class once.&lt;/p&gt;
&lt;h2&gt;Using Performance Data to Scale&lt;/h2&gt;
&lt;p&gt;Pulling all this together, we can now use our recent performance data to scale our queue workers up or down based on the content of our queue!
At any moment, we can look at the jobs in our queue and get an estimate of the workers required to service it in our desired time limit.&lt;/p&gt;
&lt;p&gt;We will need to decide on a desired queueing time limit (the maximum amount of time any job should wait in the queue), e.g. 30 seconds.
We will also need to specify a minimum and maximum worker count.
It&amp;#39;s helpful to keep at least one worker running for the queue, to handle the first job(s) enqueued after the queue has been empty for a while.
We will also want a maximum worker count, to avoid over-scaling our database connections and/or server utilization costs with too many workers.&lt;/p&gt;
&lt;p&gt;We can make a class to handle this logic for us, which is basically just our implementation of the Queueing Rule of Thumb from before.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class ResqueWorkerScaler
  def initialize(queue: &amp;#39;default&amp;#39;, workers_range: 1..100, desired_wait_ms: 300_000)
    @queue = queue
    @workers_range = workers_range
    @desired_wait_ms = desired_wait_ms
    @estimator = ResqueEstimator.new(queue: @queue)
  end

  def desired_workers
    total_time_ms = @estimator.enqueued_duration_estimate
    workers_required = [(total_time_ms / desired_wait_ms).ceil, workers_range.last].min
    [workers_required, workers_range.first].max
  end

  def scale
    # using platform-specific scaling interface, scale to desired_workers
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will want to scale our workers on a regular interval so that we are scaling up and down based on demand. We can make a Rake task that calls our &lt;code&gt;ResqueWorkerScaler&lt;/code&gt; class to scale workers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;# inside lib/tasks/resque_workers.rake

namespace :resque_workers do
  desc &amp;#39;Scale worker pool based on enqueued jobs&amp;#39;
  task :scale, [:queue] =&amp;gt; [:environment] do |_t, args|
    queue = args[:queue] || &amp;#39;default&amp;#39;
    ResqueWorkerScaler.new(queue: queue).scale
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we can set up a cron job to run this Rake task on a regular interval:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;*/5 * * * * /path/to/our/rake resque_workers:scale
# scale a non-default queue:
*/5 * * * * /path/to/our/rake resque_workers:scale[&amp;#39;my_queue&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that we set the scaling task to run every 5 minutes.
Each new worker will take some amount of time to come online and begin processing jobs — likely anywhere from 10-40 seconds, depending on the size of our codebase and the number of gems we use.
So, if we try to scale our workers every minute, we will likely be scaling up or down again before our desired changes have taken effect.
If our app is only seeing queue usage fluctuate at different times of the day, we can likely call our Rake task at an hourly interval.
But if our queue size fluctuates within the hour, we will want to introspect our queue at a more frequent interval, like the 5 minutes above.&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;Such a system where actual performance data is used to scale infrastructure can be very responsive to demand and resilient to varied usage.
Especially in an environment like background processing, where host metrics like memory usage and load average are unlikely to vary, using performance metrics to scale is much more appropriate.&lt;/p&gt;
&lt;p&gt;Alternate queue scaling implementations could measure the mean wait time per job instead of introspecting the full queue, but that metric can be unrepresentative when the queue contents and size change rapidly.
If our system experiences widely variable load, with lots of jobs enqueueing at once, or widely variable job execution times, then queue introspection is much faster to respond and reliably correct.&lt;/p&gt;
&lt;p&gt;But there are some limitations to consider in our queue introspection system.
If the queue is sufficiently large, looking at each job for an execution estimate will be prohibitively slow.
In such cases, it can be better to find the total job count, then select a random representative sampling of jobs from the queue and calculate the average execution from that sample.
Alternatively, if a job class has no performance data associated with it yet, we will need to use an assumed execution time until it has been executed and recorded a few times.&lt;/p&gt;
&lt;p&gt;The system outlined above can also be improved significantly with a few tweaks.
Consider estimating the execution time for each job class in parallel, as each estimation is isolated and idempotent.
We can also update our queue introspection to include those jobs currently being executed by a worker to improve the accuracy of our total service time estimate.
For a background processing architecture with multiple queues, we can assign each queue a desired wait time, based on queue priority, and scale workers appropriately.&lt;/p&gt;
&lt;p&gt;Queueing systems tend to collect a lot of the highly variable work in any project.
With performance data on the execution of the jobs from the queue, we can effectively scale resources to service all that work in a responsive, efficient manner.&lt;/p&gt;
&lt;p&gt;Happy scaling!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Using Service Objects in Ruby on Rails</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/06/17/using-service-objects-in-ruby-on-rails.html"/>
    <id>https://blog.appsignal.com/2020/06/17/using-service-objects-in-ruby-on-rails.html</id>
    <published>2020-06-17T00:00:00+00:00</published>
    <updated>2020-06-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out what service objects are and why you should use them.</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;This article has been modified from its original appearance in &lt;a href=&quot;https://playbookthirtynine.com&quot;&gt;Playbook Thirty-nine&lt;/a&gt; - &lt;em&gt;A Guide to Shipping Interactive Web Apps with Minimal Tooling&lt;/em&gt;, and tailored to fit this guest post for AppSignal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There’s a lot of functionality that your app needs to handle, but that logic doesn’t necessarily belong in the controller or even the model. Some examples include checking out with a cart, registering for the site, or starting a subscription.&lt;/p&gt;
&lt;p&gt;You could include all this logic in the controller, but you’ll keep repeating yourself, calling the same logic in all those places. You could put the logic in a model, but sometimes, you need access to things that are easily available in the controller, like an IP address, or a parameter in a URL. What you need is a service object.&lt;/p&gt;
&lt;p&gt;The job of a service object is to encapsulate functionality, execute one service, and provide a single point of failure. Using service objects also prevents developers from having to write the same code over and over again when it’s used in different parts of the application.&lt;/p&gt;
&lt;p&gt;A service object is just a Plain Old Ruby Object (&amp;quot;PORO&amp;quot;). It’s just a file that lives under a specific directory. It’s a Ruby class that returns a predictable response. What makes the response predicable is due to three key parts. All service objects should follow the same pattern.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Has an initialization method with a params argument.&lt;/li&gt;
&lt;li&gt;Has a single public method named call.&lt;/li&gt;
&lt;li&gt;Returns an OpenStruct with a success? and either a payload or an error.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What’s an OpenStruct?&lt;/h2&gt;
&lt;p&gt;It’s like the brainchild of a class and a hash. You can think of it as a mini-class that can receive arbitrary attributes. In our case, we’re using it as a sort of temporary data structure that handles just two attributes.&lt;/p&gt;
&lt;p&gt;If the success is &lt;code&gt;true&lt;/code&gt;, it returns a payload of data.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OpenStruct.new({success ?:true, payload: &amp;#39;some-data&amp;#39;})&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If the success is &lt;code&gt;false&lt;/code&gt;, it returns an error.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OpenStruct.new({success ?:false, error: &amp;#39;some-error&amp;#39;})&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here’s an example of a service object that reaches out and grabs data from AppSignals new &lt;a href=&quot;https://docs.appsignal.com/api/&quot;&gt;API&lt;/a&gt;, which is currently in beta.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module AppServices

  class AppSignalApiService

    require &amp;#39;httparty&amp;#39;

    def initialize(params)
      @endpoint   = params[:endpoint] || &amp;#39;markers&amp;#39;
    end

    def call
      result = HTTParty.get(&amp;quot;https://appsignal.com/api/#{appsignal_app_id}/#{@endpoint}.json?token=#{appsignal_api_key}&amp;quot;)
    rescue HTTParty::Error =&amp;gt; e
      OpenStruct.new({success?: false, error: e})
    else
      OpenStruct.new({success?: true, payload: result})
    end

    private

      def appsignal_app_id
        ENV[&amp;#39;APPSIGNAL_APP_ID&amp;#39;]
      end

      def appsignal_api_key
        ENV[&amp;#39;APPSIGNAL_API_KEY&amp;#39;]
      end

  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You would call the file above with &lt;code&gt;AppServices::AppSignalApiService.new({endpoint: &amp;#39;markers&amp;#39;}).call&lt;/code&gt;. I make liberal use of OpenStruct to return a predictable response. This is really valuable when it comes to writing tests because all of the logic’s architectural patterns are identical.&lt;/p&gt;
&lt;h2&gt;What&amp;#39;s a Module?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-06/module_screenshot.png&quot; alt=&quot;A screenshot of the file directory holding our service objects&quot;/&gt; Using modules provide us with name-spacing and prevents colliding with other classes. This means you can use the same method names in all the classes and they won’t clash because they’re under a specific namespace.&lt;/p&gt;
&lt;p&gt;Another key part of the module name is how files are organized in our app. Service objects are kept in a services folder in the project. The service object example above, with the module name of &lt;code&gt;AppServices&lt;/code&gt;, falls into the &lt;code&gt;AppServices&lt;/code&gt; folder in the services directory.&lt;/p&gt;
&lt;p&gt;I organize my service directory into multiple folders, each containing functionality for a specific part of the application.&lt;/p&gt;
&lt;p&gt;For example, the &lt;code&gt;CloudflareServices&lt;/code&gt; directory holds specific service objects for creating and removing subdomains on Cloudflare. The Wistia and Zapier services hold their respective service files.&lt;/p&gt;
&lt;p&gt;Organizing your service objects like this yields better predictability when it comes down to implementation, and it’s easy to see at a glance what the app is doing from a 10k-foot view.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2020-06/services_screenshot.png&quot; alt=&quot;A screenshot of the file directory holding our service objects&quot;/&gt; Let’s dig into the &lt;code&gt;StripeServices&lt;/code&gt; directory. This directory holds individual service objects for interacting with Stripes API. Again, the only thing these files do is take data from our application and send it to Stripe. If you ever need to update the API call in the &lt;code&gt;StripeService&lt;/code&gt; object that creates a subscription, you only have one place to do that.&lt;/p&gt;
&lt;p&gt;All of the logic that collects the data to be sent is done in a separate service object, living in the &lt;code&gt;AppServices&lt;/code&gt; directory. These files gather data from our application and send it off to the corresponding service directory for interfacing with the external API.&lt;/p&gt;
&lt;p&gt;Here’s a visual example: let’s assume that we have someone who is starting a new subscription. Everything originates from a controller. Here’s the &lt;code&gt;SubscriptionsController&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class SubscriptionsController &amp;lt; ApplicationController

  def create
    @subscription = Subscription.new(subscription_params)

    if @subscription.save

      result = AppServices::SubscriptionService.new({
        subscription_params: {
          subscription: @subscription,
          coupon: params[:coupon],
          token: params[:stripeToken]
        }
      }).call

      if result &amp;amp;&amp;amp; result.success?
        sign_in @subscription.user
        redirect_to subscribe_welcome_path, success: &amp;#39;Subscription was successfully created.&amp;#39;
      else
        @subscription.destroy
        redirect_to subscribe_path, danger: &amp;quot;Subscription was created, but there was a problem with the vendor.&amp;quot;
      end

    else
      redirect_to subscribe_path, danger:&amp;quot;Error creating subscription.&amp;quot;
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll first create the subscription in-app, and if it’s successful, we send that, the stripeToken, and stuff like the coupon into a file called &lt;code&gt;AppServices::SubscriptionService&lt;/code&gt;.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;In the &lt;code&gt;AppServices::SubscriptionService&lt;/code&gt; file, there are several things that need to happen. Here’s that object, before we get into what’s happening:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module AppServices
  class SubscriptionService

    def initialize(params)
      @subscription     = params[:subscription_params][:subscription]
      @token            = params[:subscription_params][:token]
      @plan             = @subscription.subscription_plan
      @user             = @subscription.user
    end

    def call

      # create or find customer
      customer ||= AppServices::StripeCustomerService.new({customer_params: {customer:@user, token:@token}}).call

      if customer &amp;amp;&amp;amp; customer.success?

        subscription ||= StripeServices::CreateSubscription.new({subscription_params:{
          customer: customer.payload,
          items:[subscription_items],
          expand: [&amp;#39;latest_invoice.payment_intent&amp;#39;]
        }}).call

        if subscription &amp;amp;&amp;amp; subscription.success?
          @subscription.update_attributes(
            status: &amp;#39;active&amp;#39;,
            stripe_id: subscription.payload.id,
            expiration: Time.at(subscription.payload.current_period_end).to_datetime
          )
          OpenStruct.new({success?: true, payload: subscription.payload})
        else
          handle_error(subscription&amp;amp;.error)
        end

      else
        handle_error(customer&amp;amp;.error)
      end

    end

    private

      attr_reader :plan

      def subscription_items
        base_plan
      end

      def base_plan
        [{ plan: plan.stripe_id }]
      end

      def handle_error(error)
        OpenStruct.new({success?: false, error: error})
      end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From a high-level overview, here’s what we’re looking at:&lt;/p&gt;
&lt;p&gt;We have to first get the Stripe customer ID so that we can send it to Stripe to create the subscription. That in itself is an entirely separate service object that does a number of things to make this happen.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We check to see if the &lt;code&gt;stripe_customer_id&lt;/code&gt; is saved on the user&amp;#39;s profile. If it is, we retrieve the customer from Stripe just to ensure that the customer actually exists, then return it in the payload of our OpenStruct.&lt;/li&gt;
&lt;li&gt;If the customer does not exist, we create the customer, save the stripe_customer_id, then return it in the payload of the OpenStruct.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Either way, our &lt;code&gt;CustomerService&lt;/code&gt; returns the Stripe customer ID, and it’ll do what’s necessary to make that happen. Here’s that file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module AppServices
  class CustomerService

    def initialize(params)
      @user               = params[:customer_params][:customer]
      @token              = params[:customer_params][:token]
      @account            = @user.account
    end

    def call
      if @account.stripe_customer_id.present?
        OpenStruct.new({success?: true, payload: @account.stripe_customer_id})
      else
        if find_by_email.success? &amp;amp;&amp;amp; find_by_email.payload
          OpenStruct.new({success?: true, payload: @account.stripe_customer_id})
        else
          create_customer
        end
      end
    end

    private

      attr_reader :user, :token, :account

      def find_by_email
        result ||= StripeServices::RetrieveCustomerByEmail.new({email: user.email}).call
        handle_result(result)
      end

      def create_customer
        result ||= StripeServices::CreateCustomer.new({customer_params:{email:user.email, source: token}}).call
        handle_result(result)
      end

      def handle_result(result)
        if result.success?
          account.update_column(:stripe_customer_id, result.payload.id)
          OpenStruct.new({success?: true, payload: account.stripe_customer_id})
        else
          OpenStruct.new({success?: false, error: result&amp;amp;.error})
        end
      end

  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hopefully, you can begin to see why we structure our logic across multiple service objects. Could you imagine one giant behemoth of a file with all of this logic? No way!&lt;/p&gt;
&lt;p&gt;Back to our &lt;code&gt;AppServices::SubscriptionService&lt;/code&gt; file. We now have a customer that we can send to Stripe, which completes the data that we need in order to create the subscription on Stripe.&lt;/p&gt;
&lt;p&gt;We’re now ready to call the last service object, the &lt;code&gt;StripeServices::CreateSubscription&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Again, &lt;code&gt;StripeServices::CreateSubscription&lt;/code&gt; service object never changes. It has a single responsibility, and that is to take data, send it to Stripe, and either return a success or return the object as a payload.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module StripeServices

  class CreateSubscription

    def initialize(params)
      @subscription_params = params[:subscription_params]
    end

    def call
      subscription = Stripe::Subscription.create(@subscription_params)
    rescue Stripe::StripeError =&amp;gt; e
      OpenStruct.new({success?: false, error: e})
    else
      OpenStruct.new({success?: true, payload: subscription})
    end

  end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty simple right? But you’re probably thinking, this small file is overkill. Let’s look at another example of a similar file to the one above, but this time we’ve augmented it for use with a multi-tenant application via Stripe Connect.&lt;/p&gt;
&lt;p&gt;Here’s where things get interesting. We’re using &lt;a href=&quot;https://mavenseed.com&quot;&gt;Mavenseed&lt;/a&gt; as an example here, although this same logic runs on &lt;a href=&quot;https://sport-keeper.com&quot;&gt;SportKeeper&lt;/a&gt; as well. Our multi-tenant app is a single monolith, sharing tables, separated by a site_id column. Each tenant connects to Stripe via Stripe Connect, and we then get a Stripe Account ID to save on the tenant&amp;#39;s account.&lt;/p&gt;
&lt;p&gt;Using our same Stripe API calls, we can simply pass the Stripe Account of the connected account, and Stripe will perform the API call on behalf of the connected account.&lt;/p&gt;
&lt;p&gt;So in a way, our &lt;code&gt;StripeService&lt;/code&gt; object is performing double-duty, along with both the main application and the tenants, to call the same file, but send in different data.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module StripeServices

  class CreateSubscription

    def initialize(params)
      @subscription_params  = params[:subscription_params]
      @stripe_account       = params[:stripe_account]
      @stripe_secret_key    = params[:stripe_secret_key] ? params[:stripe_secret_key] : (Rails.env.production? ? ENV[&amp;#39;STRIPE_LIVE_SECRET_KEY&amp;#39;] : ENV[&amp;#39;STRIPE_TEST_SECRET_KEY&amp;#39;])
    end

    def call
      subscription = Stripe::Subscription.create(@subscription_params, account_params)
    rescue Stripe::StripeError =&amp;gt; e
      OpenStruct.new({success?: false, error: e})
    else
      OpenStruct.new({success?: true, payload: subscription})
    end

    private

      attr_reader :stripe_account, :stripe_secret_key

      def account_params
        {
          api_key: stripe_secret_key,
          stripe_account: stripe_account,
          stripe_version: ENV[&amp;#39;STRIPE_API_VERSION&amp;#39;]
        }
      end
  end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few technical notes on this file: I could have shared a simpler example, but I really think it’s valuable for you to see how a proper service object is structured, including its responses.&lt;/p&gt;
&lt;p&gt;First, the “call” method has a rescue and else statement. This is the same as writing the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def call
   begin
   rescue Stripe ::StripeError  =&amp;gt; e
   else
   end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But Ruby methods automatically begin a block implicitly, so there’s no reason to add the begin and end. This statement reads as, “create the subscription, return an error if there is one, otherwise return the subscription.”&lt;/p&gt;
&lt;p&gt;Simple, succinct, and elegant. Ruby is truly a beautiful language and the use of service objects really highlights this.&lt;/p&gt;
&lt;p&gt;I hope that you can see the value that service files play in our applications. They provide a very succinct way of organizing our logic that is not only predictable but easily maintainable!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;——&lt;/p&gt;
&lt;p&gt;Read this chapter and more by picking up my new book &lt;a href=&quot;https://playbookthirtynine.com/?utm_source=appsignal&amp;utm_medium=cpc&amp;utm_campaign=article&amp;utm_content=service-objects&quot;&gt;Playbook Thirty-nine&lt;/a&gt; - &lt;em&gt;A Guide to Shipping Interactive Web Apps with Minimal Tooling&lt;/em&gt;. In this book, I take a top-down approach in covering common patterns and techniques, based solely on my first-hand experience as a solo-developer building and maintaining multiple high-traffic, high-revenue website applications.&lt;/p&gt;
&lt;p&gt;Use the coupon code &lt;em&gt;appsignalrocks&lt;/em&gt; and save 30%!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Dissecting Rails Migrations</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/04/14/dissecting-rails-migrationsl.html"/>
    <id>https://blog.appsignal.com/2020/04/14/dissecting-rails-migrationsl.html</id>
    <published>2020-04-14T00:00:00+00:00</published>
    <updated>2020-04-14T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Let&#039;s break down a migration process and find out how to write an effective migration.</summary>
    <content type="html">&lt;p&gt;In today&amp;#39;s post, we&amp;#39;ll take a deep dive into Rails migrations. We&amp;#39;ll break down the migration into different
pieces, and in the process, learn how to write an effective migration. We&amp;#39;ll learn how to write migrations
for multiple databases, as well as how to handle failed migrations and techniques of performing rollbacks.&lt;/p&gt;
&lt;p&gt;To understand the whole post, you&amp;#39;ll need to have a basic understanding of databases and Rails.&lt;/p&gt;
&lt;h2&gt;Migrations 101&lt;/h2&gt;
&lt;p&gt;Migrations in Rails allow us to evolve the database over the lifetime of an application. Migrations allow us to write plain Ruby code to alter the state of the database by providing an elegant DSL. We don&amp;#39;t have to write database-specific SQL since migrations provide abstractions to manipulate the database and take care of
nitty-gritty details of converting the DSL into database-specific SQL queries behind the scene. Migrations also get out of our way and provide ways of executing raw SQL on the database, if such need arises.&lt;/p&gt;
&lt;h2&gt;Twenty Thousand Leagues Into a Rails Database Migration&lt;/h2&gt;
&lt;p&gt;We can create tables, add or remove columns and add indexes on columns using the migrations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every Rails app has a special directory—&lt;code&gt;db/migrate&lt;/code&gt;—where all migrations are stored.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;#39;s start with a migration that creates the table &lt;code&gt;events&lt;/code&gt; into our database.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ rails g migration CreateEvents category:string
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command generates a timestamped file &lt;code&gt;20200405103635_create_events.rb&lt;/code&gt; in the &lt;code&gt;db/migrate&lt;/code&gt; directory. The contents of the file are as follows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class CreateEvents &amp;lt; ActiveRecord::Migration[6.0]
  def change
    create_table :events do |t|
      t.string :category

      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s break down this migration file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every migration file that Rails generates has a timestamp that is present in the filename. This timestamp is important and is used by Rails to confirm whether a migration has run or not, as we&amp;#39;ll see later.&lt;/li&gt;
&lt;li&gt;The migration contains a class that inherits from &lt;code&gt;ActiveRecord::Migration[6.0]&lt;/code&gt;. As I&amp;#39;m using Rails 6, the migration superclass has &lt;code&gt;[6.0]&lt;/code&gt;. If I was using Rails 5.2, then the superclass would be &lt;code&gt;ActiveRecord::Migration[5.2]&lt;/code&gt;. Later, we&amp;#39;ll discuss why the Rails version is part of the superclass name.&lt;/li&gt;
&lt;li&gt;The migration has a method &lt;code&gt;change&lt;/code&gt; which contains the DSL code that manipulates the database. In this case, the &lt;code&gt;change&lt;/code&gt; method is creating an &lt;code&gt;events&lt;/code&gt; table with a column &lt;code&gt;category&lt;/code&gt; of type &lt;code&gt;string&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The migration uses the code &lt;code&gt;t.timestamps&lt;/code&gt; to add timestamps &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; to the &lt;code&gt;events&lt;/code&gt; table.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When this migration is run using the &lt;code&gt;rails db:migrate&lt;/code&gt; command, it will create an &lt;code&gt;events&lt;/code&gt; table with a &lt;code&gt;category&lt;/code&gt; column of type &lt;code&gt;string&lt;/code&gt; and timestamp columns &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The actual database column type will be varchar or text, depending on the database.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Importance of Migration Timestamps and the schema_migration Table&lt;/h3&gt;
&lt;p&gt;Every time a migration is generated using the &lt;code&gt;rails g migration&lt;/code&gt; command, Rails generates
the migration file with a unique timestamp. The timestamp is in the
format &lt;code&gt;YYYYMMDDHHMMSS&lt;/code&gt;.
Whenever a migration is run, Rails inserts the migration timestamp into an internal table &lt;code&gt;schema_migrations&lt;/code&gt;. This table is created by Rails when we run our first migration. The table only has the column &lt;code&gt;version&lt;/code&gt;, which is also its primary key. This is the structure of the &lt;code&gt;schema_migrations&lt;/code&gt; table.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE IF NOT EXISTS &amp;quot;schema_migrations&amp;quot; (&amp;quot;version&amp;quot; varchar NOT NULL PRIMARY KEY);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have run the migration for creating the &lt;code&gt;events&lt;/code&gt; table, let&amp;#39;s see if Rails has stored a
timestamp of this migration in the &lt;code&gt;schema_migrations&lt;/code&gt; table.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;sqlite&amp;gt; select * from schema_migrations;
20200405103635
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we run the migrations again, Rails will first check if an entry exists in the &lt;code&gt;schema_migrations&lt;/code&gt; table with the timestamp of the migration file, and only execute it if there is no such entry. This ensures that we can incrementally add changes to the database over time and a migration will run only once on the database.&lt;/p&gt;
&lt;h3&gt;Database Schema&lt;/h3&gt;
&lt;p&gt;As we run more and more migrations, the database schema keeps evolving. Rails stores the most recent
database schema in the file &lt;code&gt;db/schema.rb&lt;/code&gt;. This file is the Ruby representation of all the migrations
run on your database over the life of the application. Because of this file, we don&amp;#39;t need to keep
old migrations files in the codebase. Rails provides tasks to &lt;code&gt;dump&lt;/code&gt; the latest schema from the database into &lt;code&gt;schema.rb&lt;/code&gt; and &lt;code&gt;load&lt;/code&gt; the schema into a database from the &lt;code&gt;schema.rb&lt;/code&gt;. So older migrations can be safely deleted from the codebase. The loading of the schema into the database is also faster compared to running each and every migration every time we set up the application.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rails also provides a way to store database schema in SQL format. We already have an article to compare the two formats. You can read more about it &lt;a href=&quot;/2020/01/15/the-pros-and-cons-of-using-structure-sql-in-your-ruby-on-rails-application.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Rails Version in the Migration&lt;/h3&gt;
&lt;p&gt;Every migration that we generate has the Rails version as part of the superclass.
So a migration generated by a Rails 6 app has the superclass &lt;code&gt;ActiveRecord::Migration[6.0]&lt;/code&gt; whereas
a migration generated by Rails 5.2 app has the superclass &lt;code&gt;ActiveRecord::Migration[5.2]&lt;/code&gt;. If you have an
old app with Rails 4.2 or below, you&amp;#39;ll notice that there is no version in the superclass. The superclass is just &lt;code&gt;ActiveRecord::Migration&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Rails version was added to the migration superclass in Rails 5. This basically ensures that the migration
API can evolve over time without breaking migrations generated by older versions of Rails.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s look deeper into this by looking at the same migration for creating an &lt;code&gt;events&lt;/code&gt; table in a Rails 4.2 app.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;class CreateEvents &amp;lt; ActiveRecord::Migration
  def change
    create_table :events do |t|
      t.string :category

      t.timestamps null: false
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;If we look at the schema of the &lt;code&gt;events&lt;/code&gt; table generated by a Rails 6 migration, we can see that
the &lt;code&gt;NOT NULL&lt;/code&gt; constraint for the timestamps columns exist.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;sqlite&amp;gt; .schema events
CREATE TABLE IF NOT EXISTS &amp;quot;events&amp;quot; (&amp;quot;id&amp;quot; integer PRIMARY KEY AUTOINCREMENT NOT NULL, &amp;quot;category&amp;quot; varchar, &amp;quot;created_at&amp;quot; datetime(6) NOT NULL, &amp;quot;updated_at&amp;quot; datetime(6) NOT NULL);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because, starting from Rails 5 onward, the migration API automatically adds a &lt;code&gt;NOT NULL&lt;/code&gt; constraint
to the timestamp columns without a need to add it explicitly in the migration file.
The Rails version in the superclass name ensures that the migration uses the migration API of the
Rails version for which the migration was generated. This allows Rails to maintain backward
compatibility with the older migrations, at the same time evolving the migrations API.&lt;/p&gt;
&lt;h3&gt;Changing the Database Schema&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;change&lt;/code&gt; method is the primary method in a migration. When a migration gets run, it calls
the &lt;code&gt;change&lt;/code&gt; method and executes the code inside it.&lt;/p&gt;
&lt;p&gt;Along with &lt;code&gt;create_table&lt;/code&gt;, Rails also provides another powerful method—&lt;code&gt;change_table&lt;/code&gt;.
As the name suggests, it is used to alter the schema of an existing table.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def change
  change_table :events do |t|
    t.remove :category
    t.string :event_type
    t.boolean :active, default: false
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This migration will remove the &lt;code&gt;category&lt;/code&gt; column from the &lt;code&gt;events&lt;/code&gt; table, add a new string column &lt;code&gt;events_type&lt;/code&gt; and a new boolean column &lt;code&gt;active&lt;/code&gt; with the default value of &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Rails also provides a lot of other helper methods which can be used inside a migration such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;change_column&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_index&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;remove_index&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rename_table&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and many more. All the methods that can be used with change can be &lt;a href=&quot;https://guides.rubyonrails.org/active_record_migrations.html#using-the-change-method&quot;&gt;found here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Timestamps&lt;/h3&gt;
&lt;p&gt;We saw that &lt;code&gt;t.timestamps&lt;/code&gt; was added to the migration by Rails and it added the columns
&lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; to the &lt;code&gt;events&lt;/code&gt; table. These special columns are used by Rails
to keep track of when a record is created and updated.
Rails adds values to these columns when a record is created and makes sure to update them when the record
is updated. These columns help us in tracking the lifetime of a database record.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;updated_at&lt;/code&gt; column is not updated when we execute the &lt;code&gt;updated_all&lt;/code&gt; method from Rails.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Handling Failures&lt;/h3&gt;
&lt;p&gt;Migrations are not bulletproof. They can fail. The reason might be wrong syntax or an invalid
database query. Whatever the reason, we have to handle the failure and recover from it so that the
database doesn&amp;#39;t go into an inconsistent state. Rails solves this problem by running each
migration inside a transaction. If the migration fails, then the transaction is rolled back.
This ensures that the database does not go into an inconsistent state.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is only done for databases that support transactions for updating database schema. They are known as Data Definition Language(DDL) transactions. MySQL and PostgreSQL both support DDL transactions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sometimes, we don&amp;#39;t want to execute certain migrations inside a transaction. A simple example is when adding a
concurrent index in PostgreSQL. Such migrations can&amp;#39;t be executed inside a DDL transaction as PostgreSQL
tries to add the index without acquiring locks on the table so that we can add the index on a live production database without taking the database down. Rails provides a way to opt-out of transactions inside a migration in the form of &lt;code&gt;disable_ddl_transactions!&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def change
  disable_ddl_transactions!

  add_index :events, :user_id, algorithm: :concurrently
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will not run the migration inside a transaction. If such a migration fails, we need to recover it ourselves. In this case, we can either &lt;code&gt;REINDEX&lt;/code&gt; or remove the index and try to add it again.&lt;/p&gt;
&lt;h3&gt;Reversible Migrations&lt;/h3&gt;
&lt;p&gt;Rails allows us to rollback changes to the database with the following command.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails db:rollback
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command reverts the last migration that was run on the database. If the migration added a column
&lt;code&gt;event_type&lt;/code&gt; then the rollback will remove that column. If the migration added an index, then rollback
will remove that index.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is also a command for rolling back the previous migration and running it. It is &lt;code&gt;rails db:redo&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rails is smart enough to know how to reverse most of the migrations. But we can also provide hints to Rails
on how to revert a migration by providing &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; methods instead of using the &lt;code&gt;change&lt;/code&gt; method.
The &lt;code&gt;up&lt;/code&gt; method will be used when the migration is run whereas the &lt;code&gt;down&lt;/code&gt; method will be used when the migration is rolled back.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def up
  change_table :events do |t|
    t.change :price, :string
  end
end

def down
  change_table :events do |t|
    t.change :price, :integer
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we are changing the &lt;code&gt;price&lt;/code&gt; column of &lt;code&gt;events&lt;/code&gt; from &lt;code&gt;integer&lt;/code&gt; to &lt;code&gt;string&lt;/code&gt;. We specify how it should be rolled back in the &lt;code&gt;down&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;This same migration can also be written using the &lt;code&gt;change&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def change
  reversible do |direction|
    change_table :events do |t|
      direction.up { t.change :price, :string }
      direction.down { t.change :price, :integer }
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails also provides a way to revert a previous migration completely using
the &lt;code&gt;revert&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def change
  revert CreateEvents

  create_table :events do
   ...
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;revert&lt;/code&gt; method also accepts a block to revert a migration partially.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def change
  revert do
    reversible do |direction|
      change_table :events do |t|
        direction.up { t.remove :event_type }
        direction.down { t.string :event_type }
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Executing It Raw&lt;/h2&gt;
&lt;p&gt;Sometimes, we want to execute complex SQL inside a migration. In such cases, we can forget
the typical migration DSL and instead execute raw SQL as follows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rb&quot;&gt;def change
  execute &amp;lt;&amp;lt;-SQL
    ....
  SQL
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Multiple Databases and Migrations&lt;/h2&gt;
&lt;p&gt;Rails 6 added support for using multiple databases within a single Rails application.
If we want to use multiple databases, we configure them in the &lt;code&gt;database.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;development:
  primary:
    &amp;lt;&amp;lt;: *default
    database: db/development.sqlite3
  analytics:
    adapter: sqlite3
    database: db/analytics_dev.sqlite3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration tells Rails that we want to use two databases—&lt;code&gt;primary&lt;/code&gt; and &lt;code&gt;analytics&lt;/code&gt;.
As we saw earlier, the migrations are stored in the &lt;code&gt;db/migrate&lt;/code&gt; directory by default. But in this case,
we can&amp;#39;t add migrations of both databases inside a single directory. We don&amp;#39;t want to run migrations
of the &lt;code&gt;analytics&lt;/code&gt; database on the &lt;code&gt;primary&lt;/code&gt; database and vice versa. If we are using multiple databases, we
are required to provide a path for storing migrations for the second database. This can be done by providing a &lt;code&gt;migrations_paths&lt;/code&gt; in the &lt;code&gt;database.yml&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;development:
  primary:
    &amp;lt;&amp;lt;: *default
    database: db/development.sqlite3
  analytics:
    adapter: sqlite3
    database: db/analytics_dev.sqlite3
    migrations_paths: db/analytics_migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then create migrations for the &lt;code&gt;analytics&lt;/code&gt; database as follows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails generate migration AddExperiments rule:string active:boolean --db=analytics
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create the migration inside &lt;code&gt;db/analytics_migrate&lt;/code&gt;, and we can run it as follows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;rails db:migrate --db=analytics
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we only run the &lt;code&gt;rails db:migrate&lt;/code&gt;, it will execute migrations for all the databases.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;analytics&lt;/code&gt; database will have its own &lt;code&gt;schema_migrations&lt;/code&gt; table to keep track of which migrations are run and which are not.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Running Migrations During Deployment&lt;/h2&gt;
&lt;p&gt;Since migrations can change the state of the database, and our code might depend on those changes,
it is extremely important that the migrations are run first before the new code is applied.&lt;/p&gt;
&lt;p&gt;In Heroku based deployments, migrations can be run in the &lt;code&gt;release&lt;/code&gt; phase of the &lt;code&gt;Procfile&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Profile
web: bin/puma -C config/puma.rb
release: bundle exec rake db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures that the migrations are run before the app dynos are restarted.&lt;/p&gt;
&lt;p&gt;In Capistrano based deployments, migrations should run before the server is restarted.&lt;/p&gt;
&lt;p&gt;In docker based deployments, we can run a sidecar container to run the migrations first before the app is restarted. This is very important as otherwise, the new containers can go into an inconsistent state if they start using new code before applying the database changes for that new code.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this post, we saw various aspects of writing a database migration in Rails. We also saw what constitutes
a migration as well as how to handle failures and roll back the migrations if needed. Rails 6 allows us to use multiple databases and the migrations for each need to be added separately. Finally, we briefly saw how to run the migrations during deployment so that database changes are applied properly before any new code starts using them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The Citadel Architecture at AppSignal</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/04/08/the-citadel-architecture-at-appsignal.html"/>
    <id>https://blog.appsignal.com/2020/04/08/the-citadel-architecture-at-appsignal.html</id>
    <published>2020-04-08T00:00:00+00:00</published>
    <updated>2020-04-08T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Check out how AppSignal uses the Citadel pattern and how our system works.</summary>
    <content type="html">&lt;p&gt;DHH just coined the term &amp;quot;Citadel,&amp;quot; which finally gives us an excellent way to reference how we approach tech at AppSignal. We said, &amp;quot;Hey, this is us! Our thing has a name now&amp;quot;.&lt;/p&gt;
&lt;blockquote className=&quot;twitter-tweet&quot; data-conversation=&quot;none&quot;&gt;
  &lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;
    In addition to the Majestic Monolith, someone should write up the pattern of
    The Citadel: A single Majestic Monolith captures the majority mass of the
    app, with a few auxiliary outpost apps for highly specialized and divergent
    needs.
  &lt;/p&gt;
  &amp;mdash; DHH (@dhh){&quot; &quot;}
  &lt;a href=&quot;https://twitter.com/dhh/status/1247522358908215296?ref_src=twsrc%5Etfw&quot;&gt;
    April 7, 2020
  &lt;/a&gt;
&lt;/blockquote&gt;
&lt;script
  async
  src=&quot;https://platform.twitter.com/widgets.js&quot;
  charset=&quot;utf-8&quot;
&gt;&lt;/script&gt;

&lt;p&gt;To explain how AppSignal uses the Citadel pattern, we&amp;#39;ll share a bit on how our system works. AppSignal is a monitoring product that has a user-facing application and an API that the monitoring agent sends data to. This data is then processed and turned into graphs and insights.&lt;/p&gt;
&lt;h2&gt;Monolith&lt;/h2&gt;
&lt;p&gt;The application our customers interact with is a monolithic Rails app, with parts of the front-end written in React. The backend is entirely written in Ruby and talks to a few databases (we split out data from different customers to separate clusters for scaling reasons). This Rails app also handles a bunch of other tasks such as sending out alerts to external services.&lt;/p&gt;
&lt;p&gt;When we started this Rails app processed incoming data from our monitoring agent as well, we foresaw that data ingestion would turn out to be a bottleneck. So we used a Sinatra app running on a subdomain that ingested data and created Sidekiq jobs that were processed by the Rails app.&lt;/p&gt;
&lt;h2&gt;Growing Pains&lt;/h2&gt;
&lt;p&gt;This architecture worked well for years. As our business grew, it became clear that the specific task of processing incoming data from the agents was going to need special treatment. When you&amp;#39;re monitoring billions and billions of requests, you run into hard limits. The main limiting factor wasn&amp;#39;t so much that Ruby is slow (we all know that it is not 😉), but that the way we had architected things caused too much locking in our databases.&lt;/p&gt;
&lt;h2&gt;An Outpost&lt;/h2&gt;
&lt;p&gt;We looked at several possibilities and then decided Kafka was the best fit for our situation. We had some experience with Rust and thought that its speed and reliability would be a very good fit for this system. We rewrote our data ingestion and processing system in Rust, using Kafka as a combination of queue and storage system.&lt;/p&gt;
&lt;p&gt;We only moved the incoming data processing part of the Rails app to this outpost service. The rest of the systems works well in the form of a monolithic app. We understand it deeply, and we like keeping things simple. The monolith still handles most of the logic and interacts with Kafka heavily. Our wish to keep our monolith led to us writing a &lt;a href=&quot;https://github.com/appsignal/rdkafka-ruby&quot;&gt;Kafka gem&lt;/a&gt;, so the main app can communicate with the outpost easily.&lt;/p&gt;
&lt;p&gt;If you want to learn more about how Kafka works at AppSignal &lt;a href=&quot;https://www.youtube.com/watch?v=-NMDqqW1uCE&quot;&gt;check out the Railsconf talk&lt;/a&gt; I gave about this.&lt;/p&gt;
&lt;h2&gt;Life at the Citadel&lt;/h2&gt;
&lt;p&gt;This brings us to the current situation where we are very happy in our citadel. As DHH said:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A single Majestic Monolith captures the majority mass of the app with a few auxiliary outpost apps for highly specialized and divergent needs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In our case, we have a single outpost service for our highly specialized needs. If there were a RailsConf this year, we would have given DHH some extra stroopwafels as appreciation for giving it a name. 🍪&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Changing the Approach to Debugging in Ruby with TracePoint</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/04/01/changing-the-approach-to-debugging-in-ruby-with-tracepoint.html"/>
    <id>https://blog.appsignal.com/2020/04/01/changing-the-approach-to-debugging-in-ruby-with-tracepoint.html</id>
    <published>2020-04-01T00:00:00+00:00</published>
    <updated>2020-04-01T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn more about TracePoint and how it makes code tell us what it&#039;s doing.</summary>
    <content type="html">&lt;p&gt;Ruby has always been known for the productivity it brings to its developers. Alongside features such as elegant syntax, rich meta-programming support, etc. that make you productive when writing code, it also has another secret weapon called &lt;code&gt;TracePoint&lt;/code&gt; that can help you &amp;quot;debug&amp;quot; faster.&lt;/p&gt;
&lt;p&gt;In this post, I&amp;#39;ll use a simple example to show you 2 interesting facts I found out about debugging:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Most of the time, finding the bug itself isn&amp;#39;t hard, but understanding how your program works in detail is. Once you have a deep understanding of this, you can usually spot the bug right away.&lt;/li&gt;
&lt;li&gt;Observing your program down to the method call level is time-consuming, and is the major bottleneck of our debugging process.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then, I&amp;#39;ll show you how &lt;code&gt;TracePoint&lt;/code&gt; could change the way we approach debugging by making the program &amp;quot;tell us&amp;quot; what it&amp;#39;s doing.&lt;/p&gt;
&lt;h2&gt;Debugging Is about Understanding Your Program and Its Design&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s assume we have a Ruby program called &lt;code&gt;plus_1&lt;/code&gt; and it&amp;#39;s not functioning correctly. How do we debug this?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# plus_1.rb
def plus_1(n)
  n + 2
end

input = ARGV[0].to_i
puts(plus_1(input))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;$ ruby plus_1.rb 1
3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ideally, we should be able to address the bug in 3 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Learn the expectations from the design&lt;/li&gt;
&lt;li&gt;Understand the current implementation&lt;/li&gt;
&lt;li&gt;Trace the bug&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Learning the Expectations from the Design&lt;/h3&gt;
&lt;p&gt;What&amp;#39;s the expected behavior here? &lt;code&gt;plus_1&lt;/code&gt; should add &lt;code&gt;1&lt;/code&gt; to its argument, which is our input from the command line. But how do we &amp;quot;know&amp;quot; this?&lt;/p&gt;
&lt;p&gt;In a real-world case, we can understand the expectations by reading test cases, documents, mockups, asking other people for feedback, etc. Our understanding depends on how the program is &amp;quot;designed&amp;quot;.&lt;/p&gt;
&lt;p&gt;This step is the most crucial part of our debugging process. If you don&amp;#39;t understand how the program should work, you&amp;#39;ll never be able to debug it.&lt;/p&gt;
&lt;p&gt;However, there are many factors that can be part of this step, such as team coordination, development workflow, etc. &lt;code&gt;TracePoint&lt;/code&gt; won&amp;#39;t be able to help you with those, so we won&amp;#39;t dwell on these problems today.&lt;/p&gt;
&lt;h3&gt;Understanding the Current Implementation&lt;/h3&gt;
&lt;p&gt;Once we&amp;#39;ve understood the expected behavior of the program, we need to learn how it functions at the moment.&lt;/p&gt;
&lt;p&gt;In most cases, we need the following information to fully understand how a program works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The methods called during the program&amp;#39;s execution&lt;/li&gt;
&lt;li&gt;The call and return order of the method calls&lt;/li&gt;
&lt;li&gt;Arguments passed to each method call&lt;/li&gt;
&lt;li&gt;Values returned from each method call&lt;/li&gt;
&lt;li&gt;Any side effects that happened during each method call, e.g. data mutation or database requests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#39;s describe our example with the above information:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# plus_1.rb
def plus_1(n)
  n + 2
end

input = ARGV[0].to_i
puts(plus_1(input))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;$ ruby plus_1.rb 1
3
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Defines a method called &lt;code&gt;plus_1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Retrieves the input (&lt;code&gt;&amp;quot;1&amp;quot;&lt;/code&gt;) from &lt;code&gt;ARGV&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;to_i&lt;/code&gt; on &lt;code&gt;&amp;quot;1&amp;quot;&lt;/code&gt;, which returns &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Assigns &lt;code&gt;1&lt;/code&gt; to local variable &lt;code&gt;input&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;plus_1&lt;/code&gt; method with &lt;code&gt;input&lt;/code&gt;(&lt;code&gt;1&lt;/code&gt;) as its argument. The parameter &lt;code&gt;n&lt;/code&gt; now carries a value of &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;+&lt;/code&gt; method on &lt;code&gt;1&lt;/code&gt; with an argument &lt;code&gt;2&lt;/code&gt;, and returns the result &lt;code&gt;3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Returns &lt;code&gt;3&lt;/code&gt; for step 5&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;puts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;to_s&lt;/code&gt; on &lt;code&gt;3&lt;/code&gt;, which returns &lt;code&gt;&amp;quot;3&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Passes &lt;code&gt; &amp;quot;3&amp;quot;&lt;/code&gt; to the &lt;code&gt;puts&lt;/code&gt; call from step 8, which triggers a side effect that prints the string to Stdout. Then it returns &lt;code&gt;nil&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The description isn&amp;#39;t 100% accurate, but it&amp;#39;s enough for a simple explanation.&lt;/p&gt;
&lt;h3&gt;Addressing the Bug&lt;/h3&gt;
&lt;p&gt;Now that we&amp;#39;ve learned how our program should work and how it actually works, we can start looking for the bug. With the information we have, we can search for the bug by following the method calls upward (start from step 10) or downward (start from step 1). In this case, we can do it by tracing back to the method that returned 3 in the first place⁠—which is the &lt;code&gt;1 + 2&lt;/code&gt; in &lt;code&gt;step 6&lt;/code&gt;.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;This Is Far from Reality!&lt;/h3&gt;
&lt;p&gt;Of course, we all know that real debugging isn&amp;#39;t as simple as the example makes it out to be. The critical difference between real-life programs and our example is the size. We used 10 steps to explain a 5-line program. How many steps would we need for a small Rails app? It&amp;#39;s basically impossible to break down a real program as detailed as we did for the example.
Without a detailed understanding of your program, you won&amp;#39;t be able to track down the bug through an obvious path, so you&amp;#39;ll need to make assumptions or guesses.&lt;/p&gt;
&lt;h2&gt;Information Is Expensive&lt;/h2&gt;
&lt;p&gt;As you probably already noticed, the key factor in debugging is how much information you have. But what does it take to retrieve that much information? Let&amp;#39;s see:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# plus_1_with_tracing.rb
def plus_1(n)
  puts(&amp;quot;n = #{n}&amp;quot;)
  n + 2
end

raw_input = ARGV[0]
puts(&amp;quot;raw_input: #{raw_input}&amp;quot;)
input = raw_input.to_i
puts(&amp;quot;input: #{input}&amp;quot;)

result = plus_1(input)
puts(&amp;quot;result of plus_1 #{result}&amp;quot;)

puts(result)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;$ ruby plus_1_with_tracing.rb 1
raw_input: 1
input: 1
n = 1
result of plus_1: 3
3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we only get 2 types of information here: values of some variables and the evaluation order of our &lt;code&gt;puts&lt;/code&gt; (which implies the program&amp;#39;s execution order).&lt;/p&gt;
&lt;p&gt;How much does this information cost us?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt; def plus_1(n)
+  puts(&amp;quot;n = #{n}&amp;quot;)
   n + 2
 end

-input = ARGV[0].to_i
-puts(plus_1(input))
+raw_input = ARGV[0]
+puts(&amp;quot;raw_input: #{raw_input}&amp;quot;)
+input = raw_input.to_i
+puts(&amp;quot;input: #{input}&amp;quot;)
+
+result = plus_1(input)
+puts(&amp;quot;result of plus_1: #{result}&amp;quot;)
+
+puts(result)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not only do we need to add 4 &lt;code&gt;puts&lt;/code&gt; into the code, but, in order to print values separately, we also need to split our logic in order to access the intermediate states of some values. In this case, we got 4 additional outputs for the internal states with 8 lines of changes. That&amp;#39;s 2 lines of changes for 1 line of output, on average! And since the number of changes grows linearly with the size of the program, we can compare it to an &lt;code&gt;O(n)&lt;/code&gt; operation.&lt;/p&gt;
&lt;h3&gt;Why Is Debugging Expensive?&lt;/h3&gt;
&lt;p&gt;Our programs can be written with many goals in mind: maintainability, performance, simplicity, etc. but usually not for &amp;quot;Traceability&amp;quot;, meaning, getting the values for inspection, which usually requires a modification of the code, e.g. splitting chained method calls.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The more the information you get, the more additions/changes you need to make to the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, once the amount of information you get reaches a certain point, you won&amp;#39;t be able to process it efficiently. So we either need to filter the information out or label it to help us understand it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The more precise the information, the more additions/changes you need to make to the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, because the work involves touching the codebase⁠—which can be very different between bugs (e.g. controller vs. model logic)⁠—it&amp;#39;s hard to automate it. Even if your codebase is tracing-friendly (e.g. it follows &amp;quot;Law of Demeter&amp;quot; strictly), most of the time, you&amp;#39;ll need to type different variable/method names manually.&lt;/p&gt;
&lt;p&gt;(Actually, in Ruby, there are some tricks to avoid this⁠—like &lt;code&gt;__method__&lt;/code&gt;. But let&amp;#39;s not complicate things here.)&lt;/p&gt;
&lt;h2&gt;TracePoint: The Savior&lt;/h2&gt;
&lt;p&gt;However, Ruby provides us an exceptional tool that can largely reduce the cost: &lt;code&gt;TracePoint&lt;/code&gt;. I bet most of you have already heard of it or used it before. But in my experience, not many people use this powerful tool in daily debugging practices.&lt;/p&gt;
&lt;p&gt;Let me show you how to use it to collect information quickly. This time, we don&amp;#39;t need to touch any of our existing logic, we just need some code before it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;TracePoint.trace(:call, :return, :c_call, :c_return) do |tp|
  event = tp.event.to_s.sub(/(.+(call|return))/, &amp;#39;\2&amp;#39;).rjust(6, &amp;quot; &amp;quot;)
  message = &amp;quot;#{event} of #{tp.defined_class}##{tp.callee_id} on #{tp.self.inspect}&amp;quot;
  # if you call `return` on any non-return events, it&amp;#39;ll raise error
  message += &amp;quot; =&amp;gt; #{tp.return_value.inspect}&amp;quot; if tp.event == :return || tp.event == :c_return
  puts(message)
end

def plus_1(n)
  n + 2
end

input = ARGV[0].to_i
puts(plus_1(input))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run the code, you&amp;#39;ll see:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return of #&amp;lt;Class:TracePoint&amp;gt;#trace on TracePoint =&amp;gt; #&amp;lt;TracePoint:c_return `trace&amp;#39;@plus_1_with_trace_point.rb:1&amp;gt;
  call of Module#method_added on Object
return of Module#method_added on Object =&amp;gt; nil
  call of String#to_i on &amp;quot;1&amp;quot;
return of String#to_i on &amp;quot;1&amp;quot; =&amp;gt; 1
  call of Object#plus_1 on main
return of Object#plus_1 on main =&amp;gt; 3
  call of Kernel#puts on main
  call of IO#puts on #&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;
  call of Integer#to_s on 3
return of Integer#to_s on 3 =&amp;gt; &amp;quot;3&amp;quot;
  call of IO#write on #&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;
3
return of IO#write on #&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt; =&amp;gt; 2
return of IO#puts on #&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt; =&amp;gt; nil
return of Kernel#puts on main =&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our code is much more readable now. Isn&amp;#39;t it amazing? It prints out most of the program execution with lots of details! We can even map it with my earlier execution breakdown:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Defines a method called &lt;code&gt;plus_1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Retrieves the input (&lt;code&gt;&amp;quot;1&amp;quot;&lt;/code&gt;) from &lt;code&gt;ARGV&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;to_i&lt;/code&gt; on &lt;code&gt;&amp;quot;1&amp;quot;&lt;/code&gt;, which returns &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Assigns &lt;code&gt;1&lt;/code&gt; to local variable &lt;code&gt;input&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;plus_1&lt;/code&gt; method with &lt;code&gt;input&lt;/code&gt;(&lt;code&gt;1&lt;/code&gt;) as its argument. The parameter &lt;code&gt;n&lt;/code&gt; now carries a value &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;+&lt;/code&gt; method on &lt;code&gt;1&lt;/code&gt; with an argument &lt;code&gt;2&lt;/code&gt;, and returns the result &lt;code&gt;3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Returns &lt;code&gt;3&lt;/code&gt; for step 5&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;puts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;to_s&lt;/code&gt; on &lt;code&gt;3&lt;/code&gt;, which returns &lt;code&gt;&amp;quot;3&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Passes &lt;code&gt; &amp;quot;3&amp;quot;&lt;/code&gt; to the &lt;code&gt;puts&lt;/code&gt; call from step 8, which triggers a side effect that prints the string to Stdout. And then it returns &lt;code&gt;nil&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# ignore this, it&amp;#39;s TracePoint tracing itself ;D
return of #&amp;lt;Class:TracePoint&amp;gt;#trace on TracePoint =&amp;gt; #&amp;lt;TracePoint:c_return `trace&amp;#39;@plus_1_with_trace_point.rb:1&amp;gt;

  call of Module#method_added on Object         # 1. Defines a method called `plus_1`.
return of Module#method_added on Object =&amp;gt; nil
  call of String#to_i on &amp;quot;1&amp;quot;                    # 3-1. Calls `to_i` on `&amp;quot;1&amp;quot;`
return of String#to_i on &amp;quot;1&amp;quot; =&amp;gt; 1               # 3-2. which returns `1`
  call of Object#plus_1 on main                 # 5. Calls `plus_1` method with `input`(`1`) as its argument.
return of Object#plus_1 on main =&amp;gt; 3            # 7. Returns `3` for step 5
  call of Kernel#puts on main                   # 8. Calls `puts`
  call of IO#puts on #&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;
  call of Integer#to_s on 3                     # 9. Calls `to_s` on `3`, which returns `&amp;quot;3&amp;quot;`
return of Integer#to_s on 3 =&amp;gt; &amp;quot;3&amp;quot;
  call of IO#write on #&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;            # 10-1. Passes `&amp;quot;3&amp;quot;` to the `puts` call from step 8
                                                # 10-2. which triggers a side effect that prints the string to Stdout
3 # original output
return of IO#write on #&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt; =&amp;gt; 2
return of IO#puts on #&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt; =&amp;gt; nil
return of Kernel#puts on main =&amp;gt; nil            # 10-3. And then it returns `nil`.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can even say it&amp;#39;s more detailed than what I said earlier! However, you may notice that steps 2, 4 and 6 are missing from the output. Unfortunately, they are not trackable by &lt;code&gt;TracePoint&lt;/code&gt; for the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Retrieves the input (&lt;code&gt;&amp;quot;1&amp;quot;&lt;/code&gt;) from &lt;code&gt;ARGV&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ARGV&lt;/code&gt; and the following &lt;code&gt;[]&lt;/code&gt; aren&amp;#39;t considered as call/c_call at the moment&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Assigns &lt;code&gt;1&lt;/code&gt; to local variable &lt;code&gt;input&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Currently, there&amp;#39;s no event for variable assignments. We can (sort of) track it with &lt;code&gt;line&lt;/code&gt; event + regex, but it won&amp;#39;t be accurate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Calls &lt;code&gt;+&lt;/code&gt; method on &lt;code&gt;1&lt;/code&gt; with an argument &lt;code&gt;2&lt;/code&gt;, and returns the result &lt;code&gt;3&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Certain method calls like built-in &lt;code&gt;+&lt;/code&gt; or attributes accessor methods aren&amp;#39;t trackable at the moment&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;From O(n) to O(log n)&lt;/h3&gt;
&lt;p&gt;As you can see from the previous example, with proper usage of &lt;code&gt;TracePoint&lt;/code&gt;, we can almost make the program &amp;quot;tell us&amp;quot; what it&amp;#39;s doing. Now, because of the number of lines we need, &lt;code&gt;TracePoint&lt;/code&gt; doesn&amp;#39;t grow linearly with the size of our program. I&amp;#39;d say the whole process becomes an &lt;code&gt;O(log(n))&lt;/code&gt; operation.&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;In this article, I&amp;#39;ve explained the main difficulty with debugging. Hopefully, I&amp;#39;ve also convinced you of how &lt;code&gt;TracePoint&lt;/code&gt; could be a game-changer. But if you try &lt;code&gt;TracePoint&lt;/code&gt; right now, it&amp;#39;ll probably frustrate you more than help you.&lt;/p&gt;
&lt;p&gt;With the amount of information that comes from &lt;code&gt;TracePoint&lt;/code&gt;, you&amp;#39;ll soon be swamped by the noise. The new challenge is to filter out the noise, leaving valuable information. For example, in most cases, we only care about specific models or service objects. In these cases, we can filter calls by the receiver&amp;#39;s class, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;TracePoint.trace(:call) do |tp|
  next unless tp.self.is_a?(Order)
  # tracing logic
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another thing to keep in mind is that the block you define for &lt;code&gt;TracePoint&lt;/code&gt; could be evaluated tens of thousands of times. At this scale, how you implement the filtering logic can have a great impact on your app&amp;#39;s performance. For example, I don&amp;#39;t recommend this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;TracePoint.trace(:call) do |tp|
  trace = caller[0]
  next unless trace.match?(&amp;quot;app&amp;quot;)
  # tracing logic
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For these 2 problems, I&amp;#39;ve prepared another article to let you know of some tricks and gotchas I found with some useful boilerplates for typical Ruby/Rails applications.&lt;/p&gt;
&lt;p&gt;And if you find this concept interesting, I also created a gem called &lt;a href=&quot;https://github.com/st0012/tapping_device&quot;&gt;tapping_device&lt;/a&gt; that hides all the implementation hassles.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Debugger and tracing are both great tools for debugging, and we have been using them for many years. But as I&amp;#39;ve demonstrated in this article, using them requires many manual operations during the debugging process. However, with the help of &lt;code&gt;TracePoint&lt;/code&gt;, you can automate many of them and thus boost your debugging performance. I hope you can now add &lt;code&gt;TracePoint&lt;/code&gt; to your debugging toolbox and give it a try.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Facade Pattern in Rails for Performance and Maintainability</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/03/18/facade-pattern-in-rails-for-performance-and-maintainability.html"/>
    <id>https://blog.appsignal.com/2020/03/18/facade-pattern-in-rails-for-performance-and-maintainability.html</id>
    <published>2020-03-18T00:00:00+00:00</published>
    <updated>2020-03-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Find out how and why you should use a software design pattern named Facade.</summary>
    <content type="html">&lt;p&gt;In today&amp;#39;s post, we will be looking into a software design pattern called Facade. When I first adopted it, it felt a little bit awkward, but the more I used it in my Rails apps, the more I started to appreciate its usefulness. More importantly, it allowed me to test my code more thoroughly, to clean out my controllers, to reduce the logic within my views and to make me think more clearly about an application&amp;#39;s code&amp;#39;s overall structure.&lt;/p&gt;
&lt;p&gt;Being a software development pattern, facade is framework agnostic but the examples I will provide here are for Ruby on Rails. However, I encourage you to read through this article and try them out regardless of the framework you are using. I&amp;#39;m sure that once you become familiar with this pattern, you will start seeing opportunities to use it in many parts of your codebase.&lt;/p&gt;
&lt;p&gt;Without further ado, let&amp;#39;s dive right in!&lt;/p&gt;
&lt;h2&gt;The Problem with the MVC Pattern&lt;/h2&gt;
&lt;p&gt;The MVC (Model-View-Controller) pattern is a software development pattern that dates back to the 1970s. It&amp;#39;s a battle-tested solution for designing software interfaces, separating programming concerns into three main groups that communicate amongst each other in a unique way.&lt;/p&gt;
&lt;p&gt;Many large web frameworks emerged in the early 2000s with the MVC pattern as their foundation. Spring (for Java), Django (for Python) and Ruby on Rails (for Ruby), were all forged with this trinity of interconnected elements at their core. Compared to the spaghetti-code resulting from software that did not make use of it, the MVC pattern was a huge achievement and turning point in the evolution of both software development and the internet.&lt;/p&gt;
&lt;p&gt;In essence, the Model-View-Controller pattern allows for the following: a user performs an action on the View. The View triggers a request to a Controller which can potentially create/read/update or delete a Model. The Model transaction responds back to the Controller, which in turn renders some change that the user will see reflected in the View.&lt;/p&gt;
&lt;p&gt;There are plenty of pros to this programming pattern. To list some:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It improves code maintainability by separating concerns&lt;/li&gt;
&lt;li&gt;It allows for greater testability (the Models, Views and Controllers can be tested in isolation)&lt;/li&gt;
&lt;li&gt;It encourages good coding practices by enforcing the Single Responsibility Principle of SOLID: &amp;quot;A class should have only one reason to change.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A phenomenal achievement for its time, developers soon realized that the MVC pattern was also somewhat limiting. Variants started to emerge, such as HMVC (hierarchical model–view–controller), MVA (model–view–adapter), MVP (model–view–presenter), MVVM (model–view–viewmodel) and others, which all sought to address the limitations of the MVC pattern.&lt;/p&gt;
&lt;p&gt;One of the problems that the MVC pattern introduces, and the topic of today&amp;#39;s article, is the following: who is responsible for handling complex view logic? The view should simply be concerned with presenting the data, the controller is just relaying the message it received from the model, and the model should not be concerned with any view logic.&lt;/p&gt;
&lt;p&gt;To help with this common conundrum, all Rails applications get initialized with a &lt;code&gt;helpers&lt;/code&gt; directory. The &lt;code&gt;helper&lt;/code&gt; directory can contain modules with methods that assist in complex View logic.&lt;/p&gt;
&lt;p&gt;Here is an example of a helper within a Rails application:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;app/helpers/application_helper.rb&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module ApplicationHelper
  def display_ad_type(advertisement)
    type = advertisement.ad_type
    case type
    when &amp;#39;foo&amp;#39;
      content_tag(:span, class: &amp;quot;foo ad-#{type}&amp;quot;) { type }
    when &amp;#39;bar&amp;#39;
      content_tag(:p, &amp;#39;bar advertisement&amp;#39;)
    else
      content_tag(:span, class: &amp;quot;badge ads-badge badge-pill ad-#{type}&amp;quot;) { type }
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example is simple but demonstrates the fact that you would want to extract this kind of decision making from the template itself in order to reduce its complexity.&lt;/p&gt;
&lt;p&gt;Helpers are nice, but there is yet another pattern for handling complicated View logic that has become accepted through the years, and that is the Facade pattern.&lt;/p&gt;
&lt;h2&gt;Introduction to the Facade Pattern&lt;/h2&gt;
&lt;p&gt;In a Ruby on Rails application, facades are usually placed within the &lt;code&gt;app/facades&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;While similar to &lt;code&gt;helpers&lt;/code&gt;, &lt;code&gt;facades&lt;/code&gt; are not a group of methods within a module. A Facade is a PORO (Plain Old Ruby Object) that is instantiated within the controller, but one that handles elaborate View business logic. As such, it allows the following benefits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rather than having a single module for &lt;code&gt;UsersHelper&lt;/code&gt; or &lt;code&gt;ArticlesHelper&lt;/code&gt; or &lt;code&gt;BooksHelper&lt;/code&gt;, each controller action can have its own Facade: &lt;code&gt;Users::IndexFacade&lt;/code&gt;, &lt;code&gt;Articles::ShowFacade&lt;/code&gt;, &lt;code&gt;Books::EditFacade&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;More so than modules, facades encourage good coding practices by allowing you to nest facades to ensure the Single Responsibility Principle is enforced. While you probably don&amp;#39;t want facades that are nested hundreds of levels deep, having one or two layers of nesting for improved maintainability and test coverage can be a good thing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is a contrived example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Books
  class IndexFacade
    attr_reader :books, :params, :user

    def initialize(user:, params:)
      @params = params
      @user   = user
      @books  = user.books
    end

    def filtered_books
      @filtered_books ||= begin
        scope = if query.present?
                  books.where(&amp;#39;name ILIKE ?&amp;#39;, &amp;quot;%#{query}%&amp;quot;)
                elsif isbn.present?
                  books.where(isbn: isbn)
                else
                  books
                end

        scope.order(created_at: :desc).page(params[:page])
      end
    end

    def recommended
      # We have a nested facade here.
      # The `Recommended Books` part of the view has a
      # single responsibility so best to extract it
      # to improve its encapsulation and testability.
      @recommended ||= Books::RecommendedFacade.new(
        books: books,
        user: user
      )
    end

    private

    def query
      @query ||= params[:query]
    end

    def isbn
      @isbn ||= params[:isbn]
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;When Not to Use the Facade Pattern&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s take a moment to also reflect on what facades are not.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Facades should not be placed in classes that live, for example, in the &lt;code&gt;lib&lt;/code&gt; directory for code that needs to be displayed in the View. The facade&amp;#39;s lifecycle should be generated in the Controller action and be used in its associated View.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Facades are not meant to be used for business logic to perform CRUD actions (there are other patterns for that, such as Services or Interactors—but that is a subject for another day.) In other words, facades should not be concerned with creating, updating or deleting. Their aim is to extract intricate presentation logic from the View or Controller and offer a single interface to access all that information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Last but not least, Facades are not a silver bullet. They do not allow you to bypass the MVC pattern, but rather, they play along with it. If a change occurs in a Model, it will not be immediately reflected in the View. As is always the case with MVC, the controller action would have to be re-rendered in order for the Facade to display changes on the View.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Controller Benefits&lt;/h2&gt;
&lt;p&gt;One of the main, obvious benefits of Facades is that they will allow you to dramatically reduce the controller logic.&lt;/p&gt;
&lt;p&gt;Your controller code will be reduced from something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class BooksController &amp;lt; ApplicationController
  def index
    @books  = if params[:query].present?
                current_user.books.where(&amp;#39;name ILIKE ?&amp;#39;, &amp;quot;%#{params[:query]}%&amp;quot;)
              elsif params[:isbn].present?
                current_user.books.where(isbn: params[:isbn])
              else
                current_user.books
              end

    @books.order(created_at: :desc).page(params[:page])
    @recommended = @books.where(some_complex_query: true)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class BooksController &amp;lt; ApplicationController
  def index
    @index_facade = Books::IndexFacade.new(user: current_user, params: params)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;View Benefits&lt;/h2&gt;
&lt;p&gt;For the Views, there are two main benefits when using Facades:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Conditional checks, inline queries and other logic can be neatly extracted from the template itself making the code far more readable. For instance, you could use it in a form:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= f.label :location %&amp;gt;
&amp;lt;%= f.select :location, options_for_select(User::LOCATION_TYPES.map { |type| [type.underscore.humanize, type] }.sort.prepend([&amp;#39;All&amp;#39;, &amp;#39;all&amp;#39;])), multiple: (current_user.active_ips.size &amp;gt; 1 &amp;amp;&amp;amp; current_user.settings.use_multiple_locations?) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Could just become:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;&amp;lt;%= f.label :location %&amp;gt;
&amp;lt;%= f.select :location, options_for_select(@form_facade.user_locations), multiple: @form_facade.multiple_locations? %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Variables that get called multiple times can be cached. This can offer significant performance improvements to your app and help remove pesky N+1 queries:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;// Somewhere in the view, a query is performed.
&amp;lt;% current_user.books.where(isbn: params[:isbn]).each do |book| %&amp;gt;
  // Do things
&amp;lt;% end %&amp;gt;

// Somewhere else in the view, the same query is performed again.
&amp;lt;% current_user.books.where(isbn: params[:isbn]).each do |book| %&amp;gt;
  // Do things
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;would become:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;// Somewhere in the view, a query is performed.
&amp;lt;% @index_facade.filtered_books.each do |book| %&amp;gt;
  // Do things
&amp;lt;% end %&amp;gt;

// Somewhere else in the view.
// Second query is not performed due to instance variable caching.
&amp;lt;% @index_facade.filtered_books.each do |book| %&amp;gt;
  // Do things
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Testing Benefits&lt;/h2&gt;
&lt;p&gt;A major benefit of Facades is that they allow you to test singular bits of business logic without having to write an entire controller test, or worse, without having to write an integration test that goes through a flow and reaches a page just to ensure that the data presentation is as expected.&lt;/p&gt;
&lt;p&gt;As you will be testing single POROs, this will help maintain a fast test suite.&lt;/p&gt;
&lt;p&gt;Here is a simple example of a test written in Minitest for demonstration purposes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;test_helper&amp;#39;

module Books
  class IndexFacadeTest &amp;lt; ActiveSupport::TestCase
    attr_reader :user, :params

    setup do
      @user = User.create(first_name: &amp;#39;Bob&amp;#39;, last_name: &amp;#39;Dylan&amp;#39;)
      @params = {}
    end

    test &amp;quot;#filtered_books returns all user&amp;#39;s books when params are empty&amp;quot;
      index_facade = Books::IndexFacade.new(user: user, params: params)

      expectation = user.books.order(created_at: :desc).page(params[:page])

      # Without writing an entire controller test or
      # integration test, we can check whether using the facade with
      # empty parameters will return the correct results
      # to the user.
      assert_equal expectation, index_facade.filtered_books
    end

    test &amp;quot;#filtered_books returns books matching a query&amp;quot;
      @params = { query: &amp;#39;Lord of the Rings&amp;#39; }
      index_facade = Books::IndexFacade.new(user: user, params: params)

      expectation = user
        .books
        .where(&amp;#39;name ILIKE ?&amp;#39;, &amp;quot;%#{params[:query]}%&amp;quot;)
        .order(created_at: :desc)
        .page(params[:page])

      assert_equal expectation, index_facade.filtered_books
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unit testing facades considerably improves test suite performance, and every large company will eventually encounter slow test suites unless problems like these aren’t addressed with some level of seriousness.&lt;/p&gt;
&lt;h2&gt;One Facade, Two Facades, Three Facades, More?&lt;/h2&gt;
&lt;p&gt;You might encounter a scenario where a View renders a partial that outputs some data. In that case, you have the option of either using the parent facade or using a nested facade. That largely depends on how much logic is involved, whether you want to test it separately and whether it makes sense to extract the functionality.&lt;/p&gt;
&lt;p&gt;There is no golden rule for how many facades to use or how many facades to nest within each other. That is to the developer&amp;#39;s discretion. I generally prefer to have a single facade for the controller action and I limit nesting to a single level to make the code easier to follow.&lt;/p&gt;
&lt;p&gt;Here are some general questions you can ask yourself during development:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does the facade encapsulate the logic I am trying to present on the view?&lt;/li&gt;
&lt;li&gt;Does the method within the facade make sense in this context?&lt;/li&gt;
&lt;li&gt;Is the code easier to follow now, or harder to follow?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When in doubt, always strive to make your code as easy to follow as possible.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In conclusion, facades are a fantastic pattern to keep your controllers and views lean, while improving code maintainability, performance and testability.&lt;/p&gt;
&lt;p&gt;However, like any programming paradigm, there is no silver bullet. Even the multitude of patterns that have emerged in more recent years (HMVC, MVVM, etc.) are not be-all-end-all solutions to the complications of software development.&lt;/p&gt;
&lt;p&gt;Similar to the second law of thermodynamics, which states that the state of entropy in a closed system will always increase, so too in any software project does the complexity increase and evolve over time. In the long run, the goal is to write code that is as easy to read, test, maintain and follow as possible; facades offer exactly this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Building a Rails App With Multiple Subdomains</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/03/04/building-a-rails-app-with-multiple-subdomains.html"/>
    <id>https://blog.appsignal.com/2020/03/04/building-a-rails-app-with-multiple-subdomains.html</id>
    <published>2020-03-04T00:00:00+00:00</published>
    <updated>2020-03-04T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Learn how to build a Rails app that can support multiple subdomains.</summary>
    <content type="html">&lt;p&gt;In today&amp;#39;s post, we&amp;#39;ll learn how to build a Rails app that can support multiple
subdomains. Let&amp;#39;s assume that we have a gaming website &lt;code&gt;funkygames.co&lt;/code&gt; and we want to support multiple subdomains such as &lt;code&gt;app.funkygames.co&lt;/code&gt;, &lt;code&gt;api.funkygames.co&lt;/code&gt;, and &lt;code&gt;dev.funkygames.co&lt;/code&gt; with a single Rails application. We want to ensure that proper authentication is performed for all subdomains and that there are no duplicate routes.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll use Rails&amp;#39; powerful routing constructs to support multiple subdomains in our application. We&amp;#39;ll also set up subdomains locally and write tests for multiple subdomains.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;For the purpose of this post, I&amp;#39;m assuming that you have set up appropriate DNS records for all the subdomains to point to the Rails app. We&amp;#39;ll only deal with the Rails side of things in this post.&lt;/p&gt;
&lt;h2&gt;Handling Multiple Subdomains&lt;/h2&gt;
&lt;p&gt;Rails uses &lt;code&gt;routes.rb&lt;/code&gt; file to handle incoming requests and map them to
specific controller actions. In a trivial app, every mapping in &lt;code&gt;routes.rb&lt;/code&gt;
maps a route to a controller action as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;  get &amp;#39;/games/:id&amp;#39;, to: &amp;#39;games#show&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this approach, all the endpoints defined in our &lt;code&gt;routes.rb&lt;/code&gt; file
are applicable to all the subdomains. So &lt;code&gt;app.funkygames.co/games/1&lt;/code&gt; as well as &lt;code&gt;api.funkygames.co/games/1&lt;/code&gt; will be handled by this route. However, we only want the request coming from &lt;code&gt;app&lt;/code&gt; subdomain to be handled by this route. The &lt;code&gt;api&lt;/code&gt; subdomain is only to be used for API routes. We&amp;#39;ll add some rules to the routes so that they are handled only if a specific rule is met for the incoming request.&lt;/p&gt;
&lt;p&gt;Rails routing provides a &lt;code&gt;constraints&lt;/code&gt; helper method which can specify additional rules for the given route.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;  get &amp;#39;/games/:id&amp;#39;, to: &amp;#39;games#show&amp;#39;, constraints: { subdomain: &amp;#39;app&amp;#39; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will ensure that if the request is coming from &lt;code&gt;app.funkygames.co/games/1&lt;/code&gt;, it will be handled by &lt;code&gt;GamesController&amp;#39;s&lt;/code&gt; show action. Any request from other subdomains apart from &lt;code&gt;app&lt;/code&gt; will not be handled by this route.&lt;/p&gt;
&lt;p&gt;It will become very cumbersome to define &lt;code&gt;constraints&lt;/code&gt; like this for each and every route.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;  get &amp;#39;/games/:id&amp;#39;, to: &amp;#39;games#show&amp;#39;, constraints: { subdomain: &amp;#39;app&amp;#39; }
  get &amp;#39;/games/list&amp;#39;, to: &amp;#39;games#list&amp;#39;, constraints: { subdomain: &amp;#39;app&amp;#39; }
  post &amp;#39;/games/start&amp;#39;, to: &amp;#39;games#start&amp;#39;, constraints: { subdomain: &amp;#39;app&amp;#39; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use the block form of the &lt;code&gt;constraints&lt;/code&gt; helper to define multiple
routes for a single subdomain.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;  constraints subdomain: &amp;#39;app&amp;#39; do
    get &amp;#39;/games/:id&amp;#39;, to: &amp;#39;games#show&amp;#39;
    get &amp;#39;/games/list&amp;#39;, to: &amp;#39;games#list&amp;#39;
    post &amp;#39;/games/start&amp;#39;, to: &amp;#39;games#start&amp;#39;
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To define routes for multiple subdomains, we just have to add multiple &lt;code&gt;constraints&lt;/code&gt; blocks in our &lt;code&gt;routes.rb&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;constraints subdomain: &amp;#39;app&amp;#39; do
  ...
end

constraints subdomain: &amp;#39;api&amp;#39; do
  ...
end

constraints subdomain: &amp;#39;dev&amp;#39; do
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Under the Hood&lt;/h3&gt;
&lt;p&gt;Rails routing provides request constraints and segment constraints. Segment constraints add rules on the request path whereas request constraints add conditions on the incoming request. The hash key in a request constraint needs to be a method on the &lt;code&gt;Request&lt;/code&gt; object that returns a string and the value needs to be the expected value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;constraints subdomain: &amp;#39;app&amp;#39; do
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above case, we are using the &lt;code&gt;subdomain&lt;/code&gt; method on the &lt;code&gt;Request&lt;/code&gt; object and matching it with a string like &lt;code&gt;app&lt;/code&gt;, &lt;code&gt;api&lt;/code&gt; or &lt;code&gt;dev&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For more details, consult the &lt;a href=&quot;https://guides.rubyonrails.org/routing.html&quot;&gt;Rails routing guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Handling Multi-level Subdomains&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s say we are using &lt;code&gt;app.staging.funkygames.co&lt;/code&gt; for our staging environment. If we have the setup above, we will quickly notice that all the requests that are supposed to hit the &lt;code&gt;app&lt;/code&gt; subdomain are returning a 404. If we debug things further, we will notice that our constraint for the subdomain is failing.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;request.subdomain #=&amp;gt; app.staging
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We expected the subdomain to return &lt;code&gt;app&lt;/code&gt;, but instead, it returns &lt;code&gt;app.staging&lt;/code&gt;. Of course, we want to solve this without adding environment-specific code! The parsing of request&amp;#39;s subdomain is managed by &lt;code&gt;config.action_dispatch.tld_length&lt;/code&gt; option. The default value of this configuration is 1, which basically supports one level of subdomains. As we have two level subdomains, we need to set the value for &lt;code&gt;config.action_dispatch.tld_length&lt;/code&gt; to 2.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# config/application.rb
config.action_dispatch.tld_length = Integer(ENV[&amp;#39;TLD_LENGTH&amp;#39;] || 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can set it using an environment variable so that we can use the same code in the staging as well as in the production environment. Now, our routing setup will work for &lt;code&gt;app.staging.funkygames.co&lt;/code&gt; as well.&lt;/p&gt;
&lt;h2&gt;Session Management&lt;/h2&gt;
&lt;p&gt;Now that routes are defined to handle requests coming from multiple subdomains,
we need to take care of authentication for all the subdomains. We can do this in two ways—we can either allow the same user session to be used across all subdomains, or we can have separate sessions for separate subdomains.&lt;/p&gt;
&lt;h3&gt;Authentication in a Nutshell&lt;/h3&gt;
&lt;p&gt;Rails uses cookies to store user session key by default. Once the user logs in, the user&amp;#39;s session information is stored in the session store of our choice and the session key is stored as a cookie in the browser. So the next time the user visits our website, the same session cookie is sent from the browser to the server and the server decides whether the user is logged in or not based on whether the session exists for the incoming session cookie.&lt;/p&gt;
&lt;p&gt;The default configuration for the session looks like this in the Rails app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.config.session_store :cookie_store, key: &amp;quot;_funkygames_session&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key &lt;code&gt;_funkygames_session&lt;/code&gt; will be used as the name of the session cookie and its value will be the session id.&lt;/p&gt;
&lt;h3&gt;Cookies Primer&lt;/h3&gt;
&lt;p&gt;By default, cookies are set by the browser on the request&amp;#39;s domain. So if we are hitting our application from &lt;code&gt;app.funkygames.co&lt;/code&gt; then the session cookie will be set against &lt;code&gt;app.funkygames.co&lt;/code&gt;. Each subdomain will set its own session cookies, therefore the user session will not be shared across subdomains by default.&lt;/p&gt;
&lt;h3&gt;Sharing Session between Different Subdomains&lt;/h3&gt;
&lt;p&gt;If we want to share the user session across subdomains, we&amp;#39;ll need to set the session cookie on the &lt;code&gt;funkygames.co&lt;/code&gt; domain itself so that all subdomains can access it. This can be achieved by passing the &lt;code&gt;domain&lt;/code&gt; option to the session store settings.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.config.session_store :cookie_store, key: &amp;quot;_funkygames_session&amp;quot;, domain: :all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By passing &lt;code&gt;domain&lt;/code&gt; as &lt;code&gt;:all&lt;/code&gt;, we are basically telling Rails to set the session cookie on the top-level domain of the application such as &lt;code&gt;funkygames.co&lt;/code&gt; instead of on the request host which may include the individual subdomains. Once we do this, the session can be shared between different subdomains.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We can also pass a list of domains to the &lt;code&gt;domains&lt;/code&gt; option in an array format to support multiple domains.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is one more option that needs to be configured to properly set the cookies for all subdomains. It is the &lt;code&gt;tld_length&lt;/code&gt; option. When using &lt;code&gt;domain: :all&lt;/code&gt;, this option can specify how to parse the domain to interpret the TLD of the domain. In our case, for &lt;code&gt;app.funkygames.co&lt;/code&gt;, we should set &lt;code&gt;tld_length&lt;/code&gt; to 2 for Rails to interpret the TLD as &lt;code&gt;funkygames.co&lt;/code&gt; when setting up the cookies. So the final session store configuration for multiple subdomains looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Rails.application.config.session_store :cookie_store,
                                       key: &amp;quot;_funkygames_session&amp;quot;,
                                       domain: :all,
                                       tld_length: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;tld_length&lt;/code&gt; option from the session store is different from the &lt;code&gt;config.action_dispatch.tld_length&lt;/code&gt; discussed earlier.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Writing Tests for Multiple Subdomains&lt;/h2&gt;
&lt;p&gt;As the routes are subdomain specific, the request specs or integration tests result in 404 errors if the test request does not have a proper subdomain. Rails integration tests provide a &lt;code&gt;host!&lt;/code&gt; helper which can set the proper subdomain for all requests made within a test file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Configuring subdomain in Rails integration tests
setup do
  host! &amp;#39;dev.example.com&amp;#39;
end

# # Configuring subdomain in RSpec request specs
before do
 host! &amp;#39;dev.example.com&amp;#39;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After this, the requests will be correctly routed to the controller actions as per subdomain routing in &lt;code&gt;routes.rb&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Note that the domain does not matter here, only the proper subdomain based in the code we are testing matters.&lt;/p&gt;
&lt;h2&gt;Setting up Multiple Subdomains Locally for Development&lt;/h2&gt;
&lt;p&gt;There are multiple ways to set up subdomains locally. The simplest is editing the &lt;code&gt;/etc/hosts&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;127.0.0.1 dev.funkygames.local
127.0.0.1 app.funkygames.local
127.0.0.1 api.funkygames.local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures that the subdomains setup will work in a local environment. We can also use tools such as &lt;a href=&quot;http://pow.cx/&quot;&gt;pow&lt;/a&gt; for managing subdomains locally.&lt;/p&gt;
&lt;h2&gt;Gotchas with Constraints Based Subdomain Routing&lt;/h2&gt;
&lt;p&gt;Though the constraints based subdomain routing works in most cases, it can be a pain in certain situations.&lt;/p&gt;
&lt;h3&gt;Dealing with External APIs&lt;/h3&gt;
&lt;p&gt;When we are working with third-party APIs and building integrations, the local development TLDs such as &lt;code&gt;.local&lt;/code&gt; or &lt;code&gt;.dev&lt;/code&gt; are not allowed. We have to use tools such as &lt;a href=&quot;https://ngrok.com/&quot;&gt;ngrok&lt;/a&gt;. The subdomain based routing does not work in such cases and we have to whitelist certain routes so that they are accessible via ngrok as well.&lt;/p&gt;
&lt;h3&gt;Routes Outside of Subdomains Constraints&lt;/h3&gt;
&lt;p&gt;Certain routes can&amp;#39;t be placed inside the subdomain constraints. A typical example is &lt;code&gt;healthcheck&lt;/code&gt; or &lt;code&gt;ping&lt;/code&gt; endpoints. If we are using a load balancer in front of our Rails app, the load balancer needs to periodically check if the app is up or not. The &lt;code&gt;healthcheck&lt;/code&gt; endpoint used in such cases can&amp;#39;t be under subdomain constraints as the load balancer most probably won&amp;#39;t have knowledge of the request host.&lt;/p&gt;
&lt;h3&gt;Absence of root Route&lt;/h3&gt;
&lt;p&gt;Rails has a special &lt;code&gt;root&lt;/code&gt; route which is basically the default route of the application. If none of the other routes are matched with the request, then the &lt;code&gt;root&lt;/code&gt; route is used. When we have all of our routes under any one of the subdomains, then there can be situations where we don&amp;#39;t have any &lt;code&gt;root&lt;/code&gt; route defined at all.
Certain gems might depend on the presence of a &lt;code&gt;root&lt;/code&gt; route and we need to add checks and balances accordingly.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this post, we set up a Rails app with multiple subdomains with very few lines of configuration. We also saw how to set up the subdomains locally as well as with different environments, with tips on writing effective tests for multiple subdomains. With the plumbing provided by Rails, it becomes easy to set up and test a Rails app with multiple subdomains.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Getting Started With System Tests in Rails With Minitest</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/02/12/getting-started-with-system-tests-in-ruby-with-minitest.html"/>
    <id>https://blog.appsignal.com/2020/02/12/getting-started-with-system-tests-in-ruby-with-minitest.html</id>
    <published>2020-02-12T00:00:00+00:00</published>
    <updated>2020-02-12T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Dive into instances when generic methods are worth replacing with specific alternatives, illustrated by cleaning up strings.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;This post was updated on 4 August 2023 to remove some outdated content and replace it with a new &amp;#39;Running a Basic System For the First Time&amp;#39; section.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;System tests are meant to auto-test the way users interact with your application, including the Javascript in your user interface.
Minitest, being the default testing framework in Rails, is a great match for system testing. It is perfect
for getting started with system testing Rails apps as it&amp;#39;s small and fast, giving you the ability to write clean and readable tests.&lt;/p&gt;
&lt;p&gt;In today&amp;#39;s article, we&amp;#39;ll look into using Minitest to run some simple system tests in a Rails 7 application. By the end of the tutorial, you should know what Minitest is and how to start using it in your application.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s get started!&lt;/p&gt;
&lt;h2&gt;Introduction to System Tests&lt;/h2&gt;
&lt;p&gt;In Rails jargon, system testing refers to &amp;quot;testing an application as a whole system&amp;quot;. Instead of testing separate parts, with system tests, we can test a whole &amp;#39;workflow&amp;#39;, just like a
user interacting with our app. In a nutshell, system tests are automated &amp;quot;A-to-Z&amp;quot; tests in Ruby on Rails apps. On the other hand, they are different from integration tests: integration tests are for testing behavior, especially of every part of an app together, but not via the user interface.&lt;/p&gt;
&lt;p&gt;Now that we know what system tests are, let&amp;#39;s continue with the example app we&amp;#39;ll be using today.&lt;/p&gt;
&lt;h2&gt;Generating a New Rails App&lt;/h2&gt;
&lt;p&gt;Run the command below to generate a very basic Ruby on Rails 7 app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails new minitest-rails-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that is done, let&amp;#39;s see what we need to begin running some system tests.&lt;/p&gt;
&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;p&gt;Go ahead and open up the project in your favorite editor and proceed to the &lt;code&gt;Gemfile&lt;/code&gt;, specifically to the &lt;code&gt;test&lt;/code&gt; block:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

...

group :test do
  # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
  gem &amp;quot;capybara&amp;quot;
  gem &amp;quot;selenium-webdriver&amp;quot;
  gem &amp;quot;webdrivers&amp;quot;
end

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The gems you see listed there are the basic tooling we need to run some system tests in our app. Now, let&amp;#39;s see what each gem does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Capybara&lt;/strong&gt; - To begin with, the Capybara gem is used for interacting with the browser. It&amp;#39;s through Capybara that we can make the tests visit pages, fill in forms, click on links and buttons. If you&amp;#39;ve worked with Capybara before, you&amp;#39;re aware of all the things you have to coordinate to make it work with a database cleaning strategy and the browser configuration. Hooray for the Rails system test set up: it takes care of all the configuration that is needed to work with Capybara out-of-the-box, so we can focus on the actual test writing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Selenium webdriver&lt;/strong&gt; - Selenium webdriver is a very popular gem that is used to interact with the browser and works by simulating real-user interactions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Webdrivers&lt;/strong&gt; - This gem works hand-in-hand with Selenium webdriver by providing &amp;quot;hooks&amp;quot; into almost all of the modern browsers available today.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, let&amp;#39;s do a quick scaffold generation to have something to work with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails generate scaffold Blog title:string body:text
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usually, generating a scaffold will automatically generate the &lt;code&gt;application_system_test_case.rb&lt;/code&gt; and everything you need for the system tests:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;#application_system_test_case.rb (default)


require &amp;quot;test_helper&amp;quot;

class ApplicationSystemTestCase &amp;lt; ActionDispatch::SystemTestCase
  driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;When Eileen Uchitelle &lt;a href=&quot;https://www.youtube.com/watch?v=sSn4B8orX70&quot;&gt;introduced system tests&lt;/a&gt;, Chrome was chosen over Firefox because at the time Firefox didn&amp;#39;t play nicely with Selenium.
That was fixed in later versions of Firefox, so I replace Chrome with Firefox, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;#application_system_test_case.rb (change driver to Firefox)

require &amp;quot;test_helper&amp;quot;

class ApplicationSystemTestCase &amp;lt; ActionDispatch::SystemTestCase
  driven_by :selenium, using: :firefox, screen_size: [1400, 1400]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The other important thing to note is that the Selenium driver is different from Capybara&amp;#39;s default and is chosen because it works with Javascript. And this is all you need to run basic system tests.&lt;/p&gt;
&lt;h2&gt;Running a Basic System For the First Time&lt;/h2&gt;
&lt;p&gt;We can quickly do a quick system test from our basic app to get a feel of what they are all about by running the command below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;rails test:system
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If everything goes as expected, you should see a browser window automatically open and the a complete simulation of a user creating a blog post (using our example scaffold)
being done by the system test suite. After it completes, you&amp;#39;ll get a summary like the one below in the terminal in your app&amp;#39;s root:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-08/successful-system-test-headful.png&quot; alt=&quot;Successful system test run&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Quick tip: If you hadn&amp;#39;t run any database migrations when you run this command, you might get an error telling you to run the migrations first, go ahead and do so then re-run it.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Choosing Between a Real Browser and Headless Browser&lt;/h2&gt;
&lt;p&gt;I can&amp;#39;t get enough of watching the system tests running in the browser and see how all the links are clicked, forms are filled in, etc. But, it is slow. To speed up the test run, you can use a &amp;#39;headless&amp;#39; browser: that is a browser that has the same access to your app as a regular browser, but without the graphical user interface. Meaning: it works the same but you don&amp;#39;t actually see the tests doing their work since a headless driver doesn&amp;#39;t open an actual browser window.&lt;/p&gt;
&lt;p&gt;If you do want to go headless, there&amp;#39;s &lt;code&gt;headless_chrome&lt;/code&gt; and &lt;code&gt;headless_firefox&lt;/code&gt;. To use them, there&amp;#39;s one small change needed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# To change driver to headless_*
#application_system_test_case.rb (change driver to headless_*:)

require &amp;quot;test_helper&amp;quot;

class ApplicationSystemTestCase &amp;lt; ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_firefox
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After making this change, you can re-run the basic &lt;code&gt;rails test:system&lt;/code&gt; test we did earlier, this time using the headless Firefox driver. See if you get results similar to mine below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2023-08/successful-system-test-headless.png&quot; alt=&quot;Successful system test run with headless Firefox&quot;/&gt;&lt;/p&gt;
&lt;p&gt;One thing you&amp;#39;ll note with running headful versus headless is that the headless version is a bit faster compared to the headful version. If you are running lots of system tests, headless is the
way to go since it will save you some time. But if watching the automated browser interacting with the default headful version is your thing, go ahead and run the default headful version.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s more customization you can do, but to get started, this is really all you need.&lt;/p&gt;
&lt;h2&gt;Running the Tests&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;$ rails test&lt;/code&gt; will run all the tests except the system tests. You need to explicitly run &lt;code&gt;$ rails test:system&lt;/code&gt;. (Fun fact: the &lt;code&gt;$ rails&lt;/code&gt; command will always run through bin/rails. No need to type &lt;code&gt;$ bin/rails&lt;/code&gt; anymore.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run all the system tests:
&lt;code&gt;$ rails test:system&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run test in a specific file (here: users_test.rb)
&lt;code&gt;$ rails test/system/users_test&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;... or one specific test:
&lt;code&gt;$ rails test test/system/users_test.rb:21&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;To run all the tests: run the system tests first:
&lt;code&gt;$ rails test:system test&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note: the options flags don&amp;#39;t work with &lt;code&gt;test:system&lt;/code&gt;; if you want to use flags like &lt;code&gt;-f&lt;/code&gt; (for fail fast) or &lt;code&gt;-v&lt;/code&gt; (for verbose), use &lt;code&gt;$ rails test test/system -v -f&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;What (Not) to Test&lt;/h2&gt;
&lt;p&gt;System tests are complementary to unit tests, not a substitute. It will do to test a happy path, plus maybe one path with an error message or redirect. System tests are not meant to test all the edge cases in the browser; just cover the main features.&lt;/p&gt;
&lt;p&gt;When choosing my test subject, I try to find an entity to test that reflects how the user uses the app. For the naming of my tests, I borrowed GitLab&amp;#39;s naming convention of ROLE_ACTION_test.rb, which fits this approach well. For instance: &lt;code&gt;user_shares_card_test.rb&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;General Tips and Tricks&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;If you use Devise for authentication, you can use the Devise integration helpers to log in and log out test users. Add &lt;code&gt;include Devise::Test::IntegrationHelpers&lt;/code&gt; to a test class, or add it in the &lt;code&gt;test_helper.rb&lt;/code&gt; file to make them available in all tests. Now you can create a user and &lt;code&gt;sign_in(@user)&lt;/code&gt;. If you put that in a &lt;code&gt;setup&lt;/code&gt; method, you don’t need to log out the user in a teardown. Rails takes care of cleaning up what&amp;#39;s in the setup method.&lt;/li&gt;
&lt;li&gt;Working with forms, it&amp;#39;s tempting to reference the ids that Rails creates automatically from model name + field name and the button types for &lt;code&gt;click_on&lt;/code&gt;s. ( &lt;code&gt;fill_in &amp;quot;user_email&amp;quot;&lt;/code&gt;, &lt;code&gt;click_on :commit&lt;/code&gt;). But since systems tests are about what a user would actually &lt;em&gt;see&lt;/em&gt; on their screen, it makes sense to use visible elements, i.e. texts. A reasonable option would be to have reference keys in i18n locale files and use those keys instead of the ever-changing literal texts. (&lt;code&gt;fill_in :user_email&lt;/code&gt;). Capybara finds the text only with the full I18n syntax: &lt;code&gt;assert_selector &amp;quot;h1&amp;quot;, text: I18n.t(&amp;quot;activerecord.models.things&amp;quot;)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The path helpers are included by default. For some libraries you need to include the helpers in the test class, for instance &lt;code&gt;include ActionMailer::TestHelper&lt;/code&gt;, to use the &lt;code&gt;assert_emails&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Create custom classes to run the tests on different screen sizes. &lt;a href=&quot;https://guides.rubyonrails.org/testing.html#testing-for-multiple-screen-sizes&quot;&gt;Check out the guides&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The screenshot feature can also be used to take screenshots for your documentation and promotional materials. If you make it a screencast and slow down the playback speed, you have a product video in minutes!&lt;/li&gt;
&lt;li&gt;Rails provides a generator for system tests.&lt;/li&gt;
&lt;li&gt;Minitest in Rails is slightly different than Minitest itself, and also adds Rails specific methods and assertions. Check the Rails docs first before the Minitest docs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had good fun with the system tests, and that was, for the most part, thanks to how easy it was to use it out-of-the-box.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this post, we looked at what configuration Rails delivers out-of-the-box and what minimal customizations we may want to add. Minitest is great for writing System tests. A few tips and tricks should help to get your first system tests up and running. Watch them do their magic in the browser!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Pros and Cons of Using structure.sql in Your Ruby on Rails Application</title>
    <link rel="alternate" href="https://blog.appsignal.com/2020/01/15/the-pros-and-cons-of-using-structure-sql-in-your-ruby-on-rails-application.html"/>
    <id>https://blog.appsignal.com/2020/01/15/the-pros-and-cons-of-using-structure-sql-in-your-ruby-on-rails-application.html</id>
    <published>2020-01-15T00:00:00+00:00</published>
    <updated>2020-01-15T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Discover the major differences and benefits of using structure.sql vs schema.rb in your Ruby on Rails application.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;This post was updated on 4 August 2023 to include a section on the advantages of &lt;code&gt;structure.sql&lt;/code&gt; over &lt;code&gt;schema.rb&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In today&amp;#39;s post, we&amp;#39;ll cover the significant differences and benefits of using &lt;code&gt;structure.sql&lt;/code&gt; versus the default &lt;code&gt;schema.rb&lt;/code&gt; schema formats in your Ruby on Rails application. In a data-driven world, knowing how to exploit all of your database&amp;#39;s rich features can make the difference between a successful and unsuccessful enterprise.&lt;/p&gt;
&lt;p&gt;After evincing the main differences between the two formats, we&amp;#39;ll outline how to switch to &lt;code&gt;structure.sql&lt;/code&gt; and demonstrate how it can help with ensuring data integrity as well as database functionality that you might otherwise not be able to preserve.&lt;/p&gt;
&lt;p&gt;In the post, I&amp;#39;ll give examples of a Rails app that makes use of &lt;code&gt;structure.sql&lt;/code&gt; with a PostgreSQL database, but the underlying concepts can be transposed to other databases as well. No real-world web application is truly complete without a reliable database to support it.&lt;/p&gt;
&lt;p&gt;Without further ado, let&amp;#39;s dive right in!&lt;/p&gt;
&lt;h2&gt;The Difference Between &lt;code&gt;schema.rb&lt;/code&gt; and &lt;code&gt;structure.sql&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;One of the first things you need to do when starting a Ruby on Rails project is to run database migrations. If you generate a User model, for instance, Rails will inevitably ask you to run migrations, which will create a &lt;code&gt;schema.rb&lt;/code&gt; file accordingly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails g model User first_name:string last_name:string
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails will generate the following migration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class CreateUsers &amp;lt; ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :first_name
      t.string :last_name

      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the migration is executed, you will find that Rails generated a &lt;code&gt;schema.rb&lt;/code&gt; file for you:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Schema.define(version: 2019_12_14_074018) do

  # These are extensions that must be enabled in order to support this database
  enable_extension &amp;quot;plpgsql&amp;quot;

  create_table &amp;quot;users&amp;quot;, force: :cascade do |t|
    t.string &amp;quot;first_name&amp;quot;
    t.string &amp;quot;last_name&amp;quot;
    t.datetime &amp;quot;created_at&amp;quot;, precision: 6, null: false
    t.datetime &amp;quot;updated_at&amp;quot;, precision: 6, null: false
  end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;code&gt;schema.rb&lt;/code&gt; file is fantastic for relatively basic applications and use cases.&lt;/p&gt;
&lt;p&gt;There are two main things to notice here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It is a Ruby representation of your database; &lt;code&gt;schema.rb&lt;/code&gt; is created by inspecting the database and expressing its structure using Ruby.&lt;/li&gt;
&lt;li&gt;It is database-agnostic (i.e. whether you use SQLite, PostgreSQL, MySQL or any other database that Rails supports, the syntax and structure will remain largely the same)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, there may come a time when this strategy becomes too limiting for your growing app.&lt;/p&gt;
&lt;p&gt;Say, for instance, you have hundreds or thousands of migration files.&lt;/p&gt;
&lt;p&gt;If you need to rapidly spin up a new production system, you might encounter a scenario where running them all in sequence takes too long. Or you might face a situation where some migrations contain code that was meant to be executed on an older version of your database, but that is no longer executable on the current version. You might have a situation where migrations were written with certain data assumptions that are no longer valid, which would cause the migrations to fail.&lt;/p&gt;
&lt;p&gt;All these scenarios prevent efficiently setting up a new instance of your application⁠—be it in production or for a new team member⁠—with a simple &lt;code&gt;rails db:create db:migrate&lt;/code&gt; command. If this were the case, how would you go about getting up to speed with a correct database schema?&lt;/p&gt;
&lt;p&gt;Certainly, one way would be to go back and fix all the broken migrations. That&amp;#39;s never a bad idea!&lt;/p&gt;
&lt;p&gt;If going back and fixing a bunch of migrations is too costly, another way would be to run the &lt;code&gt;rails db:setup&lt;/code&gt; task. This task will generate a database schema from your &lt;code&gt;schema.rb&lt;/code&gt; file. However, what if your database contained complex logic that is not represented in the &lt;code&gt;schema.rb&lt;/code&gt; representation of your database?&lt;/p&gt;
&lt;p&gt;Luckily, Rails offers an alternative: &lt;code&gt;structure.sql&lt;/code&gt;, which differs from &lt;code&gt;schema.rb&lt;/code&gt; in a number of ways, and can be a better choice.&lt;/p&gt;
&lt;h2&gt;Advantages of &lt;code&gt;structure.sql&lt;/code&gt; over &lt;code&gt;schema.rb&lt;/code&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Database-specific features&lt;/strong&gt; - &lt;code&gt;structure.sql&lt;/code&gt; allows you to use database-specific features which may not be supported in &lt;code&gt;schema.rb&lt;/code&gt;. For example, let&amp;#39;s say you want to make sure the &lt;code&gt;jsonb&lt;/code&gt; data-type that is supported by PostgreSQL but not SQLite is represented properly in your database schema. You&amp;#39;ll find that &lt;code&gt;structure.sql&lt;/code&gt; will give you a better solution compared to the standard &lt;code&gt;schema.rb&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complex migrations&lt;/strong&gt; - If your app uses complex migrations and SQL statements that need to be maintained as they are, you&amp;#39;ll find that &lt;code&gt;structure.sql&lt;/code&gt; will be exactly what you need.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintaining database state&lt;/strong&gt; - &lt;code&gt;structure.sql&lt;/code&gt; is a much better tool at capturing the exact state of a database compared to &lt;code&gt;schema.rb&lt;/code&gt;. This becomes important in cases where you need to capture the entire structure of a database as is, for instance, when dealing with legacy databases or when moving databases between different deployment servers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;#39;s definitely more advantages to using &lt;code&gt;structure.sql&lt;/code&gt; over &lt;code&gt;schema.rb&lt;/code&gt;, but we&amp;#39;ll leave it at that. It&amp;#39;s suffice to say that once an application reaches a certain level of maturity, we have to use every trick in the book to boost efficiency, preserve data correctness, and ensure blazing-fast performance. Using &lt;code&gt;structure.sql&lt;/code&gt; to manage a Rails database&amp;#39;s behavior allows for this.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Switching From &lt;code&gt;schema.rb&lt;/code&gt; to &lt;code&gt;structure.sql&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Making the change from &lt;code&gt;schema.rb&lt;/code&gt; to &lt;code&gt;structure.sql&lt;/code&gt; is a relatively straightforward process. All you need to do is set the following line in &lt;code&gt;config/application.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module YourApp
  class Application &amp;lt; Rails::Application
    config.load_defaults 6.0

    # Add this line:
    config.active_record.schema_format = :sql
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, run &lt;code&gt;rails db:migrate&lt;/code&gt; and you should see the file in &lt;code&gt;db/structure.sql&lt;/code&gt;. Voilà! Rails will dump the database structure using the tool specific to the database you are using (in PostgreSQL&amp;#39;s case, that tool is &lt;code&gt;pg_dump&lt;/code&gt;, for MySQL or MariaDB, it will contain the output of &lt;code&gt;SHOW CREATE TABLE&lt;/code&gt; for each table, etc). It is advisable to ensure this file is under version control so that the rest of your team will have the same database structure.&lt;/p&gt;
&lt;p&gt;A first glance at that file may be daunting: the &lt;code&gt;schema.rb&lt;/code&gt; file was only 25 lines, whereas the &lt;code&gt;structure.sql&lt;/code&gt; file is a &lt;a href=&quot;https://gist.github.com/tombruijn/8ec144daed3e4993d73226bb24f035c6&quot;&gt;whopping 109 lines&lt;/a&gt;! What benefits could such a large file add to the app development?&lt;/p&gt;
&lt;h2&gt;Adding Database-level Constraints&lt;/h2&gt;
&lt;p&gt;ActiveRecord is one of my favorite parts of using Rails. It allows you to query the database in a way that feels natural, almost like in a spoken language. For instance, if you want to find all a company&amp;#39;s users named Dan, then ActiveRecord allows you to simply run a query like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;company = Company.find(name: &amp;#39;Some Company&amp;#39;)

# Reads just like in a natural language!
company.users.where(first_name: &amp;#39;Dan&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are some cases in which ActiveRecord falls short though. For instance, say you have the following validation on your User model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User &amp;lt; ApplicationRecord
  validate :name_cannot_start_with_d

  private

  def name_cannot_start_with_d
    if first_name.present? &amp;amp;&amp;amp; first_name[0].downcase == &amp;#39;d&amp;#39;
      errors.add(:first_name, &amp;quot;cannot start with the letter &amp;#39;D&amp;#39;&amp;quot;)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you try to create a user with the name &amp;#39;Dan&amp;#39;, you should see an error when the validation runs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;User.create!(first_name: &amp;#39;Dan&amp;#39;)
Traceback (most recent call last):
ActiveRecord::RecordInvalid (Validation failed: First name cannot start with the letter &amp;#39;D&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is fine, but suppose you or one of your team members changed the data by bypassing ActiveRecord&amp;#39;s validation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;u = User.create(first_name: &amp;#39;Pan&amp;#39;)

# The update_attribute method bypasses ActiveRecord validations
u.update_attribute :first_name, &amp;#39;Dan&amp;#39;
u.first_name
=&amp;gt; &amp;quot;Dan&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As demonstrated, it is very easy to bypass the validation.&lt;/p&gt;
&lt;p&gt;This can have disastrous consequences for our application. ActiveRecord can be a blessing as well as a curse⁠—while it has a very clean and natural DSL that makes it a pleasure to work with, it is often overly permissive when enforcing model-level validations. The solution, as you may already know, is to add database-level constraints.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails g migration AddFirstNameConstraintToUser
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate a file that you can edit with the logic to disallow first names that start with the letter &amp;#39;D&amp;#39;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AddFirstNameConstraintToUser &amp;lt; ActiveRecord::Migration[6.0]
  def up
    execute &amp;quot;ALTER TABLE users ADD CONSTRAINT name_cannot_start_with_d CHECK (first_name !~* &amp;#39;^d&amp;#39;)&amp;quot;
  end

  def down
    execute &amp;quot;ALTER TABLE users DROP CONSTRAINT IF EXISTS name_cannot_start_with_d&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that it is &lt;em&gt;very&lt;/em&gt; important to add code that successfully reverts the migration. In the above example, I have &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; directives. The &lt;code&gt;up&lt;/code&gt; method gets executed when the migration runs, &lt;code&gt;down&lt;/code&gt; gets executed when the migration is rolled back. Without properly reverting your database structure, you may have to do some manual house-cleaning later. I&amp;#39;d recommend always having a migration file that can be executed both &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; to avoid future headaches.&lt;/p&gt;
&lt;p&gt;Now, run the migration and check whether you can bypass that constraint:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;user = User.create first_name: &amp;#39;Pan&amp;#39;
user.update_attribute :first_name, &amp;#39;Dan&amp;#39;

ActiveRecord::StatementInvalid (PG::CheckViolation: ERROR:  new row for relation &amp;quot;users&amp;quot; violates check constraint &amp;quot;name_cannot_start_with_d&amp;quot;)
DETAIL:  Failing row contains (2, Dan, null, 2019-12-14 09:40:11.809358, 2019-12-14 09:40:41.658974).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Perfect! Our constraint is working as intended. Even if, for whatever reason, we bypass ActiveRecord&amp;#39;s validation, we can still rely on the database⁠—our ultimate goalkeeper⁠—to preserve our data integrity.&lt;/p&gt;
&lt;p&gt;What does this have to do with &lt;code&gt;structure.sql&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;If you take a look at it, you&amp;#39;ll see that the following was added:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE public.users (
    id bigint NOT NULL,
    first_name character varying,
    last_name character varying,
    created_at timestamp(6) without time zone NOT NULL,
    updated_at timestamp(6) without time zone NOT NULL,
    CONSTRAINT name_cannot_start_with_d CHECK (((first_name)::text !~* &amp;#39;^d&amp;#39;::text)));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your constraint is within the schema itself!&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;schema.rb&lt;/code&gt; also supports database-level constraints, it is important to remember that it does not express everything your database may support such as triggers, sequences, stored procedures or check constraints. For example, this is what would happen to your schema file with the same exact migration (&lt;code&gt;AddFirstNameConstraintToUser&lt;/code&gt;) if you were just to use &lt;code&gt;schema.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ActiveRecord::Schema.define(version: 2019_12_14_074018) do

  # These are extensions that must be enabled in order to support this database
  enable_extension &amp;quot;plpgsql&amp;quot;

  create_table &amp;quot;users&amp;quot;, force: :cascade do |t|
    t.string &amp;quot;first_name&amp;quot;
    t.string &amp;quot;last_name&amp;quot;
    t.datetime &amp;quot;created_at&amp;quot;, precision: 6, null: false
    t.datetime &amp;quot;updated_at&amp;quot;, precision: 6, null: false
  end

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file has not changed! The constraint was not added.&lt;/p&gt;
&lt;p&gt;If you were to onboard a new developer to work on your project, you could potentially be operating under different database regulations.&lt;/p&gt;
&lt;p&gt;Committing &lt;code&gt;structure.sql&lt;/code&gt; to version control would help ensure that your team is on the same page. If you were to run &lt;code&gt;rails db:setup&lt;/code&gt; having a &lt;code&gt;structure.sql&lt;/code&gt; file, your database&amp;#39;s structure will contain the above constraint. With &lt;code&gt;schema.rb&lt;/code&gt; there is no such guarantee.&lt;/p&gt;
&lt;p&gt;The same can be said about a production system. If you needed to rapidly spin up a new instance of your application with a fresh database⁠—and running all migrations sequentially takes a long time⁠—setting up the database from the &lt;code&gt;structure.sql&lt;/code&gt; file would be a lot quicker. We can rest assured that the &lt;code&gt;structure.sql&lt;/code&gt; will create our database with the exact same structure as in other instances.&lt;/p&gt;
&lt;h2&gt;Growing Pains&lt;/h2&gt;
&lt;p&gt;Managing the concise &lt;code&gt;schema.rb&lt;/code&gt; file across a team is a far easier task than managing the verbose &lt;code&gt;structure.sql&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;One of the biggest growing pains when migrating to &lt;code&gt;structure.sql&lt;/code&gt; is ensuring that only the required changes get committed to that file, which can sometimes be difficult to do.&lt;/p&gt;
&lt;p&gt;Say, for instance, you pull someone&amp;#39;s branch and run the migrations specific to that branch. Your &lt;code&gt;structure.sql&lt;/code&gt; will now contain some changes. You then go back to working on your own branch and generate a new migration. Your &lt;code&gt;structure.sql&lt;/code&gt; file will now contain both your branch&amp;#39;s and the other branch&amp;#39;s changes. This can be a bit of a hassle to deal with, and there is undoubtedly a bit of a learning curve when it comes to managing these conflicts.&lt;/p&gt;
&lt;p&gt;By using this approach, we&amp;#39;re making a tradeoff. We have to deal with a bit of code complexity upfront that allows us to preserve our database&amp;#39;s advanced functionality. In turn, we also have to deal with a simpler schema representation as well as not having all the power of the database at our fingertips, e.g. if we want to set a backup from a &lt;code&gt;db:setup&lt;/code&gt; task. I posit that it&amp;#39;s best to put up with a bit of version-control hassle than to suffer through fixing corrupt/incorrect data in a production system, or to not be able to make use of all the advanced functionality that your database offers.&lt;/p&gt;
&lt;p&gt;Generally speaking, there are two strategies I&amp;#39;ve used to ensure my &lt;code&gt;structure.sql&lt;/code&gt; file only contains the necessary changes to a specific branch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Once you are done working on a branch that contains migrations, make sure you run &lt;code&gt;rails db:rollback STEP=n&lt;/code&gt; where &lt;code&gt;n&lt;/code&gt; is the number of migrations in that branch. This will ensure your database structure reverts to its original state.&lt;/li&gt;
&lt;li&gt;You might forget to rollback after working on a branch. In that case, when working on a new branch, make sure you pull a pristine &lt;code&gt;structure.sql&lt;/code&gt; file from master before creating any new migrations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a rule of thumb, your &lt;code&gt;structure.sql&lt;/code&gt; file should only contain the changes relevant to your branch before being merged into master.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Generally speaking, when Rails applications are small or don&amp;#39;t need some of the more advanced features that a database offers then it&amp;#39;s safe to use &lt;code&gt;schema.rb&lt;/code&gt;, which is very readable, concise and easy to manage.&lt;/p&gt;
&lt;p&gt;However, as an application grows in size and complexity, an accurate reflection of the database structure is of the essence. It will allow a team to maintain the right constraints, database modules, functions and operators that otherwise wouldn&amp;#39;t be possible. Learning to use Rails with a well-maintained &lt;code&gt;structure.sql&lt;/code&gt; file will offer an edge that the simpler &lt;code&gt;schema.rb&lt;/code&gt; simply cannot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Top 10 AppSignal Blog Posts in 2019</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/12/17/top-10-appsigna-blog-posts-in-2019.html"/>
    <id>https://blog.appsignal.com/2019/12/17/top-10-appsigna-blog-posts-in-2019.html</id>
    <published>2019-12-17T00:00:00+00:00</published>
    <updated>2019-12-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Check out the most appreciated posts on our blog in 2019.</summary>
    <content type="html">&lt;p&gt;Hi there stroopwafel fans 👋&lt;/p&gt;
&lt;p&gt;As we&amp;#39;re looking forward to publishing more Ruby, Elixir, and JavaScript posts in the coming 2020, we decided to take a look back at the blog posts that you gave the most hearts on Twitter, reads on the blog, and that we got the most appreciation for in 2019.&lt;/p&gt;
&lt;h2&gt;1. &lt;a href=&quot;/2019/10/17/javascript-errors-an-exceptional-history.html&quot;&gt;JavaScript Errors: An Exceptional History&lt;/a&gt; ✨&lt;/h2&gt;
&lt;p&gt;Join us as we go through the origins and turbulent years of growth of JavaScript and see it grow into the language it is today.&lt;/p&gt;
&lt;h2&gt;2. &lt;a href=&quot;/2019/06/18/elixir-alchemy-building-go-with-phoenix-live-view.html&quot;&gt;Building and Playing the Go Game with Phoenix LiveView&lt;/a&gt; ⚗️&lt;/h2&gt;
&lt;p&gt;In this Elixir series, we built and played the Go game. The first part is all about implementing Go in Phoenix LiveView, and in the second part - &lt;a href=&quot;/2019/07/04/elixir-alchemy-building-go-in-elixir-time-travel-and-the-ko-rule.html&quot;&gt;Time Travel and the Ko Rule&lt;/a&gt;, we&amp;#39;ve added the ability to undo and redo moves and implement Go&amp;#39;s ko rule.&lt;/p&gt;
&lt;h2&gt;3. &lt;a href=&quot;/2019/03/26/object-marshalling-in-ruby.html&quot;&gt;Object Marshalling in Ruby&lt;/a&gt; 💎&lt;/h2&gt;
&lt;p&gt;In this article, we took a deep dive into object marshalling. Find out what it is, look at the Marshall module, and then go through an example. Then, go a step deeper and compare the &lt;code&gt;_dump&lt;/code&gt; and &lt;code&gt;self._load&lt;/code&gt; methods.&lt;/p&gt;
&lt;h2&gt;4. &lt;a href=&quot;/2019/04/30/ruby-magic-hidden-gems-delegator-forwardable.html&quot;&gt;Ruby&amp;#39;s Hidden Gems: Delegator and Forwardable&lt;/a&gt; 💎&lt;/h2&gt;
&lt;p&gt;In this exploration of Ruby&amp;#39;s standard library, we&amp;#39;ve looked at delegation through Ruby&amp;#39;s Delegator and Forwardable classes.&lt;/p&gt;
&lt;h2&gt;5. &lt;a href=&quot;/2019/04/02/background-processing-system-in-ruby.html&quot;&gt;Learning by Building a Background Processing System in Ruby&lt;/a&gt; 💎&lt;/h2&gt;
&lt;p&gt;We implemented a naive background processing system for fun! Learn some things along the way as a peek into the internals of popular background processing systems like Sidekiq.&lt;/p&gt;
&lt;h2&gt;6. &lt;a href=&quot;/2019/01/22/serving-plug-building-an-elixir-http-server.html&quot;&gt;Serving Plug: Building an Elixir HTTP Server From Scratch&lt;/a&gt; ⚗️&lt;/h2&gt;
&lt;p&gt;As we&amp;#39;re on a continuous quest to find out what’s happening under the hood, we took a deep dive into HTTP servers in Elixir.&lt;/p&gt;
&lt;h2&gt;7. &lt;a href=&quot;/2019/04/16/elixir-alchemy-routing-phoenix-umbrella-apps.html&quot;&gt;Routing in Phoenix Umbrella Apps&lt;/a&gt; ⚗️&lt;/h2&gt;
&lt;p&gt;Umbrella apps are an awesome way to structure Elixir projects. Behind the curtains, they are a very thin layer that just compiles everything to a single package. Instead of building a single large monolith, you can structure your code with multiple isolated contexts...&lt;/p&gt;
&lt;h2&gt;8. &lt;a href=&quot;/2019/01/08/ruby-magic-bindings-and-lexical-scope.html&quot;&gt;Bindings and Lexical Scope in Ruby&lt;/a&gt; 💎&lt;/h2&gt;
&lt;p&gt;In this winter episode, we’ll went into bindings and scopes. So put on your skis and follow us deep into the woods.&lt;/p&gt;
&lt;h2&gt;9. &lt;a href=&quot;/2019/02/05/ruby-magic-classes-instances-and-metaclasses.html&quot;&gt;Unraveling Classes, Instances and Metaclasses in Ruby&lt;/a&gt; 💎&lt;/h2&gt;
&lt;p&gt;Through examining metaclasses, learn how class and instance methods work in Ruby. Along the way, discover the difference between defining a method by passing an explicit “definee” and using &lt;code&gt;class &amp;lt;&amp;lt; self&lt;/code&gt; or &lt;code&gt;instance_eval&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;10. &lt;a href=&quot;/2019/07/09/productive-procrastination-for-programmers.html&quot;&gt;Productive Procrastination for Programmers - works for Ruby and Elixir&lt;/a&gt; 💎⚗️✨&lt;/h2&gt;
&lt;p&gt;We pushed this post until the very end of this list in a true procrastination fashion. This is a partly-ironic, not-so-serious post on productivity, but increased productivity might actually result from reading it. It is all about the sweetness of procrastination, of pushing away that task you dread, the proverbial frog you need to eat. Take it with a grain of salt.&lt;/p&gt;
&lt;h2&gt;Holiday Season is Approaching 🎊🎉🎄❄️&lt;/h2&gt;
&lt;p&gt;That was all for this roundup of favorite articles of 2019! The whole AppSignal team wishes you all the best for the coming year, with little errors, many amazing insights and even more amazing articles ☃️&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. Don&amp;#39;t forget to subscribe to our &lt;a href=&quot;/ruby-magic&quot;&gt;Ruby Magic&lt;/a&gt;, &lt;a href=&quot;/elixir-alchemy&quot;&gt;Elixir Alchemy&lt;/a&gt;, and &lt;a href=&quot;/javascript-sorcery&quot;&gt;JavaScript Sorcery&lt;/a&gt; newsletters!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Configurable Ruby Modules: The Module Builder Pattern</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/11/29/configurable-ruby-modules-the-module-builder-pattern.html"/>
    <id>https://blog.appsignal.com/2019/11/29/configurable-ruby-modules-the-module-builder-pattern.html</id>
    <published>2019-11-29T00:00:00+00:00</published>
    <updated>2019-11-29T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Explore how to implement configurable modules in Ruby — a technique sometimes referred to as the Module Builder Pattern.</summary>
    <content type="html">&lt;p&gt;In this post, we&amp;#39;ll explore how to create Ruby modules that are configurable by users of our code — a pattern that allows gem authors to add more flexibility to their libraries.&lt;/p&gt;
&lt;p&gt;Most Ruby developers are familiar with using modules to share behavior. After all, this is one of their main use cases, according to the documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Modules serve two purposes in Ruby, namespacing and mixin functionality.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rails added some syntactic sugar in the form of &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/Concern.html&quot;&gt;&lt;code&gt;ActiveSupport::Concern&lt;/code&gt;&lt;/a&gt;, but the general principle remains the same.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Using modules to provide mixin functionality is usually straightforward. All we have to do is bundle up some methods and include our module elsewhere:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module HelloWorld
  def hello
    &amp;quot;Hello, world!&amp;quot;
  end
end
class Test
  include HelloWorld
end
Test.new.hello
#=&amp;gt; &amp;quot;Hello, world!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a pretty static mechanism, though Ruby&amp;#39;s &lt;code&gt;inherited&lt;/code&gt; and &lt;code&gt;extended&lt;/code&gt; hook methods allow for some varying behavior based on the including class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module HelloWorld
  def self.included(base)
    define_method :hello do
      &amp;quot;Hello, world from #{base}!&amp;quot;
    end
  end
end
class Test
  include HelloWorld
end
Test.new.hello
#=&amp;gt; &amp;quot;Hello, world from Test!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is somewhat more dynamic but still doesn&amp;#39;t allow our code users to, for instance, rename the &lt;code&gt;hello&lt;/code&gt; method at module inclusion time.&lt;/p&gt;
&lt;h2&gt;The Solution: Configurable Ruby Modules&lt;/h2&gt;
&lt;p&gt;Over the past few years, a new pattern has emerged that solves this problem, which people sometimes refer to as the &amp;quot;module builder pattern&amp;quot;. This technique relies on two primary features of Ruby:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Modules are just like any other objects—they can be created on the fly, assigned to variables, dynamically modified, as well as passed to or returned from methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def make_module
  # create a module on the fly and assign it to variable
  mod = Module.new

  # modify module
  mod.module_eval do
    def hello
      &amp;quot;Hello, AppSignal world!&amp;quot;
    end
  end

  # explicitly return it
  mod
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The argument to &lt;code&gt;include&lt;/code&gt; or &lt;code&gt;extend&lt;/code&gt; calls doesn&amp;#39;t have to be a module, it can also be an expression returning one, e.g. a method call.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Test
  # include the module returned by make_module
  include make_module
end

Test.new.hello
#=&amp;gt; &amp;quot;Hello, AppSignal world!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Module Builder in Action&lt;/h2&gt;
&lt;p&gt;We will now use this knowledge to build a simple module called &lt;code&gt;Wrapper&lt;/code&gt;, which implements the following behavior:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A class including &lt;code&gt;Wrapper&lt;/code&gt; can only wrap objects of a specific type. The constructor will verify the argument type and raise an error if the type doesn&amp;#39;t match what&amp;#39;s expected.&lt;/li&gt;
&lt;li&gt;The wrapped object will be available through an instance method called &lt;code&gt;original_&amp;lt;class&amp;gt;&lt;/code&gt;, e.g. &lt;code&gt;original_integer&lt;/code&gt; or &lt;code&gt;original_string&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It will allow consumers of our code to specify an alternative name for this accessor method, for example, &lt;code&gt;the_string&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;#39;s take a look at how we want our code to behave:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# 1
class IntWrapper
 # 2
 include Wrapper.for(Integer)
end

# 3
i = IntWrapper.new(42)
i.original_integer
#=&amp;gt; 42

# 4
i = IntWrapper.new(&amp;quot;42&amp;quot;)
#=&amp;gt; TypeError (not a Integer)

# 5
class StringWrapper
 include Wrapper.for(String, accessor_name: :the_string)
end

s = StringWrapper.new(&amp;quot;Hello, World!&amp;quot;)
# 6
s.the_string
#=&amp;gt; &amp;quot;Hello, World!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In step 1, we define a new class called &lt;code&gt;IntWrapper&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In step 2, we ensure that this class doesn&amp;#39;t simply include a module by name but instead, mixes in the result of a call to &lt;code&gt;Wrapper.for(Integer)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In step 3, we instantiate an object of our new class and assign it to &lt;code&gt;i&lt;/code&gt;. As specified, this object has a method called &lt;code&gt;original_integer&lt;/code&gt;, that satisfies one of our requirements.&lt;/p&gt;
&lt;p&gt;In step 4, if we try to pass in an argument of the wrong type, like a string, a helpful &lt;code&gt;TypeError&lt;/code&gt; will be raised. Finally, let&amp;#39;s verify that users are able to specify custom accessor names.&lt;/p&gt;
&lt;p&gt;For this, we define a new class called &lt;code&gt;StringWrapper&lt;/code&gt; in step 5 and pass &lt;code&gt;the_string&lt;/code&gt; as the keyword argument &lt;code&gt;accessor_name&lt;/code&gt;, which we see in action in step 6.&lt;/p&gt;
&lt;p&gt;While this is admittedly a somewhat contrived example, it has just enough varying behavior to show off the module builder pattern and how it is used.&lt;/p&gt;
&lt;h3&gt;First Attempt&lt;/h3&gt;
&lt;p&gt;Based on the requirements and usage example, we can now begin the implementation. We already know that we need a module named &lt;code&gt;Wrapper&lt;/code&gt; with a module-level method called &lt;code&gt;for&lt;/code&gt;, which takes a class as an optional keyword argument:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Wrapper
 def self.for(klass, accessor_name: nil)
 end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the return value of this method becomes the argument to &lt;code&gt;include&lt;/code&gt;, it needs to be a module. Thus, we can create a new anonymous one with &lt;code&gt;Module.new&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Module.new do
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As per our requirements, this needs to define a constructor which verifies the type of the passed-in object, as well as an appropriately named accessor method. Let&amp;#39;s start with the constructor:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;define_method :initialize do |object|
 raise TypeError, &amp;quot;not a #{klass}&amp;quot; unless object.is_a?(klass)
 @object = object
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This piece of code uses &lt;a href=&quot;https://ruby-doc.org/core-2.6.5/Module.html#method-i-define_method&quot;&gt;&lt;code&gt;define_method&lt;/code&gt;&lt;/a&gt; to dynamically add an instance method to the receiver. Since the block acts as a closure, it can use the &lt;code&gt;klass&lt;/code&gt; object from the outer scope to perform the required type check.&lt;/p&gt;
&lt;p&gt;Adding an appropriately named accessor method is not that much harder:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# 1
method_name = accessor_name || begin
 klass_name = klass.to_s.gsub(/(.)([A-Z])/,&amp;#39;\1_\2&amp;#39;).downcase
 &amp;quot;original_#{klass_name}&amp;quot;
end

# 2
define_method(method_name) { @object }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we need to see if the caller of our code passed in an &lt;code&gt;accessor_name&lt;/code&gt;. If so, we just assign it to &lt;code&gt;method_name&lt;/code&gt; and are then done. Otherwise, we take the class and convert it to an underscored string, for instance, &lt;code&gt;Integer&lt;/code&gt; turns into &lt;code&gt;integer&lt;/code&gt; or &lt;code&gt;OpenStruct&lt;/code&gt; into &lt;code&gt;open_struct&lt;/code&gt;. This &lt;code&gt;klass_name&lt;/code&gt; variable is then prefixed with &lt;code&gt;original_&lt;/code&gt; to generate the final accessor name. Once we know the method’s name, we again use &lt;code&gt;define_method&lt;/code&gt; to add it to our module, as shown in step 2.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the complete code up to this point. Less than 20 lines for a flexible and configurable Ruby module; not too bad.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Wrapper
  def self.for(klass, accessor_name: nil)
    Module.new do
      define_method :initialize do |object|
        raise TypeError, &amp;quot;not a #{klass}&amp;quot; unless object.is_a?(klass)
        @object = object
      end

      method_name = accessor_name || begin
        klass_name = klass.to_s.gsub(/(.)([A-Z])/,&amp;#39;\1_\2&amp;#39;).downcase
        &amp;quot;original_#{klass_name}&amp;quot;
      end

      define_method(method_name) { @object }
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Observant readers might remember that &lt;code&gt;Wrapper.for&lt;/code&gt; returns an anonymous module. This isn&amp;#39;t a problem, but can get a bit confusing when examining an object&amp;#39;s inheritance chain:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;StringWrapper.ancestors
#=&amp;gt; [StringWrapper, #&amp;lt;Module:0x0000000107283680&amp;gt;, Object, Kernel, BasicObject]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;#&amp;lt;Module:0x0000000107283680&amp;gt;&lt;/code&gt; (the name will vary if you are following along) refers to our anonymous module.&lt;/p&gt;
&lt;h3&gt;Improved Version&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s make life easier for our users by returning a named module instead of an anonymous one. The code for this is very similar to what we had before, with some minor changes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Wrapper
  def self.for(klass, accessor_name: nil)
    # 1
    mod = const_set(&amp;quot;#{klass}InstanceMethods&amp;quot;, Module.new)

    # 2
    mod.module_eval do
      define_method :initialize do |object|
        raise TypeError, &amp;quot;not a #{klass}&amp;quot; unless object.is_a?(klass)
        @object = object
      end

      method_name = accessor_name || begin
        klass_name = klass.to_s.gsub(/(.)([A-Z])/, &amp;#39;\1_\2&amp;#39;).downcase
        &amp;quot;original_#{klass_name}&amp;quot;
      end

      define_method(method_name) { @object }
    end

    # 3
    mod
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the first step, we create a nested module called &lt;code&gt;&amp;quot;#{klass}InstanceMethods&amp;quot;&lt;/code&gt; (for example &lt;code&gt;IntegerInstanceMethods&lt;/code&gt;), that is just an “empty” module.&lt;/p&gt;
&lt;p&gt;As shown in step 2, we use &lt;a href=&quot;https://ruby-doc.org/core-2.6.5/Module.html#method-i-module_eval&quot;&gt;&lt;code&gt;module_eval&lt;/code&gt;&lt;/a&gt; in the &lt;code&gt;for&lt;/code&gt; method, which evaluates a block of code in the context of the module it’s called on. This way, we can add behavior to the module before returning it in step 3.&lt;/p&gt;
&lt;p&gt;If we now examine the ancestors of a class including &lt;code&gt;Wrapper&lt;/code&gt;, the output will include a properly named module, which is much more meaningful and easier to debug than the previous anonymous module.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;StringWrapper.ancestors
#=&amp;gt; [StringWrapper, Wrapper::StringInstanceMethods, Object, Kernel, BasicObject]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Module Builder Pattern in the Wild&lt;/h2&gt;
&lt;p&gt;Apart from this post, where else can we find the module builder pattern or similar techniques?&lt;/p&gt;
&lt;p&gt;One example is the &lt;a href=&quot;https://dry-rb.org/&quot;&gt;&lt;code&gt;dry-rb&lt;/code&gt;&lt;/a&gt; family of gems, where, for example, &lt;a href=&quot;https://dry-rb.org/gems/dry-effects&quot;&gt;&lt;code&gt;dry-effects&lt;/code&gt;&lt;/a&gt; uses module builders to pass configuration options to the various effect handlers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# This adds a `counter` effect provider. It will handle (eliminate) effects
include Dry::Effects::Handler.State(:counter)

# Providing scope is required
# All cache values will be scoped with this key
include Dry::Effects::Handler.Cache(:blog)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can find similar usage in the excellent &lt;a href=&quot;https://shrinerb.com/&quot;&gt;Shrine&lt;/a&gt; gem, which provides a file upload toolkit for Ruby applications:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Photo &amp;lt; Sequel::Model
  include Shrine::Attachment(:image)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern is still relatively new, but I expect we&amp;#39;ll see more of it in the future, especially in gems that focus more on pure Ruby applications than Rails ones.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this post, we explored how to implement configurable modules in Ruby, a technique sometimes referred to as the module builder pattern. Like other metaprogramming techniques, this comes at the cost of increased complexity and therefore shouldn&amp;#39;t be used without good reason. However, in the rare cases where such flexibility is needed, Ruby&amp;#39;s object model once again allows for an elegant and concise solution. The module builder pattern isn&amp;#39;t something most Ruby developers will need often, but it&amp;#39;s a great tool to have in one&amp;#39;s toolkit, especially for library authors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S. If you&amp;#39;d like to read Ruby Magic posts as soon as they get off the press, &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby Templating: Baking an Interpreter</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/09/24/ruby-magic-baking-an-interpreter.html"/>
    <id>https://blog.appsignal.com/2019/09/24/ruby-magic-baking-an-interpreter.html</id>
    <published>2019-09-24T00:00:00+00:00</published>
    <updated>2019-09-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this post, we&#039;ll bake our own Interpreter, bringing the Templating series together with a lot of sticky stroop.</summary>
    <content type="html">&lt;p&gt;We hope you’ve got your stroopwafels warmed on top of your coffee because today we’re gluing things up with sticky stroop (the syrup that makes the two halves of a stroopwafel stick together). In the first two parts of our series, we baked a &lt;a href=&quot;/2019/07/02/ruby-magic-brewing-our-own-template-lexer-in-ruby.html&quot;&gt;Lexer&lt;/a&gt; and a &lt;a href=&quot;/2019/07/30/ruby-magic-ruby-templating-the-parser.html&quot;&gt;Parser&lt;/a&gt; and now, we’re adding the Interpreter and gluing things together by pouring stroop over them.&lt;/p&gt;
&lt;h2&gt;Ingredients&lt;/h2&gt;
&lt;p&gt;Alright! Let’s get the kitchen ready for baking and put our ingredients on the table. Our interpreter needs two ingredients or pieces of information to do its job: the previously generated Abstract Syntax Tree (AST) and the data we want to embed into the template. We’ll call this data the &lt;code&gt;environment&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To traverse the AST, we’ll implement the interpreter using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Visitor_pattern&quot;&gt;visitor pattern&lt;/a&gt;. A visitor (and therefore our interpreter) implements a generic visit method that accepts a node as a parameter, processes this node and potentially calls the &lt;code&gt;visit&lt;/code&gt; method again with some (or all) of the node’s children, depending on what makes sense for the current node.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  class Interpreter
    attr_reader :root, :environment

    def self.render(root, environment = {})
      new(root, environment).render
    end

    def initialize(root, environment = {})
      @root = root
      @environment = environment
    end

    def render
      visit(root)
    end

    def visit(node)
      # Process node
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before continuing, let’s also create a small &lt;code&gt;Magicbars.render&lt;/code&gt; method that accepts a template and an environment and outputs the rendered template.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  def self.render(template, environment = {})
    tokens = Lexer.tokenize(template)
    ast = Parser.parse(tokens)
    Interpreter.render(ast, environment)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, we’ll be able to test the interpreter without having to construct the AST by hand.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars.render(&amp;#39;Welcome to {{name}}&amp;#39;, name: &amp;#39;Ruby Magic&amp;#39;)
# =&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To no surprise, it currently doesn’t return anything. So let’s start implementing the &lt;code&gt;visit&lt;/code&gt; method. As a quick reminder, here’s what the AST for this template looks like.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2019-09/simple-ast.svg&quot; alt=&quot;Simple AST&quot;/&gt;&lt;/p&gt;
&lt;p&gt;For this template, we’ll have to process four different node types: &lt;code&gt;Template&lt;/code&gt;, &lt;code&gt;Content&lt;/code&gt;, &lt;code&gt;Expression&lt;/code&gt;, and &lt;code&gt;Identifier&lt;/code&gt;. To do this, we could just put a huge &lt;code&gt;case&lt;/code&gt; statement inside our &lt;code&gt;visit&lt;/code&gt; method. However, this will become unreadable pretty quickly. Instead, let’s make use of Ruby’s metaprogramming capabilities to keep our code a bit more organized and readable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  class Interpreter
    # ...

    def visit(node)
      short_name = node.class.to_s.split(&amp;#39;::&amp;#39;).last
      send(&amp;quot;visit_#{short_name}&amp;quot;, node)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method accepts a node, gets its class name, and removes any modules from it (check out our article on &lt;a href=&quot;/2019/08/20/clean-up-strings.html&quot;&gt;cleaning strings&lt;/a&gt; if you’re interested in different ways of doing this). Afterward, we use &lt;code&gt;send&lt;/code&gt; to call a method that handles this specific type of node. The method name for each type is made up of the demodulized class name and the &lt;code&gt;visit_&lt;/code&gt; prefix. It’s a bit unusual to have capital letters in method names, but it makes the method’s intent pretty clear.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  class Interpreter
    # ...

    def visit_Template(node)
      # Process template nodes
    end

    def visit_Content(node)
      # Process content nodes
    end

    def visit_Expression(node)
      # Process expression nodes
    end

    def visit_Identifier(node)
      # Process identifier nodes
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Let’s start by implementing the &lt;code&gt;visit_Template&lt;/code&gt; method. It should just process all the &lt;code&gt;statements&lt;/code&gt; of the node and join the results.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_Template(node)
  node.statements.map { |statement| visit(statement) }.join
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let’s look at the &lt;code&gt;visit_Content&lt;/code&gt; method. As a content node just wraps a string, the method is as simple as it gets.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_Content(node)
  node.content
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let’s move on to the &lt;code&gt;visit_Expression&lt;/code&gt; method where the substitution of the placeholder with the real value happens.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_Expression(node)
  key = visit(node.identifier)
  environment.fetch(key, &amp;#39;&amp;#39;)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, for the &lt;code&gt;visit_Expression&lt;/code&gt; method to know what key to fetch from the environment, let’s implement the &lt;code&gt;visit_Identifier&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_Identifier(node)
  node.value
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these four methods in place, we get the desired result when we try to render the template again.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars.render(&amp;#39;Welcome to {{name}}&amp;#39;, name: &amp;#39;Ruby Magic&amp;#39;)
# =&amp;gt; Welcome to Ruby Magic
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Interpreting Block Expressions&lt;/h2&gt;
&lt;p&gt;We wrote a lot of code to implement what a simple &lt;code&gt;gsub&lt;/code&gt; could do. So let’s move on to a more complex example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Welcome to {{name}}!

{{#if subscribed}}
  Thank you for subscribing to our mailing list.
{{else}}
  Please sign up for our mailing list to be notified about new articles!
{{/if}}

Your friends at {{company_name}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a reminder, here’s what the corresponding AST looks like.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2019-09/complex-ast.svg&quot; alt=&quot;Complex AST&quot;/&gt;&lt;/p&gt;
&lt;p&gt;There’s only one node type that we don’t yet handle. It’s the &lt;code&gt;visit_BlockExpression&lt;/code&gt; node. In a way, it is similar to the &lt;code&gt;visit_Expression&lt;/code&gt; node, but depending on the value it either continues to process the &lt;code&gt;statements&lt;/code&gt; or the &lt;code&gt;inverse_statements&lt;/code&gt; of the &lt;code&gt;BlockExpression&lt;/code&gt; node.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_BlockExpression(node)
  key = visit(node.identifier)

  if environment[key]
    node.statements.map { |statement| visit(statement) }.join
  else
    node.inverse_statements.map { |statement| visit(statement) }.join
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking at the method, we notice that the two branches are very similar, and they also look similar to the &lt;code&gt;visit_Template&lt;/code&gt; method. They all handle the visiting of all nodes of an &lt;code&gt;Array&lt;/code&gt;, so let’s extract a &lt;code&gt;visit_Array&lt;/code&gt; method to clean things up a bit.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_Array(nodes)
  nodes.map { |node| visit(node) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the new method in place, we can remove some code from the &lt;code&gt;visit_Template&lt;/code&gt; and &lt;code&gt;visit_BlockExpression&lt;/code&gt; methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_Template(node)
  visit(node.statements).join
end

def visit_BlockExpression(node)
  key = visit(node.identifier)

  if environment[key]
    visit(node.statements).join
  else
    visit(node.inverse_statements).join
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that our interpreter handles all node types, let’s try and render the complex template.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars.render(template, { name: &amp;#39;Ruby Magic&amp;#39;, subscribed: true, company_name: &amp;#39;AppSignal&amp;#39; })
# =&amp;gt; Welcome to Ruby Magic!
#
#
#  Please sign up for our mailing list to be notified about new articles!
#
#
# Your friends at AppSignal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;em&gt;almost&lt;/em&gt; looks right. But on a closer look, we notice that the message is prompting us to sign up for the mailing list, even though we provided &lt;code&gt;subscribed: true&lt;/code&gt; in the environment. That doesn’t seem right…&lt;/p&gt;
&lt;h2&gt;Adding Support for Helper Methods&lt;/h2&gt;
&lt;p&gt;Looking back at the template, we notice that there’s an &lt;code&gt;if&lt;/code&gt; in the block expression. Instead of looking up the value of &lt;code&gt;subscribed&lt;/code&gt; in the environment, the &lt;code&gt;visit_BlockExpression&lt;/code&gt; is looking up the value of &lt;code&gt;if&lt;/code&gt;. As it is not present in the environment, the call returns &lt;code&gt;nil&lt;/code&gt;, which is false.&lt;/p&gt;
&lt;p&gt;We could stop here and declare that we’re not trying to imitate &lt;a href=&quot;https://handlebarsjs.com&quot;&gt;Handlebars&lt;/a&gt; but &lt;a href=&quot;https://mustache.github.io&quot;&gt;Mustache&lt;/a&gt;, and get rid of the &lt;code&gt;if&lt;/code&gt; in the template, which will give us the desired result.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Welcome to {{name}}!

{{#subscribed}}
  Thank you for subscribing to our mailing list.
{{else}}
  Please sign up for our mailing list to be notified about new articles!
{{/subscribed}}

Your friends at {{company_name}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But why stop when we are having fun? Let’s go the extra mile and implement helper methods. They might come in handy for other things as well.&lt;/p&gt;
&lt;p&gt;Let’s start by adding a helper method support to simple expressions. We’ll add a &lt;code&gt;reverse&lt;/code&gt; helper, that reverses strings passed to it. In addition, we’ll add a &lt;code&gt;debug&lt;/code&gt; method that tells us the class name of a given value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def helpers
  @helpers ||= {
    reverse: -&amp;gt;(value) { value.to_s.reverse },
    debug: -&amp;gt;(value) { value.class }
  }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use simple lambdas to implement these helpers and store them in a hash so that we can look them up by their name.&lt;/p&gt;
&lt;p&gt;Next, let’s modify &lt;code&gt;visit_Expression&lt;/code&gt; to perform a helper lookup before trying a value lookup in the environment.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_Expression(node)
  key = visit(node.identifier)

  if helper = helpers[key]
    arguments = visit(node.arguments).map { |k| environment[k] }

    return helper.call(*arguments)
  end

  environment[key]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there is a helper matching the given identifier, the method will visit all the arguments and try to lookup values for them. Afterward, it will call the method and pass all the values as arguments.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars.render(&amp;#39;Welcome to {{reverse name}}&amp;#39;, name: &amp;#39;Ruby Magic&amp;#39;)
# =&amp;gt; Welcome to cigaM ybuR

Magicbars.render(&amp;#39;Welcome to {{debug name}}&amp;#39;, name: &amp;#39;Ruby Magic&amp;#39;)
# =&amp;gt; Welcome to String
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that in place, let’s finally implement an &lt;code&gt;if&lt;/code&gt; and an &lt;code&gt;unless&lt;/code&gt; helper. In addition to the arguments, we’ll pass two lambdas to them so that they can decide if we should continue interpreting the node’s &lt;code&gt;statements&lt;/code&gt; or &lt;code&gt;inverse_statements&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def helpers
  @helpers ||= {
    if: -&amp;gt;(value, block:, inverse_block:) { value ? block.call : inverse_block.call },
    unless: -&amp;gt;(value, block:, inverse_block:) { value ? inverse_block.call : block.call },
    # ...
  }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The necessary changes to &lt;code&gt;visit_BlockExpression&lt;/code&gt; are similar to what we did with &lt;code&gt;visit_Expression&lt;/code&gt;, only this time, we also pass the two lambdas.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def visit_BlockExpression(node)
  key = visit(node.identifier)

  if helper = helpers[key]
    arguments = visit(node.arguments).map { |k| environment[k] }

    return helper.call(
      *arguments,
      block: -&amp;gt; { visit(node.statements).join },
      inverse_block: -&amp;gt; { visit(node.inverse_statements).join }
    )
  end

  if environment[key]
    visit(node.statements).join
  else
    visit(node.inverse_statements).join
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And with this, our baking is done! We can render the complex template that started this journey into the world of lexers, parsers, and interpreters.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars.render(template, { name: &amp;#39;Ruby Magic&amp;#39;, subscribed: true, company_name: &amp;#39;AppSignal&amp;#39; })
# =&amp;gt; Welcome to Ruby Magic!
#
#
#  Thank you for subscribing to our mailing list.
#
#
# Your friends at AppSignal
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Only Scratching the Surface&lt;/h2&gt;
&lt;p&gt;In this three-part series, we covered the basics of creating a templating language. These concepts can also be used to create interpreted programming languages (like Ruby). Admittedly, we glossed over a couple of things (like proper error handling 🙀) and only scratched the surface of the underpinnings of today’s programming languages.&lt;/p&gt;
&lt;p&gt;We hope you enjoyed the series and if you want more of that, &lt;a href=&quot;/ruby-magic&quot;&gt;subsribe to the Ruby Magic list&lt;/a&gt;. If you are now hungry for stroopwafels, &lt;a href=&quot;mailto:support@appsignal.com?subject=I%20would%20like%20some%20stroopwafels&amp;body=Hi%20there,%20AppSignal%20rocks!%20Can%20you%20send%20some%20stroopwafels%20to...&quot;&gt;drop us a line&lt;/a&gt; and we might be able to fuel you with those as well!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Type Checking in Ruby — Check Yo Self Before You Wreck Yo Self</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/08/27/ruby-magic-type-checking-in-ruby.html"/>
    <id>https://blog.appsignal.com/2019/08/27/ruby-magic-type-checking-in-ruby.html</id>
    <published>2019-08-27T00:00:00+00:00</published>
    <updated>2019-08-27T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">A splash into Duck Typing, gradual typing and enjoying Sorbet on a typical summer day</summary>
    <content type="html">&lt;p&gt;Let&amp;#39;s start this post with a fun little guessing game: what do you think is the most common error tracked by AppSignal in Ruby applications?&lt;/p&gt;
&lt;p&gt;It&amp;#39;s fair to assume that many of you answered this question with &lt;code&gt;NoMethodError&lt;/code&gt;, an exception that is caused by calling a non-existent method on an object. Occasionally, this may be caused by a typo in the method name, but more often it&amp;#39;s the result of calling a method on an object of the wrong type, which often happens to be an unexpected &lt;code&gt;nil&lt;/code&gt;. Is there something we can do as Ruby developers to reduce the frequency of such errors?&lt;/p&gt;
&lt;h2&gt;Types to the Rescue?&lt;/h2&gt;
&lt;p&gt;Except for the choice of text editor or programming language, few topics can spiral into heated debates faster than discussions of type systems. We won&amp;#39;t have time to go into details here, but Chris Smith&amp;#39;s post &lt;a href=&quot;https://blog.steveklabnik.com/posts/2010-07-17-what-to-know-before-debating-type-systems&quot;&gt;&amp;quot;What To Know Before Debating Type Systems&amp;quot;&lt;/a&gt; does an excellent job at that.&lt;/p&gt;
&lt;p&gt;In the broadest terms, type systems can be divided into two main categories—static and dynamic. While the former happens ahead of time (either via the compiler or a separate tool), dynamic type checking occurs during runtime, where it may lead to exceptions if the actual types don&amp;#39;t align with the developer&amp;#39;s expectations.&lt;/p&gt;
&lt;p&gt;Proponents of both philosophies have strong opinions, but alas, there are also many misconceptions floating around: static typing does not require copious type annotations—many modern compilers can figure out the types on their own, a process known as &amp;quot;type inference&amp;quot;. On the other hand, dynamically typed languages don&amp;#39;t seem to exhibit significantly higher defect rates than their statically typed counterparts.&lt;/p&gt;
&lt;h3&gt;Duck Typing&lt;/h3&gt;
&lt;p&gt;Ruby itself is a dynamically type-checked language and follows a &amp;quot;duck typing&amp;quot; approach:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If it walks like a duck and it quacks like a duck, then it must be a duck.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What this means is that Ruby developers generally don&amp;#39;t worry too much about an object&amp;#39;s type, but whether it responds to certain &amp;quot;messages&amp;quot; (or methods).&lt;/p&gt;
&lt;p&gt;So why bother with static typing in Ruby then, you may ask? While it certainly is no panacea that will make your code magically bug free, it does provide certain benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Correctness: static typing is good at preventing &lt;strong&gt;certain classes&lt;/strong&gt; of bugs, like the aforementioned &lt;code&gt;NoMethodError&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tooling: oftentimes, having static type information available during development leads to better tooling options (e.g. refactoring support in IDEs, etc.)&lt;/li&gt;
&lt;li&gt;Documentation: many statically typed languages have great built-in documentation tools. Haskell&amp;#39;s &lt;a href=&quot;https://hoogle.haskell.org/&quot;&gt;Hoogle&lt;/a&gt; uses this to great effect by offering a search engine where functions can be looked up by their type signatures.&lt;/li&gt;
&lt;li&gt;Performance: the more the information available to the compiler, the more the performance optimizations that can potentially be applied.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This list is not exhaustive, and one can find counterexamples for most of these points, but there&amp;#39;s certainly a core of truth to them.&lt;/p&gt;
&lt;h3&gt;Gradual Type Checking&lt;/h3&gt;
&lt;p&gt;In recent years an approach commonly referred to as &amp;quot;gradual type checking&amp;quot; has made inroads into various dynamically type-checked languages: from &lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt; for JS to &lt;a href=&quot;https://hacklang.org/&quot;&gt;Hack&lt;/a&gt; for PHP and &lt;a href=&quot;http://mypy-lang.org/&quot;&gt;mypy&lt;/a&gt; for Python. What these approaches have in common is that they don&amp;#39;t require an all-or-nothing approach, but instead, allow developers to gradually add type information to variables and expressions as they see fit. This is especially useful for existing large codebases, where one can statically check the most critical parts of the system while still leaving the rest untyped and checked at runtime. All the type checking solutions for Ruby that we&amp;#39;ll explore in the rest of this article follow the same approach.&lt;/p&gt;
&lt;h2&gt;Options&lt;/h2&gt;
&lt;p&gt;After looking at why Ruby developers may want to add static type checking to their development workflows, it&amp;#39;s time to explore some of the currently popular options for doing so. However, it&amp;#39;s important to note that the idea of adding static type checking to Ruby isn&amp;#39;t new. Researchers from the University of Maryland worked on a Ruby extension named Diamondback Ruby (Druby) as early as 2009 and the Tufts University Programming Language Group released a paper called &lt;a href=&quot;https://www.cs.tufts.edu/~jfoster/papers/oops13.pdf&quot;&gt;The Ruby Type Checker&lt;/a&gt; in 2013, which eventually led to the &lt;a href=&quot;https://github.com/tupl-tufts/rdl&quot;&gt;RDL project&lt;/a&gt;, which offers type checking and design-by-contract capabilities as a library.&lt;/p&gt;
&lt;h3&gt;Sorbet&lt;/h3&gt;
&lt;p&gt;Developed by Stripe, &lt;a href=&quot;https://sorbet.org/&quot;&gt;Sorbet&lt;/a&gt; is currently the most talked-about type checking solution for Ruby, not least because big companies like Shopify, GitLab, Kickstarter and Coinbase were early adopters during its closed beta phase. It was originally &lt;a href=&quot;https://twitter.com/darkdimius/status/1002049138366730240&quot;&gt;announced&lt;/a&gt; during last year&amp;#39;s Ruby Kaigi and saw its first public release on June 20th of this year. Sorbet is written in modern C++ and despite &lt;a href=&quot;https://twitter.com/amrabdelwahab/status/1141981078984548353&quot;&gt;Matz&amp;#39;s preferences&lt;/a&gt; (quote: &amp;quot;I hate type annotations&amp;quot;), opted for an approach based on type annotations. One particularly interesting thing about Sorbet is that it opts for a combination of static and dynamic type checking since Ruby&amp;#39;s extremely dynamic nature and metaprogramming capabilities are challenging for static type systems.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# typed: true
class Test
  extend T::Sig

  sig {params(x: Integer).returns(String)}
  def to_s(x)
    x.to_s
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To enable type checking, we first need to add the &lt;code&gt;# typed: true&lt;/code&gt; magic comment and extend our class with the &lt;code&gt;T::Sig&lt;/code&gt; module. The actual type annotation is specified with the &lt;code&gt;sig&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;sig {params(x: Integer).returns(String)}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which specifies that this method takes a single argument named &lt;code&gt;x&lt;/code&gt; that is of type &lt;code&gt;Integer&lt;/code&gt; and returns a &lt;code&gt;String&lt;/code&gt;. Trying to call this method with the wrong argument type will lead to an error:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Test.new.to_s(&amp;quot;42&amp;quot;)
# Expected Integer but found String(&amp;quot;42&amp;quot;) for argument x
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Apart from these basic checks, Sorbet has quite a few more tricks up its sleeve. For example, it can save us from the dreaded &lt;code&gt;NoMethodError&lt;/code&gt; on &lt;code&gt;nil&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;users = T::Array[User].new
user = users.first
user.username

# Method username does not exist on NilClass component of T.nilable(User)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The snippet above defines an empty array of &lt;code&gt;User&lt;/code&gt; objects and when we try to access the first element (which will return &lt;code&gt;nil&lt;/code&gt;) Sorbet correctly warns us that no method named &lt;code&gt;username&lt;/code&gt; is available on &lt;code&gt;NilClass&lt;/code&gt;. However, if we are sure that a certain value can never be &lt;code&gt;nil&lt;/code&gt;, we can use &lt;code&gt;T.must&lt;/code&gt; to let Sorbet know this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;users = T::Array[User].new
user = T.must(users.first)
user.username
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the above code will now type check, it could lead to a runtime exception, so use this feature with care.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s a lot more that Sorbet can do for us: &lt;a href=&quot;https://sorbet.run/#%23%20typed%3A%20true%0Aif%20Random.rand%0A%20%20%20%20foo%20%3D%201%0Aelse%0A%20%20%20%20foo%20%3D%202%0Aend&quot;&gt;dead code detection&lt;/a&gt;, &lt;a href=&quot;https://sorbet.run/#%23%20typed%3A%20true%0Aa%20%3D%205%0Aa%20%3D%20%3Asym%0A%0Ab%20%3D%20T.let(&#039;str&#039;%2C%20String)%20%23%20Pinned%20to%20a%20String%0Ab%20%3D%20%3Asym&quot;&gt;type pinning&lt;/a&gt; (essentially committing a variable to a certain type, for example, once it has been assigned a string, it can never be assigned an integer), or the ability to define &lt;a href=&quot;https://sorbet.run/#%23%20typed%3A%20true%0Amodule%20Fooable%0A%20%20extend%20T%3A%3ASig%0A%20%20extend%20T%3A%3AHelpers%0A%20%20abstract!%0A%20%20sig%20%7Babstract.void%7D%0A%20%20def%20foo%0A%20%20end%0Aend%0A%0Aclass%20GoodFooable%0A%20%20include%20Fooable%0A%0A%20%20def%20foo%0A%20%20end%0Aend%0A%0Aclass%20BadFooable%0A%20%20include%20Fooable%0Aend&quot;&gt;interfaces&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, Sorbet can also work with &amp;quot;Ruby Interface&amp;quot; files (&lt;code&gt;rbi&lt;/code&gt;) which it keeps in a &lt;code&gt;sorbet/&lt;/code&gt; folder in your current working directory. This allows us to generate interface definitions for all the gems a project uses, which can help us with finding even more type errors.&lt;/p&gt;
&lt;p&gt;There&amp;#39;s much more to Sorbet than we can cover in a single article (e.g. the varying strictness levels or metaprogramming plugins), but its &lt;a href=&quot;https://sorbet.org/docs/overview&quot;&gt;documentation&lt;/a&gt; is pretty good already and open for PRs.&lt;/p&gt;
&lt;h3&gt;Steep&lt;/h3&gt;
&lt;p&gt;The most widely known alternative to Sorbet is &lt;a href=&quot;https://github.com/soutaro/steep&quot;&gt;Steep&lt;/a&gt; by Soutaro Matsumoto. It does not use annotations and doesn&amp;#39;t do any type inference on its own. Instead, it completely relies on &lt;code&gt;.rbi&lt;/code&gt; files in the &lt;code&gt;sig&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start from the following simple Ruby class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  attr_reader :first_name, :last_name, :address

  def initialize(first_name, last_name, address)
    @first_name = first_name
    @last_name = last_name
    @address = address
  end

  def full_name
    &amp;quot;#{first_name} #{last_name}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now scaffold an initial &lt;code&gt;user.rbi&lt;/code&gt; file with the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ steep scaffold user.rb &amp;gt; sig/user.rbi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This results in the following file which is intended as a starting point (illustrated by the fact that all types have been specified as &lt;code&gt;any&lt;/code&gt;, which provides no safety):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class User
  @first_name: any
  @last_name: any
  @address: any
  def initialize: (any, any, any) -&amp;gt; any
  def full_name: () -&amp;gt; String
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, if we try to type check at this point, we&amp;#39;ll encounter some errors:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ steep check
user.rb:11:7: NoMethodError: type=::User, method=first_name (first_name)
user.rb:11:21: NoMethodError: type=::User, method=last_name (last_name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reason we&amp;#39;re seeing these is that Steep needs a special comment to know what methods have been defined through &lt;code&gt;attr_reader&lt;/code&gt;s, so let&amp;#39;s add that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# @dynamic first_name, last_name, address
attr_reader :first_name, :last_name, :address
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, we need to add definitions for the methods to the generated &lt;code&gt;.rbi&lt;/code&gt; file. While we are at it, let&amp;#39;s also change the signatures from &lt;code&gt;any&lt;/code&gt; to the actual types:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class User
  @first_name: String
  @last_name: String
  @address: Address
  def initialize: (String, String, Address) -&amp;gt; any
  def first_name: () -&amp;gt; String
  def last_name: () -&amp;gt; String
  def address: () -&amp;gt; Address
  def full_name: () -&amp;gt; String
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, everything works as expected and &lt;code&gt;steep check&lt;/code&gt; doesn&amp;#39;t return any errors.&lt;/p&gt;
&lt;p&gt;On top of what we&amp;#39;ve seen so far, Steep also supports generics (e.g. &lt;code&gt;Hash&amp;lt;Symbol, String&amp;gt;&lt;/code&gt;) and union types, which represent an either-or choice between several types. For example, a user&amp;#39;s &lt;code&gt;top_post&lt;/code&gt; method could return the highest-ranked post written by the user, or &lt;code&gt;nil&lt;/code&gt; if they haven&amp;#39;t contributed anything yet. This is represented through the union type &lt;code&gt;(Post | nil)&lt;/code&gt;, and the corresponding signature would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def top_post: () -&amp;gt; (Post | nil)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While Steep certainly has fewer features than Sorbet, it&amp;#39;s still a helpful tool and seems to be more in line with what Matz envisioned type checking in Ruby 3 to look like.&lt;/p&gt;
&lt;h3&gt;Ruby Type Profiler&lt;/h3&gt;
&lt;p&gt;Yusuke Endoh (better known as &amp;quot;mame&amp;quot; in Ruby developer circles) from Cookpad is working on a so-called level 1 type checker called &lt;a href=&quot;https://github.com/mame/ruby-type-profiler&quot;&gt;Ruby Type Profiler&lt;/a&gt;. Unlike the other solutions presented here, it doesn&amp;#39;t need signature files or type annotations but instead tries to infer as much as possible about a Ruby program while parsing it. Although it catches a lot less potential problems than either Steep or Sorbet, it comes at no extra cost to the developer.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;While nobody can predict the future, it seems like type checking in Ruby is something that&amp;#39;s here to stay. Currently, there are efforts underway to standardize on a &amp;quot;Ruby Signature Language&amp;quot; for use in &lt;code&gt;.rbi&lt;/code&gt; files (potentially scaffolded by Ruby Type Profiler), so developers can use whichever tool they prefer. Steep already allows library authors to ship type information with their gems, and Sorbet has a similar mechanism in the form of &lt;a href=&quot;https://github.com/sorbet/sorbet-typed&quot;&gt;sorbet-typed&lt;/a&gt;, which was inspired by the DefinitelyTyped repository for TypeScript definitions. If you&amp;#39;re interested in helping shape the future of type checking in Ruby, now is a great time to get involved!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Digging Deeper into Ruby Templating: The Parser</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/07/30/ruby-magic-ruby-templating-the-parser.html"/>
    <id>https://blog.appsignal.com/2019/07/30/ruby-magic-ruby-templating-the-parser.html</id>
    <published>2019-07-30T00:00:00+00:00</published>
    <updated>2019-07-30T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">With the lexer in place, let’s move on to the next step: The parser.</summary>
    <content type="html">&lt;p&gt;Today, we continue our journey into Ruby Templating. With the lexer in place, let’s move on to the next step: The parser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/2019/07/02/ruby-magic-brewing-our-own-template-lexer-in-ruby.html&quot;&gt;Last time&lt;/a&gt;, we looked at string interpolation and subsequently, dived into creating our own templating language. We started by implementing a lexer that reads a template and converts it into a stream of tokens. Today, we’ll implement the accompanying parser. We will also dip our toes into a bit of language theory.&lt;/p&gt;
&lt;p&gt;Here we go!&lt;/p&gt;
&lt;h2&gt;Abstract Syntax Trees&lt;/h2&gt;
&lt;p&gt;Let’s look back to our simple example template for &lt;code&gt;Welcome to {{name}}&lt;/code&gt;. After using the lexer to tokenize the string, we get a list of tokens like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars::Lexer.tokenize(&amp;quot;Welcome to {{name}}&amp;quot;)
# =&amp;gt; [[:CONTENT, &amp;quot;Welcome to &amp;quot;], [:OPEN_EXPRESSION], [:IDENTIFIER, &amp;quot;name&amp;quot;], [:CLOSE]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ultimately, we want to evaluate the template and replace the expression with real values. To make things a bit more challenging, we also want to evaluate complex block expressions, allowing for repetition and conditionals.&lt;/p&gt;
&lt;p&gt;To do this, we have to generate an abstract syntax tree (AST) that describes the logical structure of the template. The tree consists of nodes that may reference other nodes or store additional data from the tokens.&lt;/p&gt;
&lt;p&gt;For our simple example, the desired abstract syntax tree looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2019-07/simple-ast.svg&quot; alt=&quot;Simple AST&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Defining a Grammar&lt;/h2&gt;
&lt;p&gt;To define the grammar, let&amp;#39;s start with the theoretical basis of a language. Like other programming languages, our templating language is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Context-free_language&quot;&gt;context-free language&lt;/a&gt; and therefore can be described by a &lt;a href=&quot;https://en.wikipedia.org/wiki/Context-free_grammar&quot;&gt;context-free grammar&lt;/a&gt;. &lt;em&gt;(Don’t let the mathematical notations in the detailed Wikipedia descriptions scare you away. The concept is pretty straight forward, and there are more developer-friendly ways to notate a grammar.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A context-free grammar is a set of rules that describe how all possible strings of a language are constructed. Let’s look at the grammar for our templating language in &lt;a href=&quot;https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form&quot;&gt;EBNF notation&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template = statements;
statements = { statement };
statement = CONTENT | expression | block_expression;
expression = OPEN_EXPRESSION, IDENTIFIER, arguments, CLOSE;
block_expression = OPEN_BLOCK, IDENTIFIER, arguments, CLOSE, statements, [ OPEN_INVERSE, CLOSE, statements ], OPEN_END_BLOCK, IDENTIFIER, CLOSE;
arguments = { IDENTIFIER };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each assignment defines a rule. The rule’s name is on the left and a bunch of other rules (lower case) or tokens (upper case) from our lexer are on the right. Rules and tokens can be concatenated using commas &lt;code&gt;,&lt;/code&gt; or alternated using the pipe &lt;code&gt;|&lt;/code&gt; symbol. Rules and tokens inside of curly braces &lt;code&gt;{ ... }&lt;/code&gt; might be repeated several times. When they are inside of brackets &lt;code&gt;[ ... ]&lt;/code&gt;, they are considered optional.&lt;/p&gt;
&lt;p&gt;The above grammar is a concise way to describe that a template consists of statements. A statement is either a &lt;code&gt;CONTENT&lt;/code&gt; token, an expression, or a block expression. An expression is an &lt;code&gt;OPEN_EXPRESSION&lt;/code&gt; token, followed by an &lt;code&gt;IDENTIFIER&lt;/code&gt; token, followed by arguments, followed by a &lt;code&gt;CLOSE&lt;/code&gt; token. And a block expression is the perfect example of why it’s better to use a notation like the one above instead of trying to describe it with a natural language.&lt;/p&gt;
&lt;p&gt;There are tools that &lt;a href=&quot;https://en.wikipedia.org/wiki/Comparison_of_parser_generators&quot;&gt;automatically generate parsers&lt;/a&gt; from grammar definitions like the one above. But in true Ruby Magic tradition, let&amp;#39;s have some fun and build the parser ourselves, hopefully learning a thing or two in the process.&lt;/p&gt;
&lt;h2&gt;Building the Parser&lt;/h2&gt;
&lt;p&gt;With the language theory aside, let’s jump into actually building the parser. Let’s start with an even more minimal, but still valid, template: &lt;code&gt;Welcome to Ruby Magic&lt;/code&gt;. This template doesn’t have any expressions and the list of tokens consists of just one element. Here’s what it looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;[[:CONTENT, &amp;quot;Welcome to Ruby Magic&amp;quot;]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we set up our parser class. It looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  class Parser
    def self.parse(tokens)
      new(tokens).parse
    end

    attr_reader :tokens

    def initialize(tokens)
      @tokens = tokens
    end

    def parse
      # Parsing starts here
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The class takes an array of tokens and stores it. It only has one public method called &lt;code&gt;parse&lt;/code&gt; that converts the tokens into an AST.&lt;/p&gt;
&lt;p&gt;Looking back at our grammar, the top-most rule is &lt;code&gt;template&lt;/code&gt;. That implies that &lt;code&gt;parse&lt;/code&gt;, at the start of the parsing process, will return a &lt;code&gt;Template&lt;/code&gt; node.&lt;/p&gt;
&lt;p&gt;Nodes are simple classes with no behavior of their own. They just connect other nodes or store some values from the tokens. Here’s what the &lt;code&gt;Template&lt;/code&gt; node looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  module Nodes
    class Template
      attr_reader :statements

      def initialize(statements)
        @statements = statements
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make our example work, we also need a &lt;code&gt;Content&lt;/code&gt; node. It just stores the text content (&lt;code&gt;&amp;quot;Welcome to Ruby Magic&amp;quot;&lt;/code&gt;) from the token.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  module Nodes
    class Content
      attr_reader :content

      def initialize(content)
        @content = content
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let’s implement the parse method to create an instance of &lt;code&gt;Template&lt;/code&gt; and an instance of &lt;code&gt;Content&lt;/code&gt; and connect them up correctly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse
  Magicbars::Nodes::Template.new(parse_content)
end

def parse_content
  return unless tokens[0][0] == :CONTENT

  Magicbars::Nodes::Content.new(tokens[0][1])
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we run the parser, we get the correct result:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars::Parser.parse(tokens)
# =&amp;gt; #&amp;lt;Magicbars::Nodes::Template:0x00007fe90e939410 @statements=#&amp;lt;Magicbars::Nodes::Content:0x00007fe90e939578 @content=&amp;quot;Welcome to Ruby Magic&amp;quot;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Admittedly, this only works for our simple example that only has one content node. Let’s switch to a more complex example that actually includes an expression: &lt;code&gt;Welcome to {{name}}&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars::Lexer.tokenize(&amp;quot;Welcome to {{name}}&amp;quot;)
# =&amp;gt; [[:CONTENT, &amp;quot;Welcome to &amp;quot;], [:OPEN_EXPRESSION], [:IDENTIFIER, &amp;quot;name&amp;quot;], [:CLOSE]]
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;For this, we need an &lt;code&gt;Expression&lt;/code&gt; node and an &lt;code&gt;Identifier&lt;/code&gt; node. The &lt;code&gt;Expression&lt;/code&gt; node stores the identifier as well as any arguments (which, according to the grammar, are an array of zero or more &lt;code&gt;Identifier&lt;/code&gt; nodes). As with the other nodes, there’s not much to see here.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  module Nodes
    class Expression
      attr_reader :identifier, :arguments

      def initialize(identifier, arguments)
        @identifier = identifier
        @arguments = arguments
      end
    end
  end
end

module Magicbars
  module Nodes
    class Identifier
      attr_reader :value

      def initialize(value)
        @value = value.to_sym
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the new nodes in place, let’s modify the &lt;code&gt;parse&lt;/code&gt; method to handle both regular content as well as expressions. We do that by introducing a &lt;code&gt;parse_statements&lt;/code&gt; method that just keeps on calling &lt;code&gt;parse_statement&lt;/code&gt; as long as it returns a value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse
  Magicbars::Nodes::Template.new(parse_statements)
end

def parse_statements
  results = []

  while result = parse_statement
    results &amp;lt;&amp;lt; result
  end

  results
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;parse_statement&lt;/code&gt; itself first calls &lt;code&gt;parse_content&lt;/code&gt; and if that doesn’t return any value, it calls &lt;code&gt;parse_expression&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse_statement
  parse_content || parse_expression
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Have you noticed that the &lt;code&gt;parse_statement&lt;/code&gt; method is starting to look very similar to the &lt;code&gt;statement&lt;/code&gt; rule in the grammar? This is where taking the time to explicitly write up the grammar beforehand helps a lot to ensure that we’re on the right path.&lt;/p&gt;
&lt;p&gt;Next, let’s modify the &lt;code&gt;parse_content&lt;/code&gt; method so that it doesn’t only look at the first token. We do this by introducing an additional &lt;code&gt;@position&lt;/code&gt; instance variable in the initializer and use it to fetch the current token.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;attr_reader :tokens, :position

def initialize(tokens)
  @tokens = tokens
  @position = 0
end

# ...

def parse_content
  return unless token = tokens[position]
  return unless token[0] == :CONTENT

  @position += 1

  Magicbars::Nodes::Content.new(token[1])
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;parse_content&lt;/code&gt; method now looks at the current token and checks its type. If it’s a &lt;code&gt;CONTENT&lt;/code&gt; token, it increments the position (because the current token was successfully parsed) and uses the token’s content to create the &lt;code&gt;Content&lt;/code&gt; node. If there is no current token (because we’re at the end of the tokens) or the type doesn’t match, the method exits early and returns &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With the improved &lt;code&gt;parse_content&lt;/code&gt; method in place, let’s tackle the new &lt;code&gt;parse_expression&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse_expression
  return unless token = tokens[position]
  return unless token[0] == :OPEN_EXPRESSION

  @position += 1

  identifier = parse_identifier
  arguments = parse_arguments

  if !tokens[position] || tokens[position][0] != :CLOSE
    raise &amp;quot;Unexpected token #{tokens[position][0]}. Expected :CLOSE.&amp;quot;
  end

  @position += 1

  Magicbars::Nodes::Expression.new(identifier, arguments)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we check that there is a current token and that its type is &lt;code&gt;OPEN_EXPRESSION&lt;/code&gt;. If that’s the case, we advance to the next token and parse the identifier as well as the arguments by calling &lt;code&gt;parse_identifier&lt;/code&gt; and &lt;code&gt;parse_arguments&lt;/code&gt;, respectively. Both methods will return the respective nodes and advance the current token. When that’s done, we ensure that the current token exists and is a &lt;code&gt;:CLOSE&lt;/code&gt; token. If it is not, we raise an error. Otherwise, we advance the position one last time, before returning the newly created &lt;code&gt;Expression&lt;/code&gt; node.&lt;/p&gt;
&lt;p&gt;At this point, we see some patterns emerge. We’re advancing to the next token several times and we’re also checking that there is a current token and its type. Because the code for that is a bit cumbersome, let’s introduce two helper methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def expect(*expected_tokens)
  upcoming = tokens[position, expected_tokens.size]

  if upcoming.map(&amp;amp;:first) == expected_tokens
    advance(expected_tokens.size)
    upcoming
  end
end

def advance(offset = 1)
  @position += offset
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;expect&lt;/code&gt; method takes a variable number of token types and checks them against the next tokens in the token stream. If they all match, it advances past the matching tokens and returns them. The &lt;code&gt;advance&lt;/code&gt; method just increments the &lt;code&gt;@position&lt;/code&gt; instance variable by the given offset.&lt;/p&gt;
&lt;p&gt;For cases where there&amp;#39;s no flexibility regarding the next expected token, we also introduce a method that raises a nice error message when the token doesn’t match.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def need(*required_tokens)
  upcoming = tokens[position, required_tokens.size]
  expect(*required_tokens) or raise &amp;quot;Unexpected tokens. Expected #{required_tokens.inspect} but got #{upcoming.inspect}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using these helper methods, &lt;code&gt;parse_content&lt;/code&gt; and &lt;code&gt;parse_expression&lt;/code&gt; are now cleaner and more readable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse_content
  if content = expect(:CONTENT)
    Magicbars::Nodes::Content.new(content[0][1])
  end
end

def parse_expression
  return unless expect(:OPEN_EXPRESSION)

  identifier = parse_identifier
  arguments = parse_arguments

  need(:CLOSE)

  Magicbars::Nodes::Expression.new(identifier, arguments)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let’s also look at &lt;code&gt;parse_identifier&lt;/code&gt; and &lt;code&gt;parse_arguments&lt;/code&gt;. Thanks to the helper methods, the &lt;code&gt;parse_identifier&lt;/code&gt; method is as simple as the &lt;code&gt;parse_content&lt;/code&gt; method. The only difference is that it returns another node type.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse_identifier
  if identifier = expect(:IDENTIFIER)
    Magicbars::Nodes::Identifier.new(identifier[0][1])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When implementing the &lt;code&gt;parse_arguments&lt;/code&gt; method, we noticed that it’s almost identical to the &lt;code&gt;parse_statements&lt;/code&gt; method. The only difference is that it calls &lt;code&gt;parse_identifier&lt;/code&gt; instead of &lt;code&gt;parse_statement&lt;/code&gt;. We can get rid of the duplicated logic by introducing another helper method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def repeat(method)
  results = []

  while result = send(method)
    results &amp;lt;&amp;lt; result
  end

  results
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;repeat&lt;/code&gt; method uses &lt;code&gt;send&lt;/code&gt; to call the given method name until it no longer returns a node. Once that happens, the collected results (or just an empty array) are returned. With this helper in place, both &lt;code&gt;parse_statements&lt;/code&gt; and &lt;code&gt;parse_arguments&lt;/code&gt; become one-line methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse_statements
  repeat(:parse_statement)
end

def parse_arguments
  repeat(:parse_identifier)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all these changes in place, let’s try and parse the token stream:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars::Parser.parse(tokens)
# =&amp;gt; #&amp;lt;Magicbars::Nodes::Template:0x00007f91a602f910
#     @statements=
#      [#&amp;lt;Magicbars::Nodes::Content:0x00007f91a58802c8 @content=&amp;quot;Welcome to &amp;quot;&amp;gt;,
#       #&amp;lt;Magicbars::Nodes::Expression:0x00007f91a602fcd0
#        @arguments=[],
#        @identifier=
#         #&amp;lt;Magicbars::Nodes::Identifier:0x00007f91a5880138 @value=:name&amp;gt;  &amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s a bit hard to read but it’s, in fact, the correct abstract syntax tree. The &lt;code&gt;Template&lt;/code&gt; node has a &lt;code&gt;Content&lt;/code&gt; and an &lt;code&gt;Expression&lt;/code&gt; statement. The &lt;code&gt;Content&lt;/code&gt; node’s value is &lt;code&gt;&amp;quot;Welcome to &amp;quot;&lt;/code&gt; and the &lt;code&gt;Expression&lt;/code&gt; node’s identifier is the &lt;code&gt;Identifier&lt;/code&gt; node with &lt;code&gt;:name&lt;/code&gt; as its value.&lt;/p&gt;
&lt;h2&gt;Parsing Block Expressions&lt;/h2&gt;
&lt;p&gt;To complete our parser implementation, we still have to implement parsing of block expressions. As a reminder, here’s the template we want to parse:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Welcome to {{name}}!

{{#if subscribed}}
  Thank you for subscribing to our mailing list.
{{else}}
  Please sign up for our mailing list to be notified about new articles!
{{/if}}

Your friends at {{company_name}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To do this, let’s first introduce a &lt;code&gt;BlockExpression&lt;/code&gt; node. While this node stores a bit more data, it doesn’t do anything else and therefore isn’t very exciting.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magicbars
  module Nodes
    class BlockExpression
      attr_reader :identifier, :arguments, :statements, :inverse_statements

      def initialize(identifier, arguments, statements, inverse_statements)
        @identifier = identifier
        @arguments = arguments
        @statements = statements
        @inverse_statements = inverse_statements
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like the &lt;code&gt;Expression&lt;/code&gt; node, it stores the identifier as well as any arguments. Additionally, it also stores the statements of the block and of the inverse block.&lt;/p&gt;
&lt;p&gt;Looking back at the grammar, we notice that to parse block expressions, we have to amend the &lt;code&gt;parse_statements&lt;/code&gt; method with a call to &lt;code&gt;parse_block_expression&lt;/code&gt;. It now looks just like the rule in the grammar.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse_statement
  parse_content || parse_expression || parse_block_expression
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;parse_block_expression&lt;/code&gt; method itself is a bit more complex. But thanks to our helper methods, it’s still quite readable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def parse_block_expression
  return unless expect(:OPEN_BLOCK)

  identifier = parse_identifier
  arguments = parse_arguments

  need(:CLOSE)

  statements = parse_statements

  if expect(:OPEN_INVERSE, :CLOSE)
    inverse_statements = parse_statements
  end

  need(:OPEN_END_BLOCK)

  if identifier.value != parse_identifier.value
    raise(&amp;quot;Error. Identifier in closing expression does not match identifier in opening expression&amp;quot;)
  end

  need(:CLOSE)

  Magicbars::Nodes::BlockExpression.new(identifier, arguments, statements, inverse_statements)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first part is very similar to the &lt;code&gt;parse_expression&lt;/code&gt; method. It parses the opening block expression with the identifier and the arguments. Afterward, it calls &lt;code&gt;parse_statements&lt;/code&gt; to parse the inside of the block.&lt;/p&gt;
&lt;p&gt;Once that’s done, we check for an &lt;code&gt;{{else}}&lt;/code&gt; expression, identified by an &lt;code&gt;OPEN_INVERSE&lt;/code&gt; token followed by a &lt;code&gt;CLOSE&lt;/code&gt; token. If both tokens are found, we call &lt;code&gt;parse_statements&lt;/code&gt; again to parse the inverse block. Otherwise, we just skip that part entirely.&lt;/p&gt;
&lt;p&gt;As a final thing, we ensure that there is an end block expression using the same identifier as the open block expression. If the identifiers don’t match, we raise an error. Otherwise, we create a new &lt;code&gt;BlockExpression&lt;/code&gt; node and return it.&lt;/p&gt;
&lt;p&gt;Calling the parser with the tokens of the advanced block expression template will return the AST for the template. I’ll not include the example output here, as it’s barely readable. Instead, here’s a visual representation of the generated AST.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2019-07/complex-ast.svg&quot; alt=&quot;Complex AST&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Because we’re calling &lt;code&gt;parse_statements&lt;/code&gt; inside of &lt;code&gt;parse_block_expression&lt;/code&gt;, both the block and the inverse block may include more expressions, block expressions, as well as regular content.&lt;/p&gt;
&lt;h2&gt;The Journey Continues…&lt;/h2&gt;
&lt;p&gt;We made decent progress with our journey towards implementing our own templating language. After a short dip into language theory, we defined a grammar for our templating language and used it to implement a parser for it from scratch.&lt;/p&gt;
&lt;p&gt;With both the lexer and the parser in place, we’re only missing an interpreter to generate the interpolated string from our template. We’ll cover this part in an upcoming edition of RubyMagic. Subscribe to &lt;a href=&quot;/ruby-magic/&quot;&gt;the Ruby Magic mailinglist&lt;/a&gt;, to get alerted when it comes out.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Brewing our own Template Lexer in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/07/02/ruby-magic-brewing-our-own-template-lexer-in-ruby.html"/>
    <id>https://blog.appsignal.com/2019/07/02/ruby-magic-brewing-our-own-template-lexer-in-ruby.html</id>
    <published>2019-07-02T00:00:00+00:00</published>
    <updated>2019-07-02T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">For our dive into templating, we end up writing our own lexer.</summary>
    <content type="html">&lt;p&gt;Put on your scuba diving suite and pack your stencils, we&amp;#39;re diving into Templates today!&lt;/p&gt;
&lt;p&gt;Most software that renders web pages or generates emails uses templating to embed variable data into text documents. The main structure of the document is often set up in a static template with placeholders for the data. The variable data, like user names or web page contents, replace the placeholders while rendering the page.&lt;/p&gt;
&lt;p&gt;For our dive into templating, we&amp;#39;ll implement a subset of &lt;a href=&quot;https://github.com/mustache/mustache&quot;&gt;Mustache&lt;/a&gt;, a templating language that&amp;#39;s available in many programming languages. In this episode, we&amp;#39;ll investigate different ways of templating. We&amp;#39;ll start out looking at string concatenation, and end up writing our own lexer to allow for more complex templates.&lt;/p&gt;
&lt;h2&gt;Using Native String Interpolation&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s start with a minimal example. Our application needs a welcome message that happens to include a project name. The quickest way to do this is by using Ruby&amp;#39;s built-in string interpolation feature.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;name = &amp;quot;Ruby Magic&amp;quot;
template = &amp;quot;Welcome to #{name}&amp;quot;
# =&amp;gt; Welcome to Ruby Magic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great! That was doable. However, what if we want to reuse the template for multiple occasions, or allow our users to update the template?&lt;/p&gt;
&lt;p&gt;The interpolation evaluates immediately. We can&amp;#39;t reuse the template (unless we redefine it—in a loop, for instance) and we can&amp;#39;t store the &lt;code&gt;Welcome to #{name}&lt;/code&gt; template in a database and populate it later without using the potentially dangerous &lt;code&gt;eval&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Luckily, Ruby has a different way of interpolating strings: &lt;code&gt;Kernel#sprintf&lt;/code&gt; or &lt;code&gt;String#%&lt;/code&gt;. These methods allow us to get an interpolated string without changing the template itself. This way, we can reuse the same template multiple times. It also doesn&amp;#39;t allow execution of arbitrary Ruby code. Let&amp;#39;s use it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;name = &amp;quot;Ruby Magic&amp;quot;
template = &amp;quot;Welcome to %{name}&amp;quot;

sprintf(template, name: name)
# =&amp;gt; &amp;quot;Welcome to Ruby Magic&amp;quot;

template % { name: name }
# =&amp;gt; &amp;quot;Welcome to Ruby Magic&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The &lt;code&gt;Regexp&lt;/code&gt; Approach to Templating&lt;/h2&gt;
&lt;p&gt;While the above solution works, it&amp;#39;s not fool-proof, and it exposes more functionality than we usually want to. Let&amp;#39;s look at an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;name = &amp;quot;Ruby Magic&amp;quot;
template = &amp;quot;Welcome to %d&amp;quot;

sprintf(template, name: name)
# =&amp;gt; TypeError (can&amp;#39;t convert Hash into Integer)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both &lt;code&gt;Kernel#sprintf&lt;/code&gt; and &lt;code&gt;String#%&lt;/code&gt; allow special syntax to handle different types of data. Not all of them are compatible with the data we pass. In this example, the template expects to format a number but gets passed a Hash, producing a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But we have more power tools in our shed: we can implement our own interpolation using regular expressions. Using regular expressions allows us to define a custom syntax, like a &lt;a href=&quot;https://mustache.github.io&quot;&gt;Mustache&lt;/a&gt;/&lt;a href=&quot;https://handlebarsjs.com&quot;&gt;Handlebars&lt;/a&gt; inspired style.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;name = &amp;quot;Ruby Magic&amp;quot;
template = &amp;quot;Welcome to {{name}}&amp;quot;
assigns = { &amp;quot;name&amp;quot; =&amp;gt; name }

template.gsub(/{{(\w+)}}/) { assigns[$1] }
# =&amp;gt; Welcome to Ruby Magic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use &lt;code&gt;String#gsub&lt;/code&gt; to replace all placeholders (words in double curly braces) with their value in the &lt;code&gt;assigns&lt;/code&gt; hash. If there is no corresponding value, this method removes the placeholder without inserting anything.&lt;/p&gt;
&lt;p&gt;Replacing placeholders in a string like this is a viable solution for a string with a couple of placeholders. However, once things get a bit more complicated, we quickly run into problems.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say we need to have conditionals in the template. The result should be different based on the value of a variable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-handlebars&quot;&gt;Welcome to
{{name}}!

{{#if subscribed}}
  Thank you for subscribing to our mailing list.
{{else}}
  Please sign up for our mailing list to be notified about new articles!
{{/if}}

Your friends at
{{company_name}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Regular expressions can&amp;#39;t smoothly handle this use case. If you try hard enough, you can probably still hack something together, but at this point, it&amp;#39;s better to build a proper templating language.&lt;/p&gt;
&lt;h2&gt;Building a Templating Language&lt;/h2&gt;
&lt;p&gt;Implementing a templating language is similar to implementing other programming languages. Just like a scripting language, a template language needs three components: A lexer, a parser, and an interpreter. We&amp;#39;ll look at these, one by one.&lt;/p&gt;
&lt;h1&gt;Lexer&lt;/h1&gt;
&lt;p&gt;The first task we need to tackle is called tokenization, or lexical analysis. The process is very similar to identifying word categories in natural languages.&lt;/p&gt;
&lt;p&gt;Take an example like &lt;code&gt;Ruby is a lovely language&lt;/code&gt;. The sentence consists of five words of different categories. To identify what category they are, you&amp;#39;d take a dictionary and look up every word&amp;#39;s category, which would result in a list like this: &lt;em&gt;Noun&lt;/em&gt;, &lt;em&gt;Verb&lt;/em&gt;, &lt;em&gt;Article&lt;/em&gt;, &lt;em&gt;Adjective&lt;/em&gt;, &lt;em&gt;Noun&lt;/em&gt;. Natural language processing calls these &amp;quot;Parts of Speech&amp;quot;. In formal languages--like programming languages-- they&amp;#39;re called &lt;em&gt;tokens&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A lexer works by reading the template and matching the stream of text with a set of regular expressions for each category in a given order. The first one that matches defines the category of the token and attaches relevant data to it.&lt;/p&gt;
&lt;p&gt;With this little bit of theory out of the way, let&amp;#39;s implement a lexer for our template language. To make things a little bit easier, we use &lt;code&gt;StringScanner&lt;/code&gt; by requiring &lt;code&gt;strscan&lt;/code&gt; from Ruby&amp;#39;s standard library. (By the way, we&amp;#39;ve got an excellent intro to &lt;code&gt;StringScanner&lt;/code&gt; in &lt;a href=&quot;/2019/03/05/stringscanner.html&quot;&gt;one of our previous editions&lt;/a&gt;.) As a first step, let&amp;#39;s build a minimal version that identifies everything as &lt;code&gt;CONTENT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We do this by creating a new &lt;code&gt;StringScanner&lt;/code&gt; instance and letting it do its job using an &lt;code&gt;until&lt;/code&gt; loop that only stops when the scanner reaches the end of the string.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;For now, we just let it match every character (&lt;code&gt;.*&lt;/code&gt;) across multiple lines (the &lt;code&gt;m&lt;/code&gt; modifier) and return one &lt;code&gt;CONTENT&lt;/code&gt; token for all of it. We represent a token as an array with the token name as the first element and any data as the second element. Our very basic lexer looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;strscan&amp;#39;

module Magicbars
  class Lexer
    def self.tokenize(code)
      new.tokenize(code)
    end

    def tokenize(code)
      scanner = StringScanner.new(code)
      tokens = []

      until scanner.eos?
        tokens &amp;lt;&amp;lt; [:CONTENT, scanner.scan(/.*?/m)]
      end

      tokens
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When running this code with &lt;code&gt;Welcome to {{name}}&lt;/code&gt; we get back a list of precisely one &lt;code&gt;CONTENT&lt;/code&gt; token with all of the code attached to it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars::Lexer.tokenize(&amp;quot;Welcome to {{name}}&amp;quot;)
=&amp;gt; [[:CONTENT, &amp;quot;Welcome to {{name}}&amp;quot;]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&amp;#39;s detect the expression. To do so, we modify the code inside the loop, so it matches &lt;code&gt;{{&lt;/code&gt; and &lt;code&gt;}}&lt;/code&gt; as &lt;code&gt;OPEN_EXPRESSION&lt;/code&gt; and &lt;code&gt;CLOSE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We do this by adding a conditional that checks for the different cases.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;until scanner.eos?
  if scanner.scan(/{{/)
    tokens &amp;lt;&amp;lt; [:OPEN_EXPRESSION]
  elsif scanner.scan(/}}/)
    tokens &amp;lt;&amp;lt; [:CLOSE]
  elsif scanner.scan(/.*?/m)
    tokens &amp;lt;&amp;lt; [:CONTENT, scanner.matched]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&amp;#39;s no added value in attaching the curly braces to the &lt;code&gt;OPEN_EXPRESSION&lt;/code&gt; and &lt;code&gt;CLOSE&lt;/code&gt; tokens, so we drop them. As the &lt;code&gt;scan&lt;/code&gt; calls are now part of the condition, we use &lt;code&gt;scanner.matched&lt;/code&gt; to attach the result of the last match to the &lt;code&gt;CONTENT&lt;/code&gt; token.&lt;/p&gt;
&lt;p&gt;Unfortunately, when rerunning the lexer, we still get only one &lt;code&gt;CONTENT&lt;/code&gt; token like before. We still have to modify the last expression to match everything up to the open expression. We do this by using &lt;code&gt;scan_until&lt;/code&gt; with a positive lookahead anchor for double curly braces that stops the scanner right before them. Our code inside the loop now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;until scanner.eos?
  if scanner.scan(/{{/)
    tokens &amp;lt;&amp;lt; [:OPEN_EXPRESSION]
  elsif scanner.scan(/}}/)
    tokens &amp;lt;&amp;lt; [:CLOSE]
  elsif scanner.scan_until(/.*?(?={{|}})/m)
    tokens &amp;lt;&amp;lt; [:CONTENT, scanner.matched]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the lexer again, now results in four tokens:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars::Lexer.tokenize(&amp;quot;Welcome to {{name}}&amp;quot;)
=&amp;gt; [[:CONTENT, &amp;quot;Welcome to &amp;quot;], [:OPEN_EXPRESSION], [:CONTENT, &amp;quot;name&amp;quot;], [:CLOSE]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our lexer looks pretty close to the result we want. However, &lt;code&gt;name&lt;/code&gt; isn&amp;#39;t regular content; it&amp;#39;s an identifier! Strings between double curly braces should be treated differently than strings outside.&lt;/p&gt;
&lt;h2&gt;A State Machine&lt;/h2&gt;
&lt;p&gt;To do this, we turn the lexer into a state machine with two distinct states. It starts in the &lt;code&gt;default&lt;/code&gt; state. When it hit&amp;#39;s an &lt;code&gt;OPEN_EXPRESSION&lt;/code&gt; token, it moves to the &lt;code&gt;expression&lt;/code&gt; state and stays there until it comes across a &lt;code&gt;CLOSE&lt;/code&gt; token which makes it transition back to the &lt;code&gt;default&lt;/code&gt; state.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2019-07/state-machine.svg&quot; alt=&quot;State Machine&quot;/&gt;&lt;/p&gt;
&lt;p&gt;We implement the state machine by adding a few methods that use an array to manage the current state.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def stack
  @stack ||= []
end

def state
  stack.last || :default
end

def push_state(state)
  stack.push(state)
end

def pop_state
  stack.pop
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;state&lt;/code&gt; method will either return the current state or &lt;code&gt;default&lt;/code&gt;. &lt;code&gt;push_state&lt;/code&gt; moves the lexer into a new state by adding it to the stack. &lt;code&gt;pop_state&lt;/code&gt; moves the lexer back to the previous state.&lt;/p&gt;
&lt;p&gt;Next, we split up the conditional within the loop and wrap it by a conditional that checks for the current state. While in the &lt;code&gt;default&lt;/code&gt; state, we handle both &lt;code&gt;OPEN_EXPRESSION&lt;/code&gt; and &lt;code&gt;CONTENT&lt;/code&gt; tokens. This also means that the regular expression for &lt;code&gt;CONTENT&lt;/code&gt; doesn&amp;#39;t need the &lt;code&gt;}}&lt;/code&gt; lookahead anymore, so we drop it. In the &lt;code&gt;expression&lt;/code&gt; state, we handle the &lt;code&gt;CLOSE&lt;/code&gt; token and add a new regular expression for &lt;code&gt;IDENTIFIER&lt;/code&gt;. Of course, we also implement the state transitions by adding a &lt;code&gt;push_state&lt;/code&gt; call to &lt;code&gt;OPEN_EXPRESSION&lt;/code&gt; and a &lt;code&gt;pop_state&lt;/code&gt; call to &lt;code&gt;CLOSE&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if state == :default
  if scanner.scan(/{{/)
    tokens &amp;lt;&amp;lt; [:OPEN_EXPRESSION]
    push_state :expression
  elsif scanner.scan_until(/.*?(?={{)/m)
    tokens &amp;lt;&amp;lt; [:CONTENT, scanner.matched]
  end
elsif state == :expression
  if scanner.scan(/}}/)
    tokens &amp;lt;&amp;lt; [:CLOSE]
    pop_state
  elsif scanner.scan(/[\w\-]+/)
    tokens &amp;lt;&amp;lt; [:IDENTIFIER, scanner.matched]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these changes in place, the lexer now properly tokenizes our example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magicbars::Lexer.tokenize(&amp;quot;Welcome to {{name}}&amp;quot;)
# =&amp;gt; [[:CONTENT, &amp;quot;Welcome to &amp;quot;], [:OPEN_EXPRESSION], [:IDENTIFIER, &amp;quot;name&amp;quot;], [:CLOSE]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Making it harder for ourselves&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s move on to a more advanced example. This one uses multiple expressions, as well as a block.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Welcome to {{name}}!

{{#if subscribed}}
  Thank you for subscribing to our mailing list.
{{else}}
  Please sign up for our mailing list to be notified about new articles!
{{/if}}

Your friends at {{company_name}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s no surprise that our lexer fails to parse this example. To make it work, we have to add the missing tokens and make it handle the content after the last expression. The code inside the loop looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if state == :default
  if scanner.scan(/{{#/)
    tokens &amp;lt;&amp;lt; [:OPEN_BLOCK]
    push_state :expression
  elsif scanner.scan(/{{\//)
    tokens &amp;lt;&amp;lt; [:OPEN_END_BLOCK]
    push_state :expression
  elsif scanner.scan(/{{else/)
    tokens &amp;lt;&amp;lt; [:OPEN_INVERSE]
    push_state :expression
  elsif scanner.scan(/{{/)
    tokens &amp;lt;&amp;lt; [:OPEN_EXPRESSION]
    push_state :expression
  elsif scanner.scan_until(/.*?(?={{)/m)
    tokens &amp;lt;&amp;lt; [:CONTENT, scanner.matched]
  else
    tokens &amp;lt;&amp;lt; [:CONTENT, scanner.rest]
    scanner.terminate
  end
elsif state == :expression
  if scanner.scan(/\s+/)
    # Ignore whitespace
  elsif scanner.scan(/}}/)
    tokens &amp;lt;&amp;lt; [:CLOSE]
    pop_state
  elsif scanner.scan(/[\w\-]+/)
    tokens &amp;lt;&amp;lt; [:IDENTIFIER, scanner.matched]
  else
    scanner.terminate
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please keep in mind that the order of the conditions is important to some extent. The first regular expression that matches is assigned. Thus, more specific expressions have to come before more generic ones. The prime example of this is the collection of specialized open tokens for blocks.&lt;/p&gt;
&lt;p&gt;Using the final version of the lexer, the example now tokenizes into this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;[
  [:CONTENT, &amp;quot;Welcome to &amp;quot;],
  [:OPEN_EXPRESSION],
  [:IDENTIFIER, &amp;quot;name&amp;quot;],
  [:CLOSE],
  [:CONTENT, &amp;quot;!\n\n&amp;quot;],
  [:OPEN_BLOCK],
  [:IDENTIFIER, &amp;quot;if&amp;quot;],
  [:IDENTIFIER, &amp;quot;subscribed&amp;quot;],
  [:CLOSE],
  [:CONTENT, &amp;quot;\n  Thank you for subscribing to our mailing list.\n&amp;quot;],
  [:OPEN_INVERSE],
  [:CLOSE],
  [:CONTENT, &amp;quot;\n  Please sign up for our mailing list to be notified about new articles!\n&amp;quot;],
  [:OPEN_END_BLOCK],
  [:IDENTIFIER, &amp;quot;if&amp;quot;],
  [:CLOSE],
  [:CONTENT, &amp;quot;\n\nYour friends at &amp;quot;],
  [:OPEN_EXPRESSION],
  [:IDENTIFIER, &amp;quot;company_name&amp;quot;],
  [:CLOSE],
  [:CONTENT, &amp;quot;\n&amp;quot;]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we&amp;#39;re finished, we&amp;#39;ve identified seven different types of tokens:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Token&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OPEN_BLOCK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{#&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OPEN_END_BLOCK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OPEN_INVERSE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{else&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OPEN_EXPRESSION&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CONTENT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anything outside of expressions (normal HTML or Text)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CLOSE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;}}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IDENTIFIER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identifiers consist of Word characters, numbers, &lt;code&gt;_&lt;/code&gt;, and &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The next step is to implement a parser that tries to figure out the structure of the token stream and translates it into an abstract syntax tree, but that&amp;#39;s for another time.&lt;/p&gt;
&lt;h2&gt;The Road Ahead&lt;/h2&gt;
&lt;p&gt;We started our journey towards our own templating language by looking at different ways to implement a basic templating system using string interpolation. When we hit the limits of the first approaches, we started implementing a proper templating system.&lt;/p&gt;
&lt;p&gt;For now, we implemented a lexer that analyses the template and figures out the different types of tokens. In an upcoming edition of Ruby Magic, we&amp;#39;ll continue the journey by implementing a parser as well as an interpreter to generate an interpolated string.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Inside Enumeration in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/05/28/ruby-magic-enumeration.html"/>
    <id>https://blog.appsignal.com/2019/05/28/ruby-magic-enumeration.html</id>
    <published>2019-05-28T00:00:00+00:00</published>
    <updated>2019-05-28T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">We dive even deeper into enumeration by looking at the internal implementation and implementing our own versions of the `Enumerable` module and the `Enumerator` class.</summary>
    <content type="html">&lt;p&gt;Welcome back to another edition of Ruby Magic! A year ago, we learned about &lt;a href=&quot;/2018/05/29/ruby-magic-enumerable-and-enumerator.html&quot;&gt;Ruby’s &lt;code&gt;Enumerable&lt;/code&gt; module&lt;/a&gt;, which provides the methods you use when working with enumerable objects like arrays, ranges, and hashes.&lt;/p&gt;
&lt;p&gt;Back then, we created a &lt;code&gt;LinkedList&lt;/code&gt; class to show how to make an object enumerable by implementing the &lt;code&gt;#each&lt;/code&gt; method on it. By including the &lt;code&gt;Enumerable&lt;/code&gt; module, we were able to call methods like &lt;code&gt;#count&lt;/code&gt;, &lt;code&gt;#map&lt;/code&gt; and &lt;code&gt;#select&lt;/code&gt; on any linked list without having to implement them ourselves.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve learned how to use enumerables, but how do they work? Part of the magic in enumerables in Ruby comes from their internal implementation, which is all based on the single &lt;code&gt;#each&lt;/code&gt; method, and even allows chaining enumerators.&lt;/p&gt;
&lt;p&gt;Today, we’ll learn how the methods in the &lt;code&gt;Enumerable&lt;/code&gt; class are implemented and how &lt;code&gt;Enumerator&lt;/code&gt; objects allow chaining enumeration methods.&lt;/p&gt;
&lt;p&gt;As you&amp;#39;ve become accustomed to, we&amp;#39;ll dive in deep by implementing our own versions of the &lt;code&gt;Enumerable&lt;/code&gt; module and &lt;code&gt;Enumerator&lt;/code&gt; class. So, put on your over-engineering helmet and let&amp;#39;s go!&lt;/p&gt;
&lt;h2&gt;Linked Lists&lt;/h2&gt;
&lt;p&gt;Before we begin, let’s start with a new version of the linked list class we wrote previously.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LinkedList
  def initialize(head = nil, *rest)
    @head = head

    if rest.first.is_a?(LinkedList)
      @tail = rest.first
    elsif rest.any?
      @tail = LinkedList.new(*rest)
    end
  end

  def &amp;lt;&amp;lt;(head)
    @head ? LinkedList.new(head, self) : LinkedList.new(head)
  end

  def inspect
    [@head, @tail].compact
  end

  def each(&amp;amp;block)
    yield @head if @head
    @tail.each(&amp;amp;block) if @tail
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike the previous version, this implementation allows empty lists to be created, as well as lists with more than two items. This version also allows passing a linked list as the tail when initializing another.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;irb&amp;gt; LinkedList.new
=&amp;gt; []
irb&amp;gt; LinkedList.new(1)
=&amp;gt; [1]
irb&amp;gt; LinkedList.new(1, 2)
=&amp;gt; [1,[2]]
irb&amp;gt; LinkedList.new(1, 2, 3)
=&amp;gt; [1,[2,[3]]]
irb&amp;gt; LinkedList.new(1, LinkedList.new(2, 3))
=&amp;gt; [1,[2,[3]]]
irb&amp;gt; LinkedList.new(1, 2, LinkedList.new(3))
=&amp;gt; [1,[2,[3]]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Previously, our &lt;code&gt;LinkedLIst&lt;/code&gt; class included &lt;a href=&quot;http://ruby-doc.org/core-2.6.3/Enumerable.html&quot;&gt;the &lt;code&gt;Enumerable&lt;/code&gt; module&lt;/a&gt;. When mapping over an object using one of &lt;code&gt;Enumerable&lt;/code&gt;&amp;#39;s methods, the result are stored in an array. This time, we&amp;#39;ll implement our own version to make sure our methods return new linked lists instead.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Enumerable Methods&lt;/h2&gt;
&lt;p&gt;Ruby&amp;#39;s &lt;code&gt;Enumerable&lt;/code&gt; module comes with enumeration methods like &lt;code&gt;#map&lt;/code&gt;, &lt;code&gt;#count&lt;/code&gt;, and &lt;code&gt;#select&lt;/code&gt;. By implementing the &lt;code&gt;#each&lt;/code&gt; method and including the &lt;code&gt;Enumerable&lt;/code&gt; module in our class, we&amp;#39;d be able to use those methods directly on our linked lists.&lt;/p&gt;
&lt;p&gt;Instead, we&amp;#39;ll implement &lt;code&gt;DIYEnumerable&lt;/code&gt; and import that instead of Ruby&amp;#39;s version. This isn&amp;#39;t something you&amp;#39;d typically do, but it will give us a clear insight into how enumeration works internally.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s start with &lt;code&gt;#count&lt;/code&gt;. Each of the importable methods in the &lt;code&gt;Enumerable&lt;/code&gt; class uses the &lt;code&gt;#each&lt;/code&gt; method we implemented in our &lt;code&gt;LinkedList&lt;/code&gt; class to loop over the object to calculate their results.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module DIYEnumerable
  def count
    result = 0
    each { |element| result += 1 }
    result
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we&amp;#39;ve implemented the &lt;code&gt;#count&lt;/code&gt; method on a new &lt;code&gt;DIYEnumerable&lt;/code&gt; module that we’ll include in our linked list. It starts a counter at zero and calls the &lt;code&gt;#each&lt;/code&gt; method to add one to the counter for every loop. After looping over all elements, the method returns the resulting counter.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module DIYEnumerable
  # ...

  def map
    result = LinkedList.new
    each { |element| result = result &amp;lt;&amp;lt; yield(element) }
    result
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;#map&lt;/code&gt; method is implemented similarly. Instead of keeping a counter, it uses an accumulator, which starts as an empty list. We&amp;#39;ll loop over all elements in the list, and yield the passed block on each element. The result of each yield is appended to the accumulator list.&lt;/p&gt;
&lt;p&gt;The method returns the accumulator after looping over all of the elements in the input list.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LinkedList
  include DIYEnumerable

  #...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After including the &lt;code&gt;DIYEnumerable&lt;/code&gt; in our &lt;code&gt;LinkedList&lt;/code&gt;, we can test our newly added &lt;code&gt;#count&lt;/code&gt; and &lt;code&gt;#map&lt;/code&gt; methods.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;irb&amp;gt; list = LinkedList.new(73, 12, 42)
=&amp;gt; [73, [12, [42]]]
irb&amp;gt; list.count
=&amp;gt; 3
irb&amp;gt; list.map { |element| element * 10 }
=&amp;gt; [420, [120, [730]]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both methods work! The &lt;code&gt;#count&lt;/code&gt; method correctly counts the items in the list, and the &lt;code&gt;#map&lt;/code&gt; method runs a block for each item and returns an updated list.&lt;/p&gt;
&lt;h2&gt;Reversed lists&lt;/h2&gt;
&lt;p&gt;However, the &lt;code&gt;#map&lt;/code&gt; method seems to have reverted the list. That’s understandable, as the &lt;code&gt;#&amp;lt;&amp;lt;&lt;/code&gt; method on our linked list class prepends items to the list instead of appending them, which is a feature of the recursive nature of linked lists.&lt;/p&gt;
&lt;p&gt;For situations where it’s essential that the order of the list is retained, we need a way to reverse the list when mapping over it. Ruby implements &lt;code&gt;Enumerable#reverse_each&lt;/code&gt;, which loops over an object in reverse. That which sounds like an excellent solution to our problem. Sadly, we can’t use an approach like that because our list is nested. We don’t know how long the list is until we loop over it entirely.&lt;/p&gt;
&lt;p&gt;Instead of running the block over the list in reverse, we’ll add a version of &lt;code&gt;#reverse_each&lt;/code&gt; that does this two steps. It first loops over the list to reverse it by creating a new list. After that, it runs the block over the reversed list.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module DIYEnumerable
  # ...

  def reverse_each(&amp;amp;block)
    list = LinkedList.new
    each { |element| list = list &amp;lt;&amp;lt; element }
    list.each(&amp;amp;block)
  end

  def map
    result = LinkedList.new
    reverse_each { |element| result = result &amp;lt;&amp;lt; yield(element) }
    result
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we&amp;#39;ll use &lt;code&gt;#reverse_each&lt;/code&gt; in our &lt;code&gt;#map&lt;/code&gt; method, to make sure it&amp;#39;s returned in the correct order.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;irb&amp;gt; list = LinkedList.new(73, 12, 42)
=&amp;gt; [73, [12, [42]]]
irb&amp;gt; list.map { |element| element * 10 }
=&amp;gt; [730, [120, [420]]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It works! Whenever we call our &lt;code&gt;#map&lt;/code&gt; method on a linked list, we&amp;#39;ll get a new list back in the same order as the original.&lt;/p&gt;
&lt;h2&gt;Chaining Enumeration with Enumerators&lt;/h2&gt;
&lt;p&gt;Through the &lt;code&gt;#each&lt;/code&gt; method implemented on our linked list class and the included &lt;code&gt;DIYEnumerator&lt;/code&gt;, we can now loop both ways and map over linked lists.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;irb&amp;gt; list.each { |x| p x }
73
12
42
irb&amp;gt; list.reverse_each { |x| p x }
42
12
73
irb&amp;gt; list.reverse_each.map { |x| x * 10 }
=&amp;gt; [730, [120, [420]]]
=&amp;gt; [420, [120, [730]]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, what if we need to &lt;em&gt;map&lt;/em&gt; over a list in reverse? Since we now reverse the list before mapping over it, it always returns in the same order as the original list. We&amp;#39;ve already implemented both &lt;code&gt;#reverse_each&lt;/code&gt; and &lt;code&gt;#map&lt;/code&gt;, so we should be able to chain them together to be able to map backwards. Luckily, Ruby&amp;#39;s &lt;code&gt;Enumerator&lt;/code&gt; class can help with that.&lt;/p&gt;
&lt;p&gt;Last time, we made sure to call &lt;code&gt;Kernel#to_enum&lt;/code&gt; if the &lt;code&gt;LinkedList#each&lt;/code&gt; method was called without a block. This allowed for chaining enumerable methods by returning an &lt;code&gt;Enumerator&lt;/code&gt; object. To find out how &lt;a href=&quot;http://ruby-doc.org/core-2.6.3/Enumerator.html&quot;&gt;the &lt;code&gt;Enumerator&lt;/code&gt; class&lt;/a&gt; works, we’ll implement our own version.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class DIYEnumerator
  include DIYEnumerable

  def initialize(object, method)
    @object = object
    @method = method
  end

  def each(&amp;amp;block)
    @object.send(@method, &amp;amp;block)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like Ruby&amp;#39;s &lt;code&gt;Enumerator&lt;/code&gt;, our enumerator class is a wrapper around a method on an object. By bubbling up to the wrapped object, we can chain enumeration methods.&lt;/p&gt;
&lt;p&gt;This works because a &lt;code&gt;DIYEnumerator&lt;/code&gt; instance is enumerable itself. It implements &lt;code&gt;#each&lt;/code&gt; by calling the wrapped object, and includes the &lt;code&gt;DIYEnumerable&lt;/code&gt; module so all enumerable methods can be called on it.&lt;/p&gt;
&lt;p&gt;We’ll return an instance of our &lt;code&gt;DIYEnumerator&lt;/code&gt; class if no block is passed to the &lt;code&gt;LinkedList#each&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LinkedList
  # ...

  def each(&amp;amp;block)
    if block_given?
      yield @head
      @tail.each(&amp;amp;block) if @tail
    else
      DIYEnumerator.new(self, :each)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using our own enumerator, we can now chain enumeration to get the result in the original order without having to pass an empty block to the &lt;code&gt;#reverse_each&lt;/code&gt; method call.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;irb&amp;gt; list = LinkedList.new(73, 12, 42)
=&amp;gt; [73, [12, [42]]]
irb&amp;gt; list.map { |element| element * 10 }
=&amp;gt; [420, [120, [730]]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Eager and lazy enumeration&lt;/h2&gt;
&lt;p&gt;This concludes our peek into the implementation of the &lt;code&gt;Enumerable&lt;/code&gt; module and the &lt;code&gt;Enumerator&lt;/code&gt; class for now. We&amp;#39;ve learned how some of the enumerable methods work and how an enumerator helps chaining enumeration by wrapping an enumerable object.&lt;/p&gt;
&lt;p&gt;There are some issues with our approach, though. By its nature, enumeration is &lt;em&gt;eager&lt;/em&gt;, meaning it loops over the list as soon as one of the enumerable methods is called on it. While that&amp;#39;s fine in most cases, mapping over a list in reverse reverses the list twice, which should be unnecessary.&lt;/p&gt;
&lt;p&gt;To lower the number of loops, we could employ &lt;a href=&quot;http://ruby-doc.org/core-2.6.3/Enumerator/Lazy.html&quot;&gt;&lt;code&gt;Enumerator::Lazy&lt;/code&gt;&lt;/a&gt; to delay looping to the last moment, and have duplicate list reversing cancel itself out.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll have to save that for a future episode, though. Don&amp;#39;t want to miss that, and further expeditions into Ruby&amp;#39;s magical inner workings? Subscribe to the &lt;a href=&quot;/ruby-magic&quot;&gt;Ruby Magic e-mail newsletter&lt;/a&gt;, to get new articles delivered to your inbox as soon as they&amp;#39;re published.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby&#039;s Hidden Gems -Delegator and Forwardable</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/04/30/ruby-magic-hidden-gems-delegator-forwardable.html"/>
    <id>https://blog.appsignal.com/2019/04/30/ruby-magic-hidden-gems-delegator-forwardable.html</id>
    <published>2019-04-30T00:00:00+00:00</published>
    <updated>2019-04-30T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In today&#039;s exploration of Ruby&#039;s standard library, we&#039;re going to look at delegation through Ruby&#039;s Delegator and Forwardable classes.</summary>
    <content type="html">&lt;p&gt;In today&amp;#39;s exploration of the hidden gems in Ruby&amp;#39;s standard library, we&amp;#39;re going to look at delegation.&lt;/p&gt;
&lt;p&gt;Unfortunately, this term—like so many others—has become somewhat muddled over the years and means different things to different people. According to &lt;a href=&quot;https://en.wikipedia.org/wiki/Delegation_(object-oriented_programming)&quot;&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Delegation refers to evaluating a member (property or method) of one object (the receiver) in the context of another original object (the sender). Delegation can be done explicitly, by passing the sending object to the receiving object, which can be done in any object-oriented language; or implicitly, by the member lookup rules of the language, which requires language support for the feature.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, more often than not, people also use the term to describe an object calling the corresponding method of another object without passing itself as an argument, which can more precisely be referred to as &amp;quot;forwarding&amp;quot;.&lt;/p&gt;
&lt;p&gt;With that out of the way, we&amp;#39;ll use &amp;quot;delegation&amp;quot; to describe both of these patterns for the rest of the article.&lt;/p&gt;
&lt;h2&gt;Delegator&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s start our exploration of delegation in Ruby by looking at the standard library&amp;#39;s &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.2/libdoc/delegate/rdoc/Delegator.html&quot;&gt;&lt;code&gt;Delegator&lt;/code&gt;&lt;/a&gt; class which provides several delegation patterns.&lt;/p&gt;
&lt;h3&gt;SimpleDelegator&lt;/h3&gt;
&lt;p&gt;The easiest of these, and the one I&amp;#39;ve encountered most in the wild, is &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.2/libdoc/delegate/rdoc/SimpleDelegator.html&quot;&gt;&lt;code&gt;SimpleDelegator&lt;/code&gt;&lt;/a&gt;, which wraps an object provided via the initializer and then delegates all missing methods to it. Let&amp;#39;s see this in action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;delegate&amp;#39;

User = Struct.new(:first_name, :last_name)

class UserDecorator &amp;lt; SimpleDelegator
  def full_name
    &amp;quot;#{first_name} #{last_name}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we needed to &lt;code&gt;require &amp;#39;delegate&amp;#39;&lt;/code&gt; to make &lt;code&gt;SimpleDelegator&lt;/code&gt; available to our code. We also used a &lt;a href=&quot;https://ruby-doc.org/core-2.6.2/Struct.html&quot;&gt;&lt;code&gt;Struct&lt;/code&gt;&lt;/a&gt; to create a simple &lt;code&gt;User&lt;/code&gt; class with &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt; accessors. We then added &lt;code&gt;UserDecorator&lt;/code&gt; which defines a &lt;code&gt;full_name&lt;/code&gt; method combining the individual name parts into a single string. This is where &lt;code&gt;SimpleDelegator&lt;/code&gt; comes into play: since neither &lt;code&gt;first_name&lt;/code&gt; nor &lt;code&gt;last_name&lt;/code&gt; are defined on the current class, they will instead be called on the wrapped object:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;decorated_user = UserDecorator.new(User.new(&amp;quot;John&amp;quot;, &amp;quot;Doe&amp;quot;))
decorated_user.full_name
#=&amp;gt; &amp;quot;John Doe&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SimpleDelegator&lt;/code&gt; also lets us override delegated methods with &lt;code&gt;super&lt;/code&gt;, calling the corresponding method on the wrapped object. We can use this in our example to only show the initial instead of the full first name:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UserDecorator &amp;lt; SimpleDelegator
  def first_name
    &amp;quot;#{super[0]}.&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;decorated_user.first_name
#=&amp;gt; &amp;quot;J.&amp;quot;
decorated_user.full_name
#=&amp;gt; &amp;quot;J. Doe&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Delegator&lt;/h3&gt;
&lt;p&gt;While reading the above examples, did you wonder how our &lt;code&gt;UserDecorator&lt;/code&gt; knew which object to delegate to? The answer to that lies in &lt;code&gt;SimpleDelegator&lt;/code&gt;&amp;#39;s parent class—&lt;code&gt;Delegator&lt;/code&gt;. This is an abstract base class for defining custom delegation schemes by providing implementations for &lt;code&gt;__getobj__&lt;/code&gt; and &lt;code&gt;__setobj__&lt;/code&gt; to get and set the delegation target respectively. Using this knowledge, we can easily build our own version of &lt;code&gt;SimpleDelegator&lt;/code&gt; for demonstration purposes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyDelegator &amp;lt; Delegator
  attr_accessor :wrapped
  alias_method :__getobj__, :wrapped

  def initialize(obj)
    @wrapped = obj
  end
end

class UserDecorator &amp;lt; MyDelegator
  def full_name
    &amp;quot;#{first_name} #{last_name}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This differs slightly from &lt;code&gt;SimpleDelegator&lt;/code&gt;&amp;#39;s real implementation which calls &lt;code&gt;__setobj__&lt;/code&gt; in its &lt;code&gt;initialize&lt;/code&gt; method. Since our custom delegator class has no need for it, we completely left out that method.&lt;/p&gt;
&lt;p&gt;This should work exactly like our previous example; and indeed it does:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;UserDecorator.superclass
#=&amp;gt; MyDelegator &amp;lt; Delegator
decorated_user = UserDecorator.new(User.new(&amp;quot;John&amp;quot;, &amp;quot;Doe&amp;quot;))
decorated_user.full_name
#=&amp;gt; &amp;quot;John Doe&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DelegateMethod&lt;/h3&gt;
&lt;p&gt;The last delegation pattern &lt;code&gt;Delegate&lt;/code&gt; provides for us is the somewhat oddly named &lt;code&gt;Object.DelegateClass&lt;/code&gt; method. This generates and returns a delegator class for a specific class, which we can then inherit from:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;  class MyClass &amp;lt; DelegateClass(ClassToDelegateTo)
    def initialize
      super(obj_of_ClassToDelegateTo)
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;While this may look confusing at first—especially the fact that the right-hand side of inheritance can contain arbitrary Ruby code—it actually follows the patterns we explored previously, i.e. it&amp;#39;s similar to inheriting from &lt;code&gt;SimpleDelegator&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ruby&amp;#39;s standard library uses this feature to define its &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.2/libdoc/tempfile/rdoc/Tempfile.html&quot;&gt;&lt;code&gt;Tempfile&lt;/code&gt;&lt;/a&gt; class which delegates much of its work to the &lt;a href=&quot;https://ruby-doc.org/core-2.6.2/File.html&quot;&gt;&lt;code&gt;File&lt;/code&gt;&lt;/a&gt; class while setting up some special rules regarding storage location and file deletion. We could use the same mechanism to set up a custom &lt;code&gt;Logfile&lt;/code&gt; class like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Logfile &amp;lt; DelegateClass(File)
  MODE = File::WRONLY|File::CREAT|File::APPEND

  def initialize(basename, logdir = &amp;#39;/var/log&amp;#39;)
    # Create logfile in location specified by logdir
    path = File.join(logdir, basename)
    logfile = File.open(path, MODE, 0644)

    # This will call Delegator&amp;#39;s initialize method, so below this point
    # we can call any method from File on our Logfile instances.
    super(logfile)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Forwardable&lt;/h2&gt;
&lt;p&gt;Interestingly enough, Ruby&amp;#39;s standard library provides us with another library for delegation in the form of the &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.2/libdoc/forwardable/rdoc/Forwardable.html&quot;&gt;&lt;code&gt;Forwardable&lt;/code&gt;&lt;/a&gt; module and its &lt;code&gt;def_delegator&lt;/code&gt; and &lt;code&gt;def_delegators&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s rewrite our original &lt;code&gt;UserDecorator&lt;/code&gt; example with &lt;code&gt;Forwardable&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;forwardable&amp;#39;

User = Struct.new(:first_name, :last_name)

class UserDecorator
  extend Forwardable
  def_delegators :@user, :first_name, :last_name

  def initialize(user)
    @user = user
  end

  def full_name
    &amp;quot;#{first_name} #{last_name}&amp;quot;
  end
end

decorated_user = UserDecorator.new(User.new(&amp;quot;John&amp;quot;, &amp;quot;Doe&amp;quot;))
decorated_user.full_name
#=&amp;gt; &amp;quot;John Doe&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most noticeable difference is that delegation isn&amp;#39;t automatically provided via &lt;code&gt;method_missing&lt;/code&gt;, but instead, needs to be explicitly declared for each method we want to forward. This allows us to &amp;quot;hide&amp;quot; any methods of the wrapped object we don&amp;#39;t want to expose to our clients, which gives us more control over our public interface and is the main reason I generally prefer &lt;code&gt;Forwardable&lt;/code&gt; over &lt;code&gt;SimpleDelegator&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another nice feature of &lt;code&gt;Forwardable&lt;/code&gt; is the ability to rename delegated methods via &lt;code&gt;def_delegator&lt;/code&gt;, which accepts an optional third argument that specifies the desired alias:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UserDecorator
  extend Forwardable
  def_delegator :@user, :first_name, :personal_name
  def_delegator :@user, :last_name, :family_name

  def initialize(user)
    @user = user
  end

  def full_name
    &amp;quot;#{personal_name} #{family_name}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above &lt;code&gt;UserDecorator&lt;/code&gt; only exposes the aliased &lt;code&gt;personal_name&lt;/code&gt; and &lt;code&gt;family_name&lt;/code&gt; methods, while still forwarding to the &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt; of the wrapped &lt;code&gt;User&lt;/code&gt; object:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;decorated_user = UserDecorator.new(User.new(&amp;quot;John&amp;quot;, &amp;quot;Doe&amp;quot;))
decorated_user.first_name
#=&amp;gt; NoMethodError: undefined method `first_name&amp;#39; for #&amp;lt;UserDecorator:0x000000010f995cb8&amp;gt;
decorated_user.personal_name
#=&amp;gt; &amp;quot;John&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This feature can come in quite handy at times. I&amp;#39;ve successfully used it in the past for things like migrating code between libraries with similar interfaces but different expectations regarding method names.&lt;/p&gt;
&lt;h2&gt;Outside the Standard Library&lt;/h2&gt;
&lt;p&gt;Despite the existing delegation solutions in the standard library, the Ruby community has developed several alternatives over the years and we&amp;#39;ll explore two of them next.&lt;/p&gt;
&lt;h3&gt;delegate&lt;/h3&gt;
&lt;p&gt;Considering Rails&amp;#39; popularity, its &lt;a href=&quot;https://apidock.com/rails/Module/delegate&quot;&gt;&lt;code&gt;delegate&lt;/code&gt;&lt;/a&gt; method may well be the most commonly used form of delegation used by Ruby developers. Here&amp;#39;s how we could use it to rewrite our trusty old &lt;code&gt;UserDecorator&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# In a real Rails app this would most likely be a subclass of ApplicationRecord
User = Struct.new(:first_name, :last_name)

class UserDecorator
  attr_reader :user
  delegate :first_name, :last_name, to: :user

  def initialize(user)
    @user = user
  end

  def full_name
    &amp;quot;#{first_name} #{last_name}&amp;quot;
  end
end

decorated_user = UserDecorator.new(User.new(&amp;quot;John&amp;quot;, &amp;quot;Doe&amp;quot;))
decorated_user.full_name
#=&amp;gt; &amp;quot;John Doe&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is quite similar to &lt;code&gt;Forwardable&lt;/code&gt;, but we don&amp;#39;t need to use &lt;code&gt;extend&lt;/code&gt; since &lt;code&gt;delegate&lt;/code&gt; is directly defined on &lt;code&gt;Module&lt;/code&gt; and therefore available in every class or module body (for better or worse, you decide). However, &lt;code&gt;delegate&lt;/code&gt; has a few neat tricks up its sleeve. First, there&amp;#39;s the &lt;code&gt;:prefix&lt;/code&gt; option which will prefix the delegated method names with the name of the object we&amp;#39;re delegating to. So,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;delegate :first_name, :last_name, to: :user, prefix: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will generate &lt;code&gt;user_first_name&lt;/code&gt; and &lt;code&gt;user_last_name&lt;/code&gt; methods. Alternatively we can provide a custom prefix:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;delegate :first_name, :last_name, to: :user, prefix: :account
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now access the different parts of the user&amp;#39;s name as &lt;code&gt;account_first_name&lt;/code&gt; and &lt;code&gt;account_last_name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another interesting option of &lt;code&gt;delegate&lt;/code&gt; is its &lt;code&gt;:allow_nil&lt;/code&gt; option. If the object we delegate to is currently &lt;code&gt;nil&lt;/code&gt;—for example because of an unset &lt;code&gt;ActiveRecord&lt;/code&gt; relation—we would usually end up with a &lt;code&gt;NoMethodError&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;decorated_user = UserDecorator.new(nil)
decorated_user.first_name
#=&amp;gt; Module::DelegationError: UserDecorator#first_name delegated to @user.first_name, but @user is nil
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, with the &lt;code&gt;:allow_nil&lt;/code&gt; option, this call will succeed and return &lt;code&gt;nil&lt;/code&gt; instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class UserDecorator
  delegate :first_name, :last_name, to: :user, allow_nil: true

  ...
end

decorated_user = UserDecorator.new(nil)
decorated_user.first_name
#=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Casting&lt;/h3&gt;
&lt;p&gt;The last delegation option we&amp;#39;ll be looking at is Jim Gay&amp;#39;s &lt;a href=&quot;https://github.com/saturnflyer/casting&quot;&gt;&lt;code&gt;Casting&lt;/code&gt;&lt;/a&gt; gem, which allows developers to &amp;quot;delegate methods in Ruby and preserve self&amp;quot;. This is probably the closest to the strict definition of delegation, as it uses Ruby&amp;#39;s dynamic nature to temporarily rebind the receiver of a method call, akin to this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;UserDecorator.instance_method(:full_name).bind(user).call
#=&amp;gt; &amp;quot;John Doe&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most interesting aspect of this is that developers can add behavior to objects, without changing their superclass hierarchies.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;casting&amp;#39;

User = Struct.new(:first_name, :last_name)

module UserDecorator
  def full_name
    &amp;quot;#{first_name} #{last_name}&amp;quot;
  end
end

user = User.new(&amp;quot;John&amp;quot;, &amp;quot;Doe&amp;quot;)
user.extend(Casting::Client)
user.delegate(:full_name, UserDecorator)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we extended &lt;code&gt;user&lt;/code&gt; with &lt;code&gt;Casting::Client&lt;/code&gt;, which gives us access to the &lt;code&gt;delegate&lt;/code&gt; method. Alternatively, we could have used &lt;code&gt;include Casting::Client&lt;/code&gt; inside the &lt;code&gt;User&lt;/code&gt; class to give this ability to all instances.&lt;/p&gt;
&lt;p&gt;Additionally, &lt;code&gt;Casting&lt;/code&gt; provides options for temporarily adding behaviors for the lifetime of a block or until manually removed again. For this to work, we first need to enable delegation of missing methods:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;user.delegate_missing_methods
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add behavior for the duration of a single block, we can then use &lt;code&gt;Casting&lt;/code&gt;&amp;#39;s &lt;code&gt;delegating&lt;/code&gt; class method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Casting.delegating(user =&amp;gt; UserDecorator) do
  user.full_name #=&amp;gt; &amp;quot;John Doe&amp;quot;
end

user.full_name
#NoMethodError: undefined method `full_name&amp;#39; for #&amp;lt;struct User first_name=&amp;quot;John&amp;quot;, last_name=&amp;quot;Doe&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, we can add behavior until we explicitly call &lt;code&gt;uncast&lt;/code&gt; again:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;user.cast_as(UserDecorator)
user.full_name
#=&amp;gt; &amp;quot;John Doe&amp;quot;
user.uncast
NoMethodError: undefined method `full_name&amp;#39; for #&amp;lt;struct User first_name=&amp;quot;John&amp;quot;, last_name=&amp;quot;Doe&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While slightly more complex than the other presented solutions, &lt;code&gt;Casting&lt;/code&gt; provides a lot of control and Jim demonstrates its various uses and more in his &lt;a href=&quot;http://www.clean-ruby.com/&quot;&gt;Clean Ruby&lt;/a&gt; book.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Delegation and method forwarding are useful patterns for dividing responsibilities between related objects. In plain Ruby projects, both &lt;code&gt;Delegator&lt;/code&gt; and &lt;code&gt;Forwardable&lt;/code&gt; can be used, whereas Rails code tends to gravitate towards its &lt;code&gt;delegate&lt;/code&gt; method. For maximum control on what is delegated, the &lt;code&gt;Casting&lt;/code&gt; gem is an excellent choice, though it&amp;#39;s slightly more complex than the other solutions.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Learning by building, a Background Processing System in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/04/02/background-processing-system-in-ruby.html"/>
    <id>https://blog.appsignal.com/2019/04/02/background-processing-system-in-ruby.html</id>
    <published>2019-04-02T00:00:00+00:00</published>
    <updated>2019-04-02T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this edition of Ruby Magic, we&#039;ll implement our very own background processing system to offload potentially slow tasks as a peek into background job libraries like Sidekiq.</summary>
    <content type="html">&lt;p&gt;In today&amp;#39;s post, we are going to implement a naive background processing system for fun! We might learn some things along the way as a peek into the internals of popular background processing systems like &lt;a href=&quot;http://sidekiq.org&quot;&gt;Sidekiq&lt;/a&gt;. The product of this fun is by no means intended for production use.&lt;/p&gt;
&lt;p&gt;Let’s imagine we have a task in our application that loads one or more websites and extracts their titles. As we don’t have any influence on the performance of these websites, we’d like to perform the task outside our main thread (or the current request—if we’re building a web application), but in the background.&lt;/p&gt;
&lt;h2&gt;Encapsulating a Task&lt;/h2&gt;
&lt;p&gt;Before we get into background processing, let’s build a service object to perform the task at hand. We’ll use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.2/libdoc/open-uri/rdoc/OpenURI.html&quot;&gt;OpenURI&lt;/a&gt; and &lt;a href=&quot;https://nokogiri.org&quot;&gt;Nokogiri&lt;/a&gt; to extract the contents of the title tag.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;open-uri&amp;#39;
require &amp;#39;nokogiri&amp;#39;

class TitleExtractorService
  def call(url)
    document = Nokogiri::HTML(open(url))
    title = document.css(&amp;#39;html &amp;gt; head &amp;gt; title&amp;#39;).first.content
    puts title.gsub(/[[:space:]]+/, &amp;#39; &amp;#39;).strip
  rescue
    puts &amp;quot;Unable to find a title for #{url}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling the service prints the title of the given URL.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;TitleExtractorService.new.call(&amp;#39;https://appsignal.com&amp;#39;)
# AppSignal: Application Performance Monitoring for Ruby on Rails and Elixir
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works as expected, but let’s see if we can improve the syntax a little to make it look and feel a bit more like other background processing systems. By creating a &lt;code&gt;Magique::Worker&lt;/code&gt; module, we can add some syntactic sugar to the service object.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magique
  module Worker
    def self.included(base)
      base.extend(ClassMethods)
    end

    module ClassMethods
      def perform_now(*args)
        new.perform(*args)
      end
    end

    def perform(*)
      raise NotImplementedError
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The module adds a &lt;code&gt;perform&lt;/code&gt; method to the worker instance and a &lt;code&gt;perform_now&lt;/code&gt; method to the worker class to make the invocation a bit better.&lt;/p&gt;
&lt;p&gt;Let’s include the module into our service object. While we’re at it, let’s also rename it to &lt;code&gt;TitleExtractorWorker&lt;/code&gt; and change the &lt;code&gt;call&lt;/code&gt; method to &lt;code&gt;perform&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class TitleExtractorWorker
  include Magique::Worker

  def perform(url)
    document = Nokogiri::HTML(open(url))
    title = document.css(&amp;#39;html &amp;gt; head &amp;gt; title&amp;#39;).first.content
    puts title.gsub(/[[:space:]]+/, &amp;#39; &amp;#39;).strip
  rescue
    puts &amp;quot;Unable to find a title for #{url}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The invocation still has the same result, but it’s a bit clearer what&amp;#39;s going on.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;TitleExtractorWorker.perform_now(&amp;#39;https://appsignal.com&amp;#39;)
# AppSignal: Application Performance Monitoring for Ruby on Rails and Elixir
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementing Asynchronous Processing&lt;/h2&gt;
&lt;p&gt;Now that we have the title extraction working, we can grab all titles from past Ruby Magic articles. To do this, let’s assume we have a &lt;code&gt;RUBYMAGIC&lt;/code&gt; constant with a list of all the URLs of past articles.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;RUBYMAGIC.each do |url|
  TitleExtractorWorker.perform_now(url)
end

# Unraveling Classes, Instances and Metaclasses in Ruby | AppSignal Blog
# Bindings and Lexical Scope in Ruby | AppSignal Blog
# Building a Ruby C Extension From Scratch | AppSignal Blog
# Closures in Ruby: Blocks, Procs and Lambdas | AppSignal Blog
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We get the titles of past articles, but it takes a while to extract them all. That’s because we wait until each request is completed before moving on to the next one.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Let’s improve that by introducing a &lt;code&gt;perform_async&lt;/code&gt; method to our worker module. To speed things up, it creates a new thread for each URL.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magique
  module Worker
    module ClassMethods
      def perform_async(*args)
        Thread.new { new.perform(*args) }
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After changing the invocation to &lt;code&gt;TitleExtractorWorker.perform_async(url)&lt;/code&gt;, we get all the titles almost at once. However, this also means that we’re opening more than 20 connections to the Ruby Magic blog at once. (&lt;em&gt;Sorry for messing with your blog, folks!&lt;/em&gt; 😅)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you’re following along with your own implementation and testing this outside of a long-running process (like a web server), don’t forget to add something like &lt;code&gt;loop { sleep 1 }&lt;/code&gt; to the end of your script to make sure the process doesn’t immediately terminate.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Queueing up Tasks&lt;/h2&gt;
&lt;p&gt;With the approach of creating a new thread for every invocation, we’ll eventually hit resource limits (both on our side and on the websites we are accessing). As we’d like to be nice citizens, let’s change the implementation to something that is asynchronous but doesn’t feel like a denial-of-service attack.&lt;/p&gt;
&lt;p&gt;A common way to solve this problem is to use the producer/consumer pattern. One or more producers push tasks onto a queue while one or more consumers take tasks from the queue and process them.&lt;/p&gt;
&lt;p&gt;A queue is basically a list of elements. In theory, a simple array would do the job. However, as we’re dealing with concurrency, we need to make sure that only one producer or consumer can access the queue at a time. If we aren’t careful about this, things will end in chaos—just like two people trying to squeeze through a door at once.&lt;/p&gt;
&lt;p&gt;This problem is known as the &lt;a href=&quot;https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem&quot;&gt;producer-consumer problem&lt;/a&gt; and there are multiple solutions to it. Luckily, it is a very common problem and Ruby ships with a proper &lt;code&gt;Queue&lt;/code&gt; implementation that we can use without having to worry about thread synchronization.&lt;/p&gt;
&lt;p&gt;To use it, let’s make sure both producers and consumers can access the queue. We do this by adding a class method to our &lt;code&gt;Magique&lt;/code&gt; module and assigning an instance of &lt;code&gt;Queue&lt;/code&gt; to it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magique
  def self.backend
    @backend
  end

  def self.backend=(backend)
    @backend = backend
  end
end

Magique.backend = Queue.new
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we change our &lt;code&gt;perform_async&lt;/code&gt; implementation to push a task onto the queue instead of creating its own new thread. A task is represented as a hash including a reference to the worker class as well as the arguments passed to the &lt;code&gt;perform_async&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magique
  module Worker
    module ClassMethods
      def perform_async(*args)
        Magique.backend.push(worker: self, args: args)
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, we’re done with the producer side of things. Next, let’s take a look at the consumer side.&lt;/p&gt;
&lt;p&gt;Each consumer is a separate thread that takes tasks from the queue and performs them. Instead of stopping after one task, like the thread, the consumer then takes another task from the queue and performs it, and so on. Here’s a basic implementation of a consumer called &lt;code&gt;Magique::Processor&lt;/code&gt;. Each processor creates a new thread that loops infinitely. For every iteration, it tries to grab a new task from the queue, creates a new instance of the worker class, and calls its &lt;code&gt;perform&lt;/code&gt; method with the given arguments.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Magique
  class Processor
    def self.start(concurrency = 1)
      concurrency.times { |n| new(&amp;quot;Processor #{n}&amp;quot;) }
    end

    def initialize(name)
      thread = Thread.new do
        loop do
          payload = Magique.backend.pop
          worker_class = payload[:worker]
          worker_class.new.perform(*payload[:args])
        end
      end

      thread.name = name
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition to the processing loop, we add a convenience method called &lt;code&gt;Magique::Processor.start&lt;/code&gt;. This allows us to spin up multiple processors at once. While naming the thread isn’t really necessary, it will allow us to see if things are actually working as expected.&lt;/p&gt;
&lt;p&gt;Let’s adjust the output of our &lt;code&gt;TitleExtractorWorker&lt;/code&gt; to include the name of the current thread.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;puts &amp;quot;[#{Thread.current.name}] #{title.gsub(/[[:space:]]+/, &amp;#39; &amp;#39;).strip}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test our background processing setup, we first need to spin up a set of processors before enqueueing our tasks.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Magique.backend = Queue.new
Magique::Processor.start(5)

RUBYMAGIC.each do |url|
  TitleExtractorWorker.perform_async(url)
end

# [Processor 3] Bindings and Lexical Scope in Ruby | AppSignal Blog
# [Processor 4] Building a Ruby C Extension From Scratch | AppSignal Blog
# [Processor 1] Unraveling Classes, Instances and Metaclasses in Ruby | AppSignal Blog
# [Processor 0] Ruby&amp;#39;s Hidden Gems, StringScanner | AppSignal Blog
# [Processor 2] Fibers and Enumerators in Ruby: Turning Blocks Inside Out | AppSignal Blog
# [Processor 4] Closures in Ruby: Blocks, Procs and Lambdas | AppSignal Blog
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When this is run, we still get the titles of all articles. While it’s not as fast as just using a separate thread for every task, it’s still faster than the initial implementation that had no background processing. Thanks to the added processor names, we can also confirm that all processors are working through the queue. By tweaking the number of concurrent processors, it’s possible to find a balance between processing speed and existing resource limitations.&lt;/p&gt;
&lt;h2&gt;Expanding to Multiple Processes and Machines&lt;/h2&gt;
&lt;p&gt;So far, the current implementation of our background processing system works well enough. It’s still limited to the same process, though. Resource-hungry tasks will still affect the performance of the entire process. As a final step, let’s look at distributing the workload across multiple processes and maybe even multiple machines.&lt;/p&gt;
&lt;p&gt;The queue is the only connection between producers and consumers. Right now, it’s using an in-memory implementation. Let’s take more inspiration from Sidekiq and implement a queue using &lt;a href=&quot;https://redis.io&quot;&gt;Redis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Redis has support for lists that allow us to push and fetch tasks from. Additionally, the Redis Ruby gem is thread-safe and the Redis commands to modify lists are &lt;a href=&quot;https://en.wikipedia.org/wiki/Linearizability&quot;&gt;atomic&lt;/a&gt;. These properties make it possible to use it for our asynchronous background processing system without running into synchronization problems.&lt;/p&gt;
&lt;p&gt;Let’s create a Redis backed queue that implements the &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;shift&lt;/code&gt; methods just like the &lt;code&gt;Queue&lt;/code&gt; we used previously.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;json&amp;#39;
require &amp;#39;redis&amp;#39;

module Magique
  module Backend
    class Redis
      def initialize(connection = ::Redis.new)
        @connection = connection
      end

      def push(job)
        @connection.lpush(&amp;#39;magique:queue&amp;#39;, JSON.dump(job))
      end

      def shift
        _queue, job = @connection.brpop(&amp;#39;magique:queue&amp;#39;)
        payload = JSON.parse(job, symbolize_names: true)
        payload[:worker] = Object.const_get(payload[:worker])
        payload
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As Redis doesn’t know anything about Ruby objects, we have to serialize our tasks into JSON before storing them in the database using the &lt;code&gt;lpush&lt;/code&gt; command that adds an element to the front of the list.&lt;/p&gt;
&lt;p&gt;To fetch a task from the queue, we’re using the &lt;code&gt;brpop&lt;/code&gt; command, which gets the last element from a list. If the list is empty, it’ll block until a new element is available. This is a nice way to pause our processors when no tasks are available. Finally, after getting a task out of Redis, we have to look up the real Ruby class based on the name of the worker using &lt;code&gt;Object.const_get&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As a final step, let’s split things up into multiple processes. On the producer side of things, the only thing we have to do is change the backend to our newly implemented Redis queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# ...

Magique.backend = Magique::Backend::Redis.new

RUBYMAGIC.each do |url|
  TitleExtractorWorker.perform_async(url)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the consumer side of things, we can get away with a few lines like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# ...

Magique.backend = Magique::Backend::Redis.new
Magique::Processor.start(5)

loop { sleep 1 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When executed, the consumer process will wait for new work to arrive in the queue. Once we start the producer process that pushes tasks into the queue, we can see that they get processed immediately.&lt;/p&gt;
&lt;h2&gt;Enjoy Responsibly and Don’t Use This in Production&lt;/h2&gt;
&lt;p&gt;While we kept it far from a real world setup you would use in production (so don&amp;#39;t!), we took a few steps in building a background processor. We started by making a process run as a background service. Then we made it async and used &lt;code&gt;Queue&lt;/code&gt; to solve the producer-consumer problem. Then we expanded the process to multiple processes or machines using Redis rather then an in-memory implementation.&lt;/p&gt;
&lt;p&gt;As mentioned before, this is a simplified implementation of a background processing system. There are a lot of things missing and not explicitly dealt with. These include (but are not limited to) error handling, multiple queues, scheduling, connection pooling, and signal handling.&lt;/p&gt;
&lt;p&gt;Nonetheless, we had fun writing this and hope you enjoyed a peek under the hood of a background processing system. Perhaps you even took away a thing or two.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby&#039;s Hidden Gems, StringScanner</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/03/05/stringscanner.html"/>
    <id>https://blog.appsignal.com/2019/03/05/stringscanner.html</id>
    <published>2019-03-05T00:00:00+00:00</published>
    <updated>2019-03-05T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Ruby comes with an excellent standard library. Parts of which are unknown, almost hidden gems, like today&#039;s subject Stringscanner</summary>
    <content type="html">&lt;p&gt;Ruby is not only a fun language, it also comes with an excellent standard library. Some of which are not that known, and are almost hidden Gems. Today guest writer Michael Kohl highlights a favorite: Stringscanner.&lt;/p&gt;
&lt;h1&gt;Ruby&amp;#39;s hidden Gems: StringScanner&lt;/h1&gt;
&lt;p&gt;One can get quite far without having to resort to installing third party gems, from data structures like &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/ostruct/rdoc/OpenStruct.html&quot;&gt;OpenStruct&lt;/a&gt; and &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/set/rdoc/Set.html&quot;&gt;Set&lt;/a&gt; over &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/csv/rdoc/CSV.html&quot;&gt;CSV parsing&lt;/a&gt; to &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/benchmark/rdoc/Benchmark.html&quot;&gt;benchmarking&lt;/a&gt;. However, there are some less well-known libraries available in Ruby&amp;#39;s standard installation that can be very useful, one of which is &lt;code&gt;StringScanner&lt;/code&gt; which according to the &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html&quot;&gt;documentation&lt;/a&gt; &lt;em&gt;&amp;quot;provides lexical scanning operations on a string&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Scanning and parsing&lt;/h2&gt;
&lt;p&gt;So what does &amp;quot;lexical scanning&amp;quot; mean exactly? Essentially it describes the process of taking an input string and extracting meaningful bits of information from it, following certain rules. For example, this can be seen at the first stage of a compiler which takes an expression like &lt;code&gt;2 + 1&lt;/code&gt; as input and turns it into the following sequence of tokens:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[{ number: &amp;quot;1&amp;quot; }, {operator: &amp;quot;+&amp;quot;}, { number: &amp;quot;1&amp;quot;}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lexical scanners are usually implemented as &lt;a href=&quot;https://en.wikipedia.org/wiki/Finite-state_machine&quot;&gt;finite-state automata&lt;/a&gt; and there are several well-known tools available that can generate them for us (e.g. &lt;a href=&quot;https://www.antlr.org/&quot;&gt;ANTLR&lt;/a&gt; or &lt;a href=&quot;https://www.colm.net/open-source/ragel/&quot;&gt;Ragel&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;However, sometimes our parsing needs aren&amp;#39;t that elaborate, and a simpler library like the &lt;a href=&quot;https://en.wikipedia.org/wiki/Regular_expression&quot;&gt;regular expression&lt;/a&gt; based &lt;code&gt;StringScanner&lt;/code&gt; can come in very handy in such situations. It works by remembering the location of a so-called &lt;em&gt;scan pointer&lt;/em&gt; which is nothing more than an index into the string. The scanning process then tries to match the code right after the scan pointer with the provided expression. Apart from matching operations, &lt;code&gt;StringScanner&lt;/code&gt; also provides methods for moving the scan pointer (moving forwards or backwards through the string), looking ahead (seeing what&amp;#39;s next without modifying the scan pointer just yet) as well as finding out where in the string we currently are (is it the beginning or end of a line/the entire string etc).&lt;/p&gt;
&lt;h2&gt;Parsing Rails Logs&lt;/h2&gt;
&lt;p&gt;Enough theory, let&amp;#39;s see &lt;code&gt;StringScanner&lt;/code&gt; in action. The following example will take a Rails&amp;#39; log entry like the one below,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;log_entry = &amp;lt;&amp;lt;EOS
Started GET &amp;quot;/&amp;quot; for 127.0.0.1 at 2017-08-20 20:53:10 +0900
Processing by HomeController#index as HTML
  Rendered text template within layouts/application (0.0ms)
  Rendered layouts/_assets.html.erb (2.0ms)
  Rendered layouts/_top.html.erb (2.6ms)
  Rendered layouts/_about.html.erb (0.3ms)
  Rendered layouts/_google_analytics.html.erb (0.4ms)
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
EOS
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and parse it into the following hash:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{
  method: &amp;quot;GET&amp;quot;,
  path: &amp;quot;/&amp;quot;
  ip: &amp;quot;127.0.0.1&amp;quot;,
  timestamp: &amp;quot;2017-08-20 20:53:10 +0900&amp;quot;,
  success: true,
  response_code: &amp;quot;200&amp;quot;,
  duration: &amp;quot;79ms&amp;quot;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;NB: While this makes for a good example for &lt;code&gt;StringScanner&lt;/code&gt; a real application would be better off using &lt;a href=&quot;https://github.com/roidrage/lograge&quot;&gt;Lograge&lt;/a&gt; and its JSON log formatter.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In order to use &lt;code&gt;StringScanner&lt;/code&gt; we first need to require it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;strscan&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After this we can initialize a new instance by passing the log entry as an argument to the constructor. At the same time we&amp;#39;ll also define an empty hash to hold the result of our parsing efforts:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner = StringScanner.new(log_entry)
log = {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now use the scanner&amp;#39;s &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-pos&quot;&gt;pos&lt;/a&gt; method to get the current location of our scan pointer. As expected, the result is &lt;code&gt;0&lt;/code&gt;, the first character of the string:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner.pos #=&amp;gt; 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s visualize this so the process will be easier to follow along:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/&amp;quot; for 127.0.0.1 at 2017-08-20 20:53:10 +0900
^
...
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;For further introspection of the scanner&amp;#39;s state we can use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-beginning_of_line-3F&quot;&gt;&lt;code&gt;beginning_of_line?&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-eos-3F&quot;&gt;&lt;code&gt;eos?&lt;/code&gt;&lt;/a&gt; to confirm that the scan pointer currently is at the beginning of a line and that we have not yet fully consumed our input:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner.beginning_of_line? #=&amp;gt; true
scanner.eos? #=&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first bit of information we want to extract is the HTTP request method, which can be found right after the word &amp;quot;Started&amp;quot; followed by a space. We can use the scanner&amp;#39;s appropriately named &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-skip&quot;&gt;skip&lt;/a&gt; method to advance the scan pointer, which will return the number of ignored characters, which in our case is 8. Additionally we can use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-matched-3F&quot;&gt;matched?&lt;/a&gt; to confirm that everything worked as expected:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner.skip(/Started /) #=&amp;gt; 8
scanner.matched? #=&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scan pointer is now right before the request method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/&amp;quot; for 127.0.0.1 at 2017-08-20 20:53:10 +0900
       ^
...
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-scan_until&quot;&gt;scan_until&lt;/a&gt; to extract the actual value, which returns the entire regular expression match. Since the request method is all in uppercase, we can use a simple character class and the &lt;code&gt;+&lt;/code&gt; operator which matches one or characters:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;log[:method] = scanner.scan_until(/[A-Z]+/) #=&amp;gt; &amp;quot;GET&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After this operation the scan pointer will be at the final &amp;quot;T&amp;quot; of the word &amp;quot;GET&amp;quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/&amp;quot; for 127.0.0.1 at 2017-08-20 20:53:10 +0900
          ^
...
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To extract the requested path, we will therefore need to skip one space and then extract everything enclosed in double quotes. There are several ways to achieve this, one of them is via a capture group (the part of the Regular expression included in parenthesis, i.e. &lt;code&gt;(.+)&lt;/code&gt;) which matches one or more of any character:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner.scan(/\s&amp;quot;(.+)&amp;quot;/) #=&amp;gt; &amp;quot; \&amp;quot;/\&amp;quot;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, we will not be using the return value of this &lt;code&gt;scan&lt;/code&gt; operation directly, but instead use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-captures&quot;&gt;captures&lt;/a&gt; to get the value of the first capture group instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;log[:path] =  scanner.captures.first #=&amp;gt; &amp;quot;/&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We successfully extracted the path and the scan pointer is now at the closing double quote:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/&amp;quot; for 127.0.0.1 at 2017-08-20 20:53:10 +0900
              ^
...
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To parse the IP address from the log, we once again use &lt;code&gt;skip&lt;/code&gt; to ignore the string &amp;quot;for&amp;quot; surrounded by spaces and then use &lt;code&gt;scan_until&lt;/code&gt; to match one or more non whitespace characters (&lt;code&gt;\s&lt;/code&gt; is the character class representing whitespace and &lt;code&gt;[^\s]&lt;/code&gt; is its negation):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner.skip(/ for /) #=&amp;gt; 5
log[:ip] = scanner.scan_until(/[^\s]+/) #=&amp;gt; &amp;quot;127.0.0.1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can you tell where the scan pointer will be now? Think about it for a moment and then compare your answer to the solution:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/&amp;quot; for 127.0.0.1 at 2017-08-20 20:53:10 +0900
                            ^
...
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parsing the timestamp should feel very familiar by now. First we use trusty old &lt;code&gt;skip&lt;/code&gt; to ignore the literal string &lt;code&gt;&amp;quot; at &amp;quot;&lt;/code&gt; and then use &lt;code&gt;scan_until&lt;/code&gt; to read until the end of the current line, which is represented by &lt;code&gt;$&lt;/code&gt; in regular expressions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner.skip(/ at /) #=&amp;gt; 4
log[:timestamp] = scanner.scan_until(/$/) #=&amp;gt; &amp;quot;2017-08-20 20:53:10 +0900&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next piece of information we&amp;#39;re interested in is the HTTP status code on the last line, so we&amp;#39;ll use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-skip_until&quot;&gt;skip_until&lt;/a&gt; to take us all the way to the space after the word &amp;quot;Completed&amp;quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner.skip_until(/Completed /) #=&amp;gt; 296
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the name suggests this works similarly to &lt;code&gt;scan_until&lt;/code&gt; but instead of returning the matched string it returns the number of skipped over characters. This puts the scan pointer right in front of the HTTP status code we&amp;#39;re interested in.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/&amp;quot; for 127.0.0.1 at 2017-08-20 20:53:10 +0900
...
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
         ^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now before we scan the actual HTTP response code, wouldn&amp;#39;t it be nice if we could tell if the HTTP response code denotes a success (for the sake of this example any code in the 2xx range) or failure (all other ranges)? To achieve this we will make use of &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-peek&quot;&gt;peek&lt;/a&gt; to look at the next character, without actually moving the scan pointer.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;log[:success] = scanner.peek(1) == &amp;quot;2&amp;quot; #=&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html#method-i-scan&quot;&gt;scan&lt;/a&gt; to read the next three characters, represented by the regular expression &lt;code&gt;/\d{3}/&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;log[:response_code] = scanner.scan(/\d{3}/) #=&amp;gt; &amp;quot;200&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once again the scan pointer will be right at the end of the previously matched regular expression:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/&amp;quot; for 127.0.0.1 at 2017-08-20 20:53:10 +0900
...
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
            ^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last bit of information we want to extract from our log entry is the execution time in milliseconds, which can be achieved by &lt;code&gt;skip&lt;/code&gt;ping over the string &lt;code&gt;&amp;quot; OK in &amp;quot;&lt;/code&gt; and then reading everything up to and including the literal string &lt;code&gt;&amp;quot;ms&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;scanner.skip(/ OK in /) #=&amp;gt; 7
log[:duration] = scanner.scan_until(/ms/) #=&amp;gt; &amp;quot;79ms&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And with that last bit in there, we have the hash we wanted.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{
  method: &amp;quot;GET&amp;quot;,
  path: &amp;quot;/&amp;quot;
  ip: &amp;quot;127.0.0.1&amp;quot;,
  timestamp: &amp;quot;2017-08-20 20:53:10 +0900&amp;quot;,
  success: true,
  response_code: &amp;quot;200&amp;quot;,
  duration: &amp;quot;79ms&amp;quot;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Ruby&amp;#39;s &lt;code&gt;StringScanner&lt;/code&gt; occupies a nice middle ground between simple regular expressions and a full-blown lexer. It isn&amp;#39;t the best choice for complex scanning and parsing needs. But it&amp;#39;s straightforward nature makes it easy for everyone with basic regular expression knowledge to extract information from input strings and I&amp;#39;ve used those successfully in production code in the past. We hope you&amp;#39;ll discover this hidden Gem.&lt;/p&gt;
&lt;p&gt;PS: Let us know what you think are hidden Gems we should highlight next!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Unraveling Classes, Instances and Metaclasses in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/02/05/ruby-magic-classes-instances-and-metaclasses.html"/>
    <id>https://blog.appsignal.com/2019/02/05/ruby-magic-classes-instances-and-metaclasses.html</id>
    <published>2019-02-05T00:00:00+00:00</published>
    <updated>2019-02-05T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Through examining metaclasses, we&#039;ll learn how class and instance methods work in Ruby.</summary>
    <content type="html">&lt;p&gt;Welcome to a new episode of Ruby Magic! This month&amp;#39;s edition is all about metaclasses, a subject sparked by a discussion between two developers (Hi Maud!).&lt;/p&gt;
&lt;p&gt;Through examining metaclasses, we&amp;#39;ll learn how class and instance methods work in Ruby. Along the way, discover the difference between defining a method by passing an explicit &amp;quot;definee&amp;quot; and using &lt;code&gt;class &amp;lt;&amp;lt; self&lt;/code&gt; or &lt;code&gt;instance_eval&lt;/code&gt;. Let&amp;#39;s go!&lt;/p&gt;
&lt;h2&gt;Class Instances and Instance Methods&lt;/h2&gt;
&lt;p&gt;To understand why metaclasses are used in Ruby, we&amp;#39;ll start by examining what the differences are between instance- and class methods.&lt;/p&gt;
&lt;p&gt;In Ruby, a &lt;em&gt;class&lt;/em&gt; is an object that defines a blueprint to create other objects. Classes define which methods are available on any instance of that class.&lt;/p&gt;
&lt;p&gt;Defining a method inside a class creates an &lt;em&gt;instance method&lt;/em&gt; on that class. Any future instance of that class will have that method available.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

user = User.new(&amp;#39;Thijs&amp;#39;)
user.name # =&amp;gt; &amp;quot;Thijs&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we create a class named &lt;code&gt;User&lt;/code&gt;, with an &lt;em&gt;instance method&lt;/em&gt; named &lt;code&gt;#name&lt;/code&gt; that returns the user&amp;#39;s name. Using the class, we then create a &lt;em&gt;class instance&lt;/em&gt; and store it in a variable named &lt;code&gt;user&lt;/code&gt;. Since &lt;code&gt;user&lt;/code&gt; is an instance of the &lt;code&gt;User&lt;/code&gt; class, it has the &lt;code&gt;#name&lt;/code&gt; method available.&lt;/p&gt;
&lt;p&gt;A class stores its instance methods in its &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Virtual_method_table&quot;&gt;method table&lt;/a&gt;&lt;/em&gt;. Any instance of that class refers to its class’ method table to get access to its instance methods.&lt;/p&gt;
&lt;h2&gt;Class Objects&lt;/h2&gt;
&lt;p&gt;A &lt;em&gt;class method&lt;/em&gt; is a method that can be called directly on the class without having to create an instance first. A class method is created by prefixing its name with &lt;code&gt;self.&lt;/code&gt; when defining it.&lt;/p&gt;
&lt;p&gt;A class is itself an object. A constant refers to the class object, so class methods defined on it can be called from anywhere in the application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  # ...

  def self.all
    [new(&amp;quot;Thijs&amp;quot;), new(&amp;quot;Robert&amp;quot;), new(&amp;quot;Tom&amp;quot;)]
  end
end

User.all # =&amp;gt; [#&amp;lt;User:0x00007fb01701efb8 @name=&amp;quot;Thijs&amp;quot;&amp;gt;, #&amp;lt;User:0x00007fb01701ef68 @name=&amp;quot;Robert&amp;quot;&amp;gt;, #&amp;lt;User:0x00007fb01701ef18 @name=&amp;quot;Tom&amp;quot;&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Methods defined with a &lt;code&gt;self.&lt;/code&gt;-prefix aren’t added to the class’s method table. They’re instead added to the class’ metaclass.&lt;/p&gt;
&lt;h2&gt;Metaclasses&lt;/h2&gt;
&lt;p&gt;Aside from a class, each object in Ruby has a hidden metaclass. Metaclasses are singletons, meaning they belong to a single object. If you create multiple instances of a class, they’ll share the same class, but they’ll all have separate metaclasses.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;thijs, robert, tom = User.all

thijs.class # =&amp;gt; User
robert.class # =&amp;gt; User
tom.class # =&amp;gt; User

thijs.singleton_class  # =&amp;gt; #&amp;lt;Class:#&amp;lt;User:0x00007fb71a9a2cb0&amp;gt;&amp;gt;
robert.singleton_class # =&amp;gt; #&amp;lt;Class:#&amp;lt;User:0x00007fb71a9a2c60&amp;gt;&amp;gt;
tom.singleton_class    # =&amp;gt; #&amp;lt;Class:#&amp;lt;User:0x00007fb71a9a2c10&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we see that although each of the objects has the class &lt;code&gt;User&lt;/code&gt;, their singleton classes have different object IDs, meaning they’re separate objects.&lt;/p&gt;
&lt;p&gt;By having access to a metaclass, Ruby allows adding methods directly to existing objects. Doing so won’t add a new method to the object’s class.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;robert = User.new(&amp;quot;Robert&amp;quot;)

def robert.last_name
  &amp;quot;Beekman&amp;quot;
end

robert.last_name # =&amp;gt; &amp;quot;Beekman&amp;quot;
User.new(&amp;quot;Tom&amp;quot;).last_name # =&amp;gt; NoMethodError (undefined method `last_name&amp;#39; for #&amp;lt;User:0x00007fe1cb116408&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we add a &lt;code&gt;#last_name&lt;/code&gt; to the user stored in the &lt;code&gt;robert&lt;/code&gt; variable. Although &lt;code&gt;robert&lt;/code&gt; is an instance of &lt;code&gt;User&lt;/code&gt;, any newly created instances of &lt;code&gt;User&lt;/code&gt; won’t have access to the &lt;code&gt;#last_name&lt;/code&gt; method, as it only exists on &lt;code&gt;robert&lt;/code&gt;’s metaclass.&lt;/p&gt;
&lt;h2&gt;What Is &lt;code&gt;self&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;When defining a method and passing a receiver, the new method is added to the receiver’s metaclass, instead of adding it to the class’ method table.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;tom = User.new(&amp;quot;Tom&amp;quot;)

def tom.last_name
  &amp;quot;de Bruijn&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, we&amp;#39;ve added &lt;code&gt;#last_name&lt;/code&gt; directly on the &lt;code&gt;tom&lt;/code&gt; object, by passing &lt;code&gt;tom&lt;/code&gt; as the receiver when defining the method.&lt;/p&gt;
&lt;p&gt;This is also how it works for class methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  # ...

  def self.all
    [new(&amp;quot;Thijs&amp;quot;), new(&amp;quot;Robert&amp;quot;), new(&amp;quot;Tom&amp;quot;)]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we explicitly pass &lt;code&gt;self&lt;/code&gt; as a receiver when creating the &lt;code&gt;.all&lt;/code&gt; method. In a class definition, &lt;code&gt;self&lt;/code&gt; refers to the class (&lt;code&gt;User&lt;/code&gt; in this case), so the &lt;code&gt;.all&lt;/code&gt; method gets added to &lt;code&gt;User&lt;/code&gt;&amp;#39;s metaclass.&lt;/p&gt;
&lt;p&gt;Because &lt;code&gt;User&lt;/code&gt; is an object stored in a constant, we’ll access the same object—and the same metaclass—whenever we reference it.&lt;/p&gt;
&lt;h2&gt;Opening the Metaclass&lt;/h2&gt;
&lt;p&gt;We’ve learned that class methods are methods in the class object’s metaclass. Knowing this, we’ll look at some other techniques of creating class methods that you might have seen before.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;class &amp;lt;&amp;lt; self&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Although it has gone out of style a bit, some libraries use &lt;code&gt;class &amp;lt;&amp;lt; self&lt;/code&gt; to define class methods. This syntax trick opens up the current class&amp;#39;s metaclass and interacts with it directly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  class &amp;lt;&amp;lt; self
    self # =&amp;gt; #&amp;lt;Class:User&amp;gt;

    def all
      [new(&amp;quot;Thijs&amp;quot;), new(&amp;quot;Robert&amp;quot;), new(&amp;quot;Tom&amp;quot;)]
    end
  end
end

User.all # =&amp;gt; [#&amp;lt;User:0x00007fb01701efb8 @name=&amp;quot;Thijs&amp;quot;&amp;gt;, #&amp;lt;User:0x00007fb01701ef68 @name=&amp;quot;Robert&amp;quot;&amp;gt;, #&amp;lt;User:0x00007fb01701ef18 @name=&amp;quot;Tom&amp;quot;&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example creates a class method named &lt;code&gt;User.all&lt;/code&gt; by adding a method to &lt;code&gt;User&lt;/code&gt;&amp;#39;s metaclass. Instead of explicitly passing a receiver for the method as we saw previously, we set &lt;code&gt;self&lt;/code&gt; to &lt;code&gt;User&lt;/code&gt;&amp;#39;s metaclass instead of &lt;code&gt;User&lt;/code&gt; itself.&lt;/p&gt;
&lt;p&gt;As we learned before, any method definition without an explicit receiver gets added as an instance method of the current class. Inside the block, the current class is &lt;code&gt;User&lt;/code&gt;&amp;#39;s metaclass (&lt;code&gt;#&amp;lt;Class:User&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;instance_eval&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Another option is by using &lt;code&gt;instance_eval&lt;/code&gt;, which does the same thing with one major difference. Although the class&amp;#39;s metaclass receives the methods defined in the block, &lt;code&gt;self&lt;/code&gt; remains a reference to the main class.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class User
  instance_eval do
    self # =&amp;gt; User

    def all
      [new(&amp;quot;Thijs&amp;quot;), new(&amp;quot;Robert&amp;quot;), new(&amp;quot;Tom&amp;quot;)]
    end
  end
end

User.all # =&amp;gt; [#&amp;lt;User:0x00007fb01701efb8 @name=&amp;quot;Thijs&amp;quot;&amp;gt;, #&amp;lt;User:0x00007fb01701ef68 @name=&amp;quot;Robert&amp;quot;&amp;gt;, #&amp;lt;User:0x00007fb01701ef18 @name=&amp;quot;Tom&amp;quot;&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we define an instance method on &lt;code&gt;User&lt;/code&gt;&amp;#39;s metaclass just like before, but &lt;code&gt;self&lt;/code&gt; still points to &lt;code&gt;User&lt;/code&gt;. Although it usually points to the same object, the &amp;quot;default definee&amp;quot; and &lt;code&gt;self&lt;/code&gt; can point to different objects.&lt;/p&gt;
&lt;h2&gt;What We&amp;#39;ve Learned&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve learned that classes are the only objects that can have methods, and that instance methods are actually methods on an object&amp;#39;s metaclass. We know that &lt;code&gt;class &amp;lt;&amp;lt; self&lt;/code&gt; simply swaps &lt;code&gt;self&lt;/code&gt; around to allow you to define methods on the metaclass, and we know that &lt;code&gt;instance_eval&lt;/code&gt; does mostly the same thing (but without touching &lt;code&gt;self&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Although you won&amp;#39;t explicitly work with metaclasses, Ruby uses them extensively under the hood. Knowing what happens when you define a method can help you understand why Ruby behaves like it does (and why you have to prefix class methods with &lt;code&gt;self.&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Thanks for reading. If you liked what you read, you might like to &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to Ruby Magic&lt;/a&gt; to receive an e-mail when we publish a new article about once a month.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Bindings and Lexical Scope in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2019/01/08/ruby-magic-bindings-and-lexical-scope.html"/>
    <id>https://blog.appsignal.com/2019/01/08/ruby-magic-bindings-and-lexical-scope.html</id>
    <published>2019-01-08T00:00:00+00:00</published>
    <updated>2019-01-08T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this edition of Ruby Magic, we’ll show how scope works in Ruby’s methods and closures. We’ll also take a peek at how ERB uses the binding object to render templates.</summary>
    <content type="html">&lt;p&gt;Happy new year, and welcome back to Ruby Magic! In this winter episode, we&amp;#39;ll dive into bindings and scopes. So put on your skis and follow us deep into the woods.&lt;/p&gt;
&lt;p&gt;Last time, we looked at &lt;a href=&quot;/2018/09/04/ruby-magic-closures-in-ruby-blocks-procs-and-lambdas.html&quot;&gt;closures in Ruby&lt;/a&gt; by comparing blocks, procs and lambdas. Aside from the differences between the three types, we touched on what defines a &lt;em&gt;closure&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A closure is a first-class function with an environment. The environment is a mapping to the variables that existed when the closure was created. The closure retains its access to these variables, even if they’re defined in another scope.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We&amp;#39;ve explored Ruby&amp;#39;s equivalent to first-class functions, but we conveniently skipped over &lt;em&gt;environments&lt;/em&gt;. In this episode, we’ll look at how that environment works for closures, classes and class instances by examining how Ruby handles &lt;em&gt;lexical scope&lt;/em&gt; through its &lt;em&gt;bindings&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Lexical Scope&lt;/h2&gt;
&lt;p&gt;In programming, scope refers to the &lt;em&gt;bindings&lt;/em&gt; available at a specific part of the code. A binding, or &lt;em&gt;name binding&lt;/em&gt;, binds a name to a memory reference, like a variable&amp;#39;s name to its value. The scope defines what &lt;code&gt;self&lt;/code&gt; means, the methods that can be called, and the variables that are available.&lt;/p&gt;
&lt;p&gt;Ruby, like most modern programming languages, uses a static scope, often called &lt;em&gt;lexical scope&lt;/em&gt; (as opposed to &lt;a href=&quot;https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynamic_scope&quot;&gt;dynamic scope&lt;/a&gt;). The current scope is based on the structure of the code and determines the variables available at specific parts of the code. This means that the scope changes when code jumps between methods, blocks and classes—as they can all have different local variables, for example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def bar
  foo = 1
  foo
end

bar #  =&amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this method, we create a &lt;em&gt;local variable&lt;/em&gt; inside a method and print it to the console. The variable is &lt;em&gt;in scope&lt;/em&gt; inside the method, as it’s created there.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;foo = 1

def bar
  foo
end

bar # =&amp;gt; NameError (undefined local variable or method `foo&amp;#39; for main:Object)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we create the variable outside the method. When we call the variable inside a method, we get an error, as the variable is &lt;em&gt;out of scope&lt;/em&gt;. Local variables are tightly scoped, meaning a method can’t access a variable outside itself unless it’s passed as an argument.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@foo = 1

def bar
  @foo
end

bar #  =&amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While local variables are available locally, &lt;em&gt;instance variables&lt;/em&gt; are available to all methods of a class instance.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Inherited Scopes and Proc Bindings&lt;/h2&gt;
&lt;p&gt;As we&amp;#39;ve seen in the previous examples, the scope is based on the location in the code. A local variable defined outside a method is not in scope inside the method but can be made available by turning it into an instance variable. Methods can&amp;#39;t access local variables defined outside of them because methods have their own scope, with their own bindings.&lt;/p&gt;
&lt;p&gt;Procs (including blocks and lambda&amp;#39;s, by extension) are different. Whenever a proc is instantiated, a binding is created which inherits references to the local variables in the context the block was created.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;foo = 1
Proc.new { foo }.call # =&amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we set a variable named &lt;code&gt;foo&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;. Internally, the Proc object created on the second line creates a new binding. When calling the proc, we can ask for the value of the variable.&lt;/p&gt;
&lt;p&gt;Since the binding is created when the proc is initialized, we can’t create the proc before defining the variable, even if the block isn&amp;#39;t called until after the variable is defined.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;proc = Proc.new { foo }
foo = 1
proc.call # =&amp;gt; NameError (undefined local variable or method `foo&amp;#39; for main:Object)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling the proc will produce a &lt;code&gt;NameError&lt;/code&gt; as the variable isn’t defined in the proc’s bindings. Thus, any variables accessed in a proc should be defined before the proc is created or passed as an argument.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;foo = 1
proc = Proc.new { foo }
foo = 2
proc.call # =&amp;gt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can, however, change the variable after it has been defined in the main context since the proc’s binding holds a reference to it instead of copying it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;foo = 1
Proc.new { foo = 2 }.call
foo #=&amp;gt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we can see that the &lt;code&gt;foo&lt;/code&gt; variable points to the same object when in the proc as outside of it. We can update it inside the proc to have the variable outside of it updated as well.&lt;/p&gt;
&lt;h2&gt;Bindings&lt;/h2&gt;
&lt;p&gt;To keep track of the current scope, Ruby uses &lt;em&gt;bindings&lt;/em&gt;, which encapsulate the execution context at each position in the code. The &lt;code&gt;binding&lt;/code&gt; method returns a &lt;code&gt;Binding&lt;/code&gt; object which describes the bindings at the current position.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;foo = 1
binding.local_variables # =&amp;gt; [:foo]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The binding object has a method named &lt;code&gt;#local_variables&lt;/code&gt; which returns the names of all local variables available in the current scope.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;foo = 1
binding.eval(&amp;quot;foo&amp;quot;) # =&amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Code can be evaluated on the binding by using the &lt;code&gt;#eval&lt;/code&gt; method. The example above isn&amp;#39;t very useful, as simply calling &lt;code&gt;foo&lt;/code&gt; would have the same result. However, since a binding is an object that can be passed around, it can be used for some more interesting things. Let&amp;#39;s look at an example.&lt;/p&gt;
&lt;h3&gt;A Real-Life Example&lt;/h3&gt;
&lt;p&gt;Now that we&amp;#39;ve learned about bindings in the safety of our garage, like take them out on to the slopes and play around in the snow. Aside from Ruby&amp;#39;s internal use of bindings throughout the language, there are some situations where binding objects are used explicitly. A good example is ERB—Ruby&amp;#39;s templating system.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;erb&amp;#39;

x = 1

def y
  2
end

template = ERB.new(&amp;quot;x is &amp;lt;%= x %&amp;gt;, y() returns &amp;lt;%= y %&amp;gt;, self is `&amp;lt;%= self %&amp;gt;`&amp;quot;)
template.result(binding) # =&amp;gt; &amp;quot;x is 1, y() returns 2, self is `main`&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we create a variable named &lt;code&gt;x&lt;/code&gt;, a method called &lt;code&gt;y&lt;/code&gt;, and an ERB template that references both. We then pass the current binding to &lt;code&gt;ERB#result&lt;/code&gt;, which evaluates the ERB tags in the template and returns a string with the variables filled in.&lt;/p&gt;
&lt;p&gt;Under the hood, ERB uses &lt;code&gt;Binding#eval&lt;/code&gt; to evaluate each ERB tag&amp;#39;s contents in the scope of the passed binding. A simplified implementation that works for the example above could look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class DiyErb
  def initialize(template)
    @template = template
  end

  def result(binding)
    @template.gsub(/&amp;lt;%=(.+?)%&amp;gt;/) do
      binding.eval($1)
    end
  end
end

x = 1

def y
  2
end

template = DiyErb.new(&amp;quot;x is &amp;lt;%= x %&amp;gt;, y() returns &amp;lt;%= y %&amp;gt;, self is `&amp;lt;%= self %&amp;gt;`&amp;quot;)
template.result(binding) # =&amp;gt; &amp;quot;x is 1, y() returns 2, self is `main`&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;DiyErb&lt;/code&gt; class takes a template string on initialization. Its &lt;code&gt;#result&lt;/code&gt; method finds all ERB tags and replaces them with the result of evaluating their contents. To do that, it calls &lt;code&gt;Binding#eval&lt;/code&gt; on the passed binding, with the contents of the ERB tags.&lt;/p&gt;
&lt;p&gt;By passing the current binding when calling the &lt;code&gt;#result&lt;/code&gt; method, the &lt;code&gt;eval&lt;/code&gt; calls can access the variables defined outside of the method, and even outside of the class, without having to pass them explicitly.&lt;/p&gt;
&lt;h2&gt;Did we lose you in the woods?&lt;/h2&gt;
&lt;p&gt;We hope you enjoyed our ski trip into the woods. We went deeper into scopes and closures, after glossing over them. We hope we haven&amp;#39;t lost you in the woods. Please let us know if you&amp;#39;d like to learn more about bindings, or have any other Ruby topic you&amp;#39;d like to dive into.&lt;/p&gt;
&lt;p&gt;Thanks for following us and please kick the snow off your bindings before leaving them for the next developer. If you like these magical trips, you might like to &lt;a href=&quot;/ruby-magic&quot;&gt;subscribe to Ruby Magic&lt;/a&gt; to receive an e-mail when we publish a new article about once a month.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Fibers and Enumerators in Ruby - Turning Blocks Inside Out</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/11/27/ruby-magic-fibers-and-enumerators-in-ruby.html"/>
    <id>https://blog.appsignal.com/2018/11/27/ruby-magic-fibers-and-enumerators-in-ruby.html</id>
    <published>2018-11-27T00:00:00+00:00</published>
    <updated>2018-11-27T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this edition of Ruby Magic, we shine a light on Enumerable and Fiber to explain flow controlling enumerables and turning blocks inside out.</summary>
    <content type="html">&lt;p&gt;Ruby has various ways of performing iteration—loops, blocks and enumerators. Most Ruby programmers are at least familiar with loops and blocks but &lt;code&gt;Enumerator&lt;/code&gt; and &lt;code&gt;Fiber&lt;/code&gt; often stay in the dark. In this edition of Ruby Magic, guest author Julik shines a light on &lt;code&gt;Enumerable&lt;/code&gt; and &lt;code&gt;Fiber&lt;/code&gt; to explain flow controlling enumerables and turning blocks inside out.&lt;/p&gt;
&lt;h2&gt;Suspending Blocks and Chained Iteration&lt;/h2&gt;
&lt;p&gt;We&amp;#39;ve discussed &lt;a href=&quot;/2018/05/29/ruby-magic-enumerable-and-enumerator.html&quot;&gt;Enumerator&lt;/a&gt; in a previous edition of Ruby Magic, where we described how to return an &lt;code&gt;Enumerator&lt;/code&gt; from your own &lt;code&gt;#each&lt;/code&gt; method and what it can be used for. An even broader use case for &lt;code&gt;Enumerator&lt;/code&gt; and &lt;code&gt;Fiber&lt;/code&gt; is that they can &amp;quot;suspend a block&amp;quot; mid-flight. Not just the block given to &lt;code&gt;#each&lt;/code&gt; or the entire call to &lt;code&gt;#each&lt;/code&gt;, but any block!&lt;/p&gt;
&lt;p&gt;This is a very powerful construct, which can be used to implement shims for methods that work by using blocks as a bridge to callers that expect sequential calls instead of taking a block. For example, imagine we want to open a database handle and read each item that we have retrieved:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;db.with_each_row_of_result(sql_stmt) do |row|
  yield row
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The block API is great since it will potentially perform all kinds of cleanup for us when the block is terminated. However, some consumers might want to work with the database in this way:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@cursor = cursor

# later:
row = @cursor.next_row
send_row_to_event_stream(row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In practice, it means we want to &amp;quot;suspend&amp;quot; the execution of the block &amp;quot;just for now&amp;quot; and carry on later within the block. Thus, the caller takes over the flow control instead of it being in the hands of the callee (the method performing the block).&lt;/p&gt;
&lt;h2&gt;Chaining Iterators&lt;/h2&gt;
&lt;p&gt;One of the most common uses of this pattern is chaining multiple iterators together. When we do so, the methods we are used to for iteration (like &lt;code&gt;#each&lt;/code&gt;), return an Enumerator object instead, which we can use to &amp;quot;grab&amp;quot; the values that the block sends us using the &lt;code&gt;yield&lt;/code&gt; statement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;range = 1..8
each_enum = range.each # =&amp;gt; &amp;lt;Enumerator...&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The enumerators can then be &lt;em&gt;chained&lt;/em&gt; which allows us to perform operations like &amp;quot;any iteration but with the index&amp;quot;. In this example, we&amp;#39;re calling &lt;code&gt;#map&lt;/code&gt; on a range to get an &lt;code&gt;Enumerable&lt;/code&gt; object. We then chain &lt;code&gt;#with_index&lt;/code&gt; to iterate over the range with an index:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;(1..3).map.with_index {|element_n, index| [element_n, index] }
#=&amp;gt; [[1, 0], [2, 1], [3, 2]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be very useful, especially if your system uses events. Ruby provides a built-in method for wrapping any method with an Enumerator generator, which allows us to accomplish exactly this. Imagine we want to &amp;quot;pull&amp;quot; rows one by one from our &lt;code&gt;with_each_row_of_result&lt;/code&gt;, instead of the method yielding them to us.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;@cursor = db.to_enum(:with_each_row_of_result, sql_stmt)
schedule_for_later do
  begin
    row = @cursor.next
    send_row_to_event_stream(row)
  rescue StopIteration # the block has ended and the cursor is empty, the cleanup has taken place
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we were to implement this ourselves, this is how it would likely come about:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;cursor = Enumerator.new do |yielder|
  db.with_each_row_of_result(sql_stmt) do |row|
    yielder.yield row
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;Turning Blocks Inside Out&lt;/h2&gt;
&lt;p&gt;Rails allows us to assign the response body to also be an Enumerator. It will call &lt;code&gt;next&lt;/code&gt; on the Enumerator we assign as the response body and expect the returned value to be a string—which will be written out into the Rack response. For example, we can return a call to the &lt;code&gt;#each&lt;/code&gt; method of a Range as a Rails response body:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyController &amp;lt; ApplicationController
  def index
    response.body = (&amp;#39;a&amp;#39;..&amp;#39;z&amp;#39;).each
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what I call &lt;em&gt;turning a block inside out.&lt;/em&gt; In essence, it is a control flow helper that allows us to &amp;quot;freeze time&amp;quot; in a block (or a loop, which is also a block in Ruby) mid-flight.&lt;/p&gt;
&lt;p&gt;However, Enumerators have a limiting property that makes them slightly less useful. Imagine we want to do something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;File.open(&amp;#39;output.tmp&amp;#39;, &amp;#39;wb&amp;#39;) do |f|
  # Yield file for writing, continuously
  loop { yield(f) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s wrap it with an enumerator, and write into it&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;writer_enum = File.to_enum(:open, &amp;#39;output.tmp&amp;#39;, &amp;#39;wb&amp;#39;)
file = en.next
file &amp;lt;&amp;lt; data
file &amp;lt;&amp;lt; more_data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything works great. However, there is a hitch—how do we tell the enumerator that we are done writing, so that it can &amp;quot;finish&amp;quot; the block, close the file and exit? This will perform a number of important steps—for example, resource cleanup (the file will be closed), as well as ensuring all the buffered writes are flushed to disk. We do have access to the &lt;code&gt;File&lt;/code&gt; object, and we can close it ourselves, but we would like the enumerator to manage the closing for us; we have to let the enumerator proceed past the block.&lt;/p&gt;
&lt;p&gt;Another hurdle is that sometimes we want to pass arguments of what is happening within the suspended block. Imagine we have a block-accepting method with the following semantics:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;write_file_through_encryptor(file_name) do |writable|
  writable &amp;lt;&amp;lt; &amp;quot;Some data&amp;quot;
  writable &amp;lt;&amp;lt; &amp;quot;Some more data&amp;quot;
  writable &amp;lt;&amp;lt; &amp;quot;Even more data&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;but in our calling code we want to use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;writable = write_file_through_encryptor(file_name)
writable &amp;lt;&amp;lt; &amp;quot;Some data&amp;quot;
# ...later on
writable &amp;lt;&amp;lt; &amp;quot;Some more data&amp;quot;
writable.finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ideally, we would wrap our method call into some structure that would permit us the following trick:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;write_file_through_encryptor(file_name) do |writable|
  loop do
    yield_and_wait_for_next_call(writable)
    # Then we somehow break out of this loop to let the block complete
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What if we were to wrap our writes like this?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;deferred_writable = write_file_through_encryptor(file_name)
deferred_writable.next(&amp;quot;Some data&amp;quot;)
deferred_writable.next(&amp;quot;Some more data&amp;quot;)
deferred_writable.next(&amp;quot;Even more data&amp;quot;)
deferred_writable.next(:terminate)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, we will use the &lt;code&gt;:terminate&lt;/code&gt; as a magic value that will tell our method that it can finish the block and return. &lt;strong&gt;This&lt;/strong&gt; is where &lt;code&gt;Enumerator&lt;/code&gt; won&amp;#39;t really help us because we can&amp;#39;t pass any arguments to &lt;code&gt;Enumerator#next&lt;/code&gt;. If we could, we would be able to do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;deferred_writable = write_file_through_encryptor(file_name)
deferred_writable.next(&amp;quot;Some data&amp;quot;)
...
deferred_writable.next(:terminate)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Enter Ruby&amp;#39;s Fibers&lt;/h2&gt;
&lt;p&gt;This is exactly what Fibers permit. A Fiber allows you to &lt;em&gt;accept arguments on each reentry&lt;/em&gt;, so we can implement our wrapper like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;deferred_writable = Fiber.new do |data_to_write_or_termination|
  write_file_through_encryptor(filename) do |f|
     # Here we enter the block context of the fiber, reentry will be to the start of this block
    loop do
      # When we call Fiber.yield our fiber will be suspended—we won&amp;#39;t reach the
      # &amp;quot;data_to_write_or_termination = &amp;quot; assignment before our fiber gets resumed
      data_to_write_or_termination = Fiber.yield
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is how it works: When you first call &lt;code&gt;.resume&lt;/code&gt; on your &lt;code&gt;deferred_writable&lt;/code&gt;, it enters the fiber and goes all the way to the first &lt;code&gt;Fiber.yield&lt;/code&gt; statement or to the end of the outermost Fiber block, whichever comes first. When you call &lt;code&gt;Fiber.yield&lt;/code&gt;, it gives you back control. Remember the Enumerator? The block is going to be &lt;em&gt;suspended&lt;/em&gt;, and the next time you call &lt;code&gt;.resume&lt;/code&gt;, the argument to &lt;code&gt;resume&lt;/code&gt; becomes the new &lt;code&gt;data_to_write&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;deferred_writes = Fiber.new do |data_to_write|
  loop do
    $stderr.puts &amp;quot;Received #{data_to_write} to work with&amp;quot;
    data_to_write = Fiber.yield
  end
end
# =&amp;gt; #&amp;lt;Fiber:0x007f9f531783e8&amp;gt;
deferred_writes.resume(&amp;quot;Hello&amp;quot;) #=&amp;gt; Received Hello to work with
deferred_writes.resume(&amp;quot;Goodbye&amp;quot;) #=&amp;gt; Received Goodbye to work with
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, within the Fiber, the code flow is &lt;em&gt;started&lt;/em&gt; on the first call to &lt;code&gt;Fiber#resume&lt;/code&gt;, suspended at the first call to &lt;code&gt;Fiber.yield&lt;/code&gt;, and then &lt;em&gt;continued&lt;/em&gt; on subsequent calls to &lt;code&gt;Fiber#resume&lt;/code&gt;, with the return value of &lt;code&gt;Fiber.yield&lt;/code&gt; being the arguments to &lt;code&gt;resume&lt;/code&gt;. The code continues running from the point where &lt;code&gt;Fiber.yield&lt;/code&gt; was last called.&lt;/p&gt;
&lt;p&gt;This is a bit of a quirk of Fibers in that the initial arguments to the fiber will be passed to you as the block arguments, not via the return value of &lt;code&gt;Fiber.yield&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With that in mind, we know that by passing a special argument to &lt;code&gt;resume&lt;/code&gt;, we can decide within the Fiber whether we should stop or not. Let&amp;#39;s try that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;deferred_writes = Fiber.new do |data_to_write|
  loop do
    $stderr.puts &amp;quot;Received #{data_to_write} to work with&amp;quot;
    break if data_to_write == :terminate # Break out of the loop, or...
    write_to_output(data_to_write)       # ...write to the output
    data_to_write = Fiber.yield          # suspend ourselves and wait for the next `resume`
  end
  # We end up here if we break out of the loop above. There is no Fiber.yield
  # statement anywhere, so the Fiber will terminate and become &amp;quot;dead&amp;quot;.
end

deferred_writes.resume(&amp;quot;Hello&amp;quot;) #=&amp;gt; Received Hello to work with
deferred_writes.resume(&amp;quot;Goodbye&amp;quot;) #=&amp;gt; Received Goodbye to work with
deferred_writes.resume(:terminate)
deferred_writes.resume(&amp;quot;Some more data after close&amp;quot;) # FiberError: dead fiber called
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a number of situations where these facilities can be very useful. Since a Fiber contains a suspended block of code that can be manually resumed, Fibers can be used for implementing event reactors and for dealing with concurrent operations within a single thread. They are lightweight, so you can implement a server using Fibers by assigning a single client to a single Fiber and switching between these Fiber objects as necessary.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;client_fiber = Fiber.new do |socket|
   loop do
     received_from_client = socket.read_nonblock(10)
     sent_to_client = socket.write_nonblock(&amp;quot;OK&amp;quot;)
     Fiber.yield # Return control back to the caller and wait for it to call &amp;#39;resume&amp;#39; on us
   end
end

client_fibers &amp;lt;&amp;lt; client_fiber

# and then in your main webserver loop
client_fibers.each do |client_fiber|
  client_fiber.resume # Receive data from the client if any, and send it an OK
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ruby has an additional standard library called &lt;code&gt;fiber&lt;/code&gt; which allows you to explicitly transfer control from one fiber to another, which can be a bonus facility for these uses.&lt;/p&gt;
&lt;h2&gt;Controlling Data Emission Rates&lt;/h2&gt;
&lt;p&gt;Another great use for fibers and enumerators can arise when you want to be able to control the rate at which a Ruby block emits data. For example, in &lt;a href=&quot;https://github.com/WeTransfer/zip_tricks&quot;&gt;zip_tricks&lt;/a&gt; we support the following block use as the primary way of using the library:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ZipTricks::Streamer.open(output_io) do |z|
  z.write_deflated_file(&amp;quot;big.csv&amp;quot;) do |destination|
   columns.each do |col|
     destination &amp;lt;&amp;lt; column
   end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We therefore allow &amp;quot;push&amp;quot; control on the part of the code that creates the ZIP archive, and it is impossible to control how much data it outputs and how often. If we want to write our ZIP in chunks of, say, 5 MB—which would be a limitation on AWS S3 object storage—we would have to create a custom &lt;code&gt;output_io&lt;/code&gt; object which would somehow &amp;quot;refuse&amp;quot; to accept &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; method calls when the segment needs to be split off into an S3 multipart part. We can, however, invert the control and make it &amp;quot;pull&amp;quot;. We will still use the same block for writing our big CSV file, but we will be resuming and halting it based on the output it provides. We therefore make the following use possible:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;output_enum = ZipTricks::Streamer.output_enum do |z|
  z.write_deflated_file(&amp;quot;big.csv&amp;quot;) do |destination|
   columns.each do |col|
     destination &amp;lt;&amp;lt; column
   end
  end
end

# At this point nothing has been generated or written yet
enum = output_enum.each # Create an Enumerator
bin_str = enum.next # Let the block generate some binary data and then suspend it
output.write(bin_str) # Our block is suspended and waiting for the next invocation of `next`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows us to control at which rate our ZIP file generator emits data.&lt;/p&gt;
&lt;p&gt;Enumerator and Fiber are, therefore, a &lt;strong&gt;control flow mechanism&lt;/strong&gt; for turning &amp;quot;push&amp;quot; blocks into &amp;quot;pull&amp;quot; objects that accept method calls.&lt;/p&gt;
&lt;p&gt;There is only one pitfall with Fibers and Enumerators—if you have something like &lt;code&gt;ensure&lt;/code&gt; in your block, or something that needs to be done after the block completes, it is now up to the caller to call you enough times. In a way, it is comparable to the constraints you have when using Promises in JavaScript.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This concludes our look into flow-controlled enumerables in Ruby. Along the way, Julik shone light on the similarities and differences between the &lt;code&gt;Enumerable&lt;/code&gt; and &lt;code&gt;Fiber&lt;/code&gt; classes, and dove into examples where caller determined the flow of data. We’ve also learned about &lt;code&gt;Fiber&lt;/code&gt;’s additional magic to allow passing arguments on each block reentry. Happy flow-controlling!&lt;/p&gt;
&lt;p&gt;To get a steady dose of magic, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe&lt;/a&gt; to Ruby Magic and we&amp;#39;ll deliver our monthly edition straight to your inbox.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Building a Ruby C Extension From Scratch</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/10/30/ruby-magic-building-a-ruby-c-extension-from-scratch.html"/>
    <id>https://blog.appsignal.com/2018/10/30/ruby-magic-building-a-ruby-c-extension-from-scratch.html</id>
    <published>2018-10-30T00:00:00+00:00</published>
    <updated>2018-10-30T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this edition of Ruby Magic, we&#039;ll show you how to use code written in C from Ruby.</summary>
    <content type="html">&lt;p&gt;In this edition of Ruby Magic, we&amp;#39;ll show you how to use code written in C from Ruby. This can be used to optimize performance sensitive parts of your code or to create an interface between a C library and Ruby. This is done by creating extensions that wrap libraries written in C.&lt;/p&gt;
&lt;p&gt;There are a lot of mature and performant libraries written in C. Instead of reinventing the wheel by porting them we can also leverage these libraries from Ruby. In this way, we get to code in our favorite language, while using C libraries in areas where Ruby isn&amp;#39;t traditionally strong. At AppSignal, we&amp;#39;ve used this approach in developing the &lt;a href=&quot;https://github.com/appsignal/rdkafka-ruby&quot;&gt;rdkafka gem&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So let&amp;#39;s see how one can approach this. If you want to follow along and experiment yourself, check out the &lt;a href=&quot;https://github.com/appsignal/c_from_ruby_example&quot;&gt;example code&lt;/a&gt;. To start off, let&amp;#39;s take this piece of Ruby code with a string, a number and a boolean (you&amp;#39;ll C why, pun intended) and port it to a C library:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module CFromRubyExample
  class Helpers
    def self.string(value)
      &amp;quot;String: &amp;#39;#{value}&amp;#39;&amp;quot;
    end

    def self.number(value)
      value + 1
    end

    def self.boolean(value)
      !value
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order, the methods shown concatenate a string, increase a number by one and return the opposite of a boolean, respectively.&lt;/p&gt;
&lt;h2&gt;Our Library Ported to C&lt;/h2&gt;
&lt;p&gt;Below, you can see the code ported to C. The C Standard Library and the IO Library are included so that we can use string formatting. We use &lt;code&gt;char*&lt;/code&gt; instead of a Ruby &lt;code&gt;String&lt;/code&gt;. &lt;code&gt;char*&lt;/code&gt; points to the location of a buffer of characters somewhere in memory.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;# include &amp;lt;stdlib.h&amp;gt;
# include &amp;lt;stdio.h&amp;gt;

char* string_from_library(char* value) {
  char* out = (char*)malloc(256 * sizeof(char));
  sprintf(out, &amp;quot;String: &amp;#39;%s&amp;#39;&amp;quot;, value);
  return out;
}

int number_from_library(int value) {
  return value + 1;
}

int boolean_from_library(int value) {
  if (value == 0) {
    return 1;
  } else {
    return 0;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, you have to jump through some hoops to do simple string formatting. To concatenate a string, we first have to allocate a buffer. With this done, the &lt;code&gt;sprintf&lt;/code&gt; function can then write the formatted result to it. Finally, we can return the buffer.&lt;/p&gt;
&lt;p&gt;With the code above, we already introduced a possible crash or security issue. If the incoming string is longer than 245 bytes, the dreaded buffer overflow will occur. You should definitely be careful when writing C, it&amp;#39;s easy to shoot yourself in the foot.&lt;/p&gt;
&lt;p&gt;Next up is a header file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;char* string_from_library(char*);
int number_from_library(int);
int boolean_from_library(int);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file describes the public API of our C library. Other programs use it to know which functions in the library can be called.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;The 2018 Way: Use the &lt;code&gt;ffi&lt;/code&gt; Gem&lt;/h2&gt;
&lt;p&gt;So, we now have a C library that we want to use from Ruby. There are two ways to wrap this C code in a gem. The modern way involves using the &lt;code&gt;ffi&lt;/code&gt; gem. It automates many of the hoops we have to jump through. Using &lt;code&gt;ffi&lt;/code&gt; with the C code we just wrote looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module CFromRubyExample
  class Helpers
    extend FFI::Library

    ffi_lib File.join(File.dirname(__FILE__), &amp;quot;../../ext/library.so&amp;quot;)

    attach_function :string, [:string], :string
    attach_function :number, [:int], :int
    attach_function :boolean, [:int], :int
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the purpose of this article, we&amp;#39;re also going to explain how to wrap the C code with a C extension. This will give us much more insight into how it all works under the hood in Ruby.&lt;/p&gt;
&lt;h2&gt;Wrapping our Library in a C Extension&lt;/h2&gt;
&lt;p&gt;So we now have a C library we want to use from Ruby. The next step is to create a gem that compiles and wraps it. After creating the gem, we first add &lt;code&gt;ext&lt;/code&gt; to the &lt;code&gt;require_paths&lt;/code&gt; in the gemspec:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Gem::Specification.new do |spec|
  spec.name          = &amp;quot;c_from_ruby_example&amp;quot;
  # ...
  spec.require_paths = [&amp;quot;lib&amp;quot;, &amp;quot;ext&amp;quot;]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This informs Rubygems that there is a native extension that needs to be built. It will look for a file called &lt;code&gt;extconf.rb&lt;/code&gt; or a &lt;code&gt;Rakefile&lt;/code&gt;. In this case, we added &lt;code&gt;extconf.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;quot;mkmf&amp;quot;

create_makefile &amp;quot;extension&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We require &lt;code&gt;mkmf&lt;/code&gt;, which stands for &amp;quot;Make Makefile&amp;quot;. It&amp;#39;s a set of helpers included with Ruby that eliminates the finicky part of getting a C build set up. We call &lt;code&gt;create_makefile&lt;/code&gt; and set a name for the extension. This creates a &lt;code&gt;Makefile&lt;/code&gt; which contains all the configuration and commands to build the C code.&lt;/p&gt;
&lt;p&gt;Next, we need to write some C code to connect the library to Ruby. We&amp;#39;ll create some functions that convert C types such as &lt;code&gt;char*&lt;/code&gt; to Ruby types such as &lt;code&gt;String&lt;/code&gt;. Then we&amp;#39;ll create a Ruby class with C code.&lt;/p&gt;
&lt;p&gt;First off, we include some header files from Ruby. These will import the functions we need to do type conversion. We also include the &lt;code&gt;library.h&lt;/code&gt; header file that we created earlier so that we can call our library.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;quot;ruby/ruby.h&amp;quot;
#include &amp;quot;ruby/encoding.h&amp;quot;
#include &amp;quot;library.h&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then create a function to wrap each function in our library. This is the one for string:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;static VALUE string(VALUE self, VALUE value) {
  Check_Type(value, T_STRING);

  char* pointer_in = RSTRING_PTR(value);
  char* pointer_out = string_from_library(pointer_in);
  return rb_str_new2(pointer_out);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first check if the Ruby value coming in is a string, since processing a non-string value might cause all sorts of bugs. We then convert the Ruby &lt;code&gt;String&lt;/code&gt; to a &lt;code&gt;char*&lt;/code&gt; with the &lt;code&gt;RSTRING_PTR&lt;/code&gt; helper macro that Ruby provides. We can now call our C library. To convert the returned &lt;code&gt;char*&lt;/code&gt;, we use the includes &lt;code&gt;rb_str_new2&lt;/code&gt; function. We&amp;#39;ll add similar wrapping functions for number and boolean.&lt;/p&gt;
&lt;p&gt;For numbers, we do something similar using the &lt;code&gt;NUM2INT&lt;/code&gt; and &lt;code&gt;INT2NUM&lt;/code&gt; helpers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;static VALUE number(VALUE self, VALUE value) {
  Check_Type(value, T_FIXNUM);

  int number_in = NUM2INT(value);
  int number_out = number_from_library(number_in);
  return INT2NUM(number_out);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The boolean version is also similar. Note that C doesn&amp;#39;t actually have a boolean type. The convention is to instead use 0 and 1.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;static VALUE boolean(VALUE self, VALUE value) {
  int boolean_in = RTEST(value);
  int boolean_out = boolean_from_library(boolean_in);
  if (boolean_out == 1) {
    return Qtrue;
  } else {
    return Qfalse;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we can wire up everything so that we can call it from Ruby:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void Init_extension(void) {
  VALUE CFromRubyExample = rb_define_module(&amp;quot;CFromRubyExample&amp;quot;);
  VALUE NativeHelpers = rb_define_class_under(CFromRubyExample, &amp;quot;NativeHelpers&amp;quot;, rb_cObject);

  rb_define_singleton_method(NativeHelpers, &amp;quot;string&amp;quot;, string, 1);
  rb_define_singleton_method(NativeHelpers, &amp;quot;number&amp;quot;, number, 1);
  rb_define_singleton_method(NativeHelpers, &amp;quot;boolean&amp;quot;, boolean, 1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, you read that right: we can create Ruby modules, classes and methods in C. We set up our class here. We then add Ruby methods to the class. We have to provide the name of the Ruby method, the name of the C wrapper function that will be called and indicate the number of arguments.&lt;/p&gt;
&lt;p&gt;After all that work, we can finally call our C code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;CFromRubyExample::NativeHelpers.string(&amp;quot;a string&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We jumped through hoops, didn&amp;#39;t crash and got our C extension to work. Writing C extensions is not for the faint of heart. Even when using the &lt;code&gt;ffi&lt;/code&gt; gem you can still quite easily crash your Ruby process. But it is doable and can open up a world of performant and stable C libraries for you!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The Magic of Class-level Instance Variables</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/10/02/ruby-magic-class-level-instance-variables.html"/>
    <id>https://blog.appsignal.com/2018/10/02/ruby-magic-class-level-instance-variables.html</id>
    <published>2018-10-02T00:00:00+00:00</published>
    <updated>2018-10-02T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Even classes are objects in Ruby. In this edition of Ruby Magic, we&#039;ll look at class-level instance variables by creating a module for wrapping an object&#039;s methods.</summary>
    <content type="html">&lt;p&gt;In &lt;a href=&quot;/2018/08/07/ruby-magic-changing-the-way-ruby-creates-objects.html&quot;&gt;a previous Ruby Magic&lt;/a&gt;, we figured out how to reliably inject modules into classes by overwriting its &lt;code&gt;.new&lt;/code&gt; method, allowing us to wrap methods with additional behavior.&lt;/p&gt;
&lt;p&gt;This time, we&amp;#39;re taking it one step further by extracting that behaviour into a module of its own so we can reuse it. We&amp;#39;ll build a &lt;code&gt;Wrappable&lt;/code&gt; module that handles the class extension for us, and we&amp;#39;ll learn all about class-level instance variables along the way. Let&amp;#39;s dive right in!&lt;/p&gt;
&lt;h2&gt;Introducing the &lt;code&gt;Wrappable&lt;/code&gt; Module&lt;/h2&gt;
&lt;p&gt;In order to wrap objects with modules when they are initialized, we have to let the class know what wrapping models to use. Let’s start by creating a simple &lt;code&gt;Wrappable&lt;/code&gt; module that provides a &lt;code&gt;wrap&lt;/code&gt; method which pushes the given module into an array defined as a class attribute. Additionally, we redefine the &lt;code&gt;new&lt;/code&gt; method as discussed in the previous post.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Wrappable
  @@wrappers = []

  def wrap(mod)
    @@wrappers &amp;lt;&amp;lt; mod
  end

  def new(*arguments, &amp;amp;block)
    instance = allocate
    @@wrappers.each { |mod| instance.singleton_class.include(mod) }
    instance.send(:initialize, *arguments, &amp;amp;block)
    instance
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add the new behavior to a class, we use &lt;code&gt;extend&lt;/code&gt;. The &lt;code&gt;extend&lt;/code&gt; method adds the given module to the class. The methods then become class methods. To add a module to wrap instances of this class with, we can now call the &lt;code&gt;wrap&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Logging
  def make_noise
    puts &amp;quot;Started making noise&amp;quot;
    super
    puts &amp;quot;Finished making noise&amp;quot;
  end
end

class Bird
  extend Wrappable

  wrap Logging

  def make_noise
    puts &amp;quot;Chirp, chirp!&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s give this a try by creating a new instance of &lt;code&gt;Bird&lt;/code&gt; and calling the &lt;code&gt;make_noise&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;bird = Bird.new
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great! It works as expected. However, things start to behave a bit strange once we extend a second class with the &lt;code&gt;Wrappable&lt;/code&gt; module.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Powered
  def make_noise
    puts &amp;quot;Powering up&amp;quot;
    super
    puts &amp;quot;Shutting down&amp;quot;
  end
end

class Machine
  extend Wrappable

  wrap Powered

  def make_noise
    puts &amp;quot;Buzzzzzz&amp;quot;
  end
end

machine = Machine.new
machine.make_noise
# Powering up
# Started making noise
# Buzzzzzz
# Finished making noise
# Shutting down

bird = Bird.new
bird.make_noise
# Powering up
# Started making noise
# Chirp, chirp!
# Finished making noise
# Shutting down
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Even though &lt;code&gt;Machine&lt;/code&gt; hasn&amp;#39;t been wrapped with the &lt;code&gt;Logging&lt;/code&gt; module, it still outputs logging information. What’s worse - even the bird is now powering up and down. That can’t be right, can it?&lt;/p&gt;
&lt;p&gt;The root of this problem lies in the way we are storing the modules. The class variable &lt;code&gt;@@wrappables&lt;/code&gt; is defined on the &lt;code&gt;Wrappable&lt;/code&gt; module and used whenever we add a new module, regardless of the class that &lt;code&gt;wrap&lt;/code&gt; is used in.&lt;/p&gt;
&lt;p&gt;This get’s more obvious when looking at the class variables defined on the &lt;code&gt;Wrappable&lt;/code&gt; module and the &lt;code&gt;Bird&lt;/code&gt; and &lt;code&gt;Machine&lt;/code&gt; classes. While &lt;code&gt;Wrappable&lt;/code&gt; has a class method defined, the two classes don&amp;#39;t.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Wrappable.class_variables # =&amp;gt; [:@@wrappers]
Bird.class_variables # =&amp;gt; []
Machine.class_variables # =&amp;gt; []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To fix this, we have to modify the implementation so that it uses instance variables. However, these aren&amp;#39;t variables on the instances of &lt;code&gt;Bird&lt;/code&gt; or &lt;code&gt;Machine&lt;/code&gt;, but instance variables on the classes themselves.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;In Ruby, classes are just objects&lt;/h2&gt;
  &lt;p&gt;
    This is definitely a bit mind boggling at first, but still a very important
    concept to understand. Classes are instances of &lt;code&gt;Class&lt;/code&gt; and
    writing &lt;code&gt;class Bird; end&lt;/code&gt; is equivalent to writing{&quot; &quot;}
    &lt;code&gt;Bird = Class.new&lt;/code&gt;. To make things even more confusing{&quot; &quot;}
    &lt;code&gt;Class&lt;/code&gt; inherits from &lt;code&gt;Module&lt;/code&gt; which inherits from{&quot; &quot;}
    &lt;code&gt;Object&lt;/code&gt;. As a result, classes and modules have the same methods
    as any other object. Most of the methods we use on classes (like the{&quot; &quot;}
    &lt;code&gt;attr_accessor&lt;/code&gt; macro) are actually instance methods of{&quot; &quot;}
    &lt;code&gt;Module&lt;/code&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;h2&gt;Using Instance Variables on Classes&lt;/h2&gt;
&lt;p&gt;Let’s change the &lt;code&gt;Wrappable&lt;/code&gt; implementation to use instance variables. To keep things a bit cleaner, we introduce a &lt;code&gt;wrappers&lt;/code&gt; method that either sets up the array or returns the existing one when the instance variable already exists. We also modify the &lt;code&gt;wrap&lt;/code&gt; and &lt;code&gt;new&lt;/code&gt; methods so that they utilize that new method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Wrappable
  def wrap(mod)
    wrappers &amp;lt;&amp;lt; mod
  end

  def wrappers
    @wrappers ||= []
  end

  def new(*arguments, &amp;amp;block)
    instance = allocate
    wrappers.each { |mod| instance.singleton_class.include(mod) }
    instance.send(:initialize, *arguments, &amp;amp;block)
    instance
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we check the instance variables on the module and on the two classes, we can see that both &lt;code&gt;Bird&lt;/code&gt; and &lt;code&gt;Machine&lt;/code&gt; now maintain their own collection of wrapping modules.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Wrappable.instance_variables #=&amp;gt; []
Bird.instance_variables #=&amp;gt; [:@wrappers]
Machine.instance_variables #=&amp;gt; [:@wrappers]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not surprisingly, this also fixes the problem we observed earlier - now, both classes are wrapped with their own individual modules.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;bird = Bird.new
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise

machine = Machine.new
machine.make_noise
# Powering up
# Buzzzzzz
# Shutting down
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Supporting Inheritance&lt;/h2&gt;
&lt;p&gt;This all works great until inheritance is introduced. We would expect that classes would inherit the wrapping modules from the superclass. Let’s check if that&amp;#39;s the case.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Flying
  def make_noise
    super
    puts &amp;quot;Is flying away&amp;quot;
  end
end

class Pigeon &amp;lt; Bird
  wrap Flying

  def make_noise
    puts &amp;quot;Coo!&amp;quot;
  end
end

pigeon = Pigeon.new
pigeon.make_noise
# Coo!
# Is flying away
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, it doesn’t work as expected, because &lt;code&gt;Pigeon&lt;/code&gt; is also maintaining its own collection of wrapping modules. While it makes sense that wrapping modules defined for &lt;code&gt;Pigeon&lt;/code&gt; aren’t defined on &lt;code&gt;Bird&lt;/code&gt;, it’s not exactly what we want. Let’s figure out a way to get all wrappers from the entire inheritance chain.&lt;/p&gt;
&lt;p&gt;Lucky for us, Ruby provides the &lt;code&gt;Module#ancestors&lt;/code&gt; method to list all the classes and modules a class (or module) inherits from.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Pigeon.ancestors # =&amp;gt; [Pigeon, Bird, Object, Kernel, BasicObject]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By adding a &lt;code&gt;grep&lt;/code&gt; call, we can pick the ones that are actually extended with &lt;code&gt;Wrappable&lt;/code&gt;. As we want to wrap the instances with wrappers from higher up the chain first, we call &lt;code&gt;.reverse&lt;/code&gt; to flip the order.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Pigeon.ancestors.grep(Wrappable).reverse # =&amp;gt; [Bird, Pigeon]
&lt;/code&gt;&lt;/pre&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;
    Ruby’s &lt;code&gt;#===&lt;/code&gt; method
  &lt;/h2&gt;
  &lt;p&gt;
    Some of Ruby’s magic comes down to the &lt;code&gt;#===&lt;/code&gt; (or{&quot; &quot;}
    &lt;em&gt;case equality&lt;/em&gt;) method. By default, it behaves just like the{&quot; &quot;}
    &lt;code&gt;#==&lt;/code&gt; (or &lt;em&gt;equality&lt;/em&gt;) method. However, several classes
    override the &lt;code&gt;#===&lt;/code&gt; method to provide different behavior in{&quot; &quot;}
    &lt;code&gt;case&lt;/code&gt; statements. This is how you can use regular expressions (
    &lt;code&gt;#===&lt;/code&gt; is equivalent to &lt;code&gt;#match?&lt;/code&gt;), or classes (
    &lt;code&gt;#===&lt;/code&gt; is equivalent to &lt;code&gt;#kind_of?&lt;/code&gt;) in those
    statements. Methods like &lt;code&gt;Enumerable#grep&lt;/code&gt;,{&quot; &quot;}
    &lt;code&gt;Enumerable#all?&lt;/code&gt;, or &lt;code&gt;Enumerable#any?&lt;/code&gt; also rely on
    the case equality method.
  &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Now we can call &lt;code&gt;flat_map(&amp;amp;:wrappers)&lt;/code&gt; to get a list of all wrappers defined in the inheritance chain as a single array.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;Pigeon.ancestors.grep(Wrappable).reverse.flat_map(&amp;amp;:wrappers) # =&amp;gt; [Logging]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All that&amp;#39;s left is packing that into an &lt;code&gt;inherited_wrappers&lt;/code&gt; module and slightly modifying the new method so that it uses that instead of the &lt;code&gt;wrappers&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Wrappable
  def inherited_wrappers
    ancestors
      .grep(Wrappable)
      .reverse
      .flat_map(&amp;amp;:wrappers)
  end

  def new(*arguments, &amp;amp;block)
    instance = allocate
    inherited_wrappers.each { |mod|instance.singleton_class.include(mod) }
    instance.send(:initialize, *arguments, &amp;amp;block)
    instance
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A final test run confirms that everything is now working as expected. The wrapping modules are only applied to the class (and its subclasses) they are applied on.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;bird = Bird.new
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise

machine = Machine.new
machine.make_noise
# Powering up
# Buzzzzz
# Shutting down

pigeon = Pigeon.new
pigeon.make_noise
# Started making noise
# Coo!
# Finished making noise
# Is flying away
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;That&amp;#39;s a wrap!&lt;/h2&gt;
&lt;p&gt;Admittedly, these noisy birds are a bit of a theoretic example (tweet, tweet). But inheritable class instance variables are not just cool to understand how classes work. They are a great example that classes are just objects in Ruby.&lt;/p&gt;
&lt;p&gt;And we&amp;#39;ll admit that inheritable class instance variables might even be quite useful in real life. For example, think about defining attributes and relationships on a model with the ability to introspect them later. For us the magic is to play around with this and get a better understanding of how things work. And open your mind for a next level of solutions. 🧙🏼‍♀️&lt;/p&gt;
&lt;p&gt;As always, we’re looking forward to hearing what you build using this or similar patterns. Just chirp to &lt;a href=&quot;http://twitter.com/AppSignal&quot;&gt;@AppSignal&lt;/a&gt; on Twitter.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Closures in Ruby: Blocks, Procs and Lambdas
</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/09/04/ruby-magic-closures-in-ruby-blocks-procs-and-lambdas.html"/>
    <id>https://blog.appsignal.com/2018/09/04/ruby-magic-closures-in-ruby-blocks-procs-and-lambdas.html</id>
    <published>2018-09-04T00:00:00+00:00</published>
    <updated>2018-09-04T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In Ruby Magic we love to dive into the magic behind the things we use every day to understand how they work. In this edition, we’ll explore the differences between blocks, procs and lambdas.</summary>
    <content type="html">&lt;p&gt;In Ruby Magic we love to dive into the magic behind the things we use every day to understand how they work. In this edition, we’ll explore the differences between blocks, procs and lambdas.&lt;/p&gt;
&lt;p&gt;In programming languages with first-class functions, functions can be stored in variables and passed as arguments to other functions. Functions can even use other functions as their return values.&lt;/p&gt;
&lt;p&gt;A closure is a first-class function with an environment. The environment is a mapping to the variables that existed when the closure was created. The closure will retain its access to these variables, even if they’re defined in another scope.&lt;/p&gt;
&lt;p&gt;Ruby doesn’t have first-class functions, but it does have closures in the form of blocks, procs and lambdas. Blocks are used for passing blocks of code to methods, and procs and lambda’s allow storing blocks of code in variables.&lt;/p&gt;
&lt;h2&gt;Blocks&lt;/h2&gt;
&lt;p&gt;In Ruby, &lt;em&gt;blocks&lt;/em&gt; are snippets of code that can be created to be executed later. Blocks are passed to methods that yield them within the &lt;code&gt;do&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; keywords. One of the many examples is the &lt;code&gt;#each&lt;/code&gt; method, which loops over &lt;a href=&quot;/2018/05/29/ruby-magic-enumerable-and-enumerator.html&quot;&gt;enumerable&lt;/a&gt; objects.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;[1,2,3].each do |n|
  puts &amp;quot;#{n}!&amp;quot;
end

[1,2,3].each { |n| puts &amp;quot;#{n}!&amp;quot; } # the one-line equivalent.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, a block is passed to the &lt;code&gt;Array#each&lt;/code&gt; method, which runs the block for each item in the array and prints it to the console.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def each
  i = 0
  while i &amp;lt; size
    yield at(i)
    i += 1
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this simplified example of &lt;code&gt;Array#each&lt;/code&gt;, in the &lt;code&gt;while&lt;/code&gt; loop, &lt;code&gt;yield&lt;/code&gt; is called to execute the passed block for every item in the array. Note that this method has no arguments, as the block is passed to the method implicitly.&lt;/p&gt;
&lt;h3&gt;Implicit Blocks and the &lt;code&gt;yield&lt;/code&gt; Keyword&lt;/h3&gt;
&lt;p&gt;In Ruby, methods can take blocks implicitly and explicitly. Implicit block passing works by calling the &lt;code&gt;yield&lt;/code&gt; keyword in a method. The &lt;code&gt;yield&lt;/code&gt; keyword is special. It finds and calls a passed block, so you don&amp;#39;t have to add the block to the list of arguments the method accepts.&lt;/p&gt;
&lt;p&gt;Because Ruby allows implicit block passing, you can call all methods with a block. If it doesn’t call &lt;code&gt;yield&lt;/code&gt;, the block is ignored.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; &amp;quot;foo bar baz&amp;quot;.split { p &amp;quot;block!&amp;quot; }
=&amp;gt; [&amp;quot;foo&amp;quot;, &amp;quot;bar&amp;quot;, &amp;quot;baz&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the called method &lt;em&gt;does&lt;/em&gt; yield, the passed block is found and called with any arguments that were passed to the &lt;code&gt;yield&lt;/code&gt; keyword.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def each
  return to_enum(:each) unless block_given?

  i = 0
  while i &amp;lt; size
    yield at(i)
    i += 1
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example &lt;a href=&quot;/2018/05/29/ruby-magic-enumerable-and-enumerator.html&quot;&gt;returns an instance of &lt;code&gt;Enumerator&lt;/code&gt;&lt;/a&gt; unless a block is given.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;yield&lt;/code&gt; and &lt;code&gt;block_given?&lt;/code&gt; keywords find the block in the current scope. This allows passing blocks implicitly, but prevents the code from accessing the block directly as it&amp;#39;s not stored in a variable.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h3&gt;Explicitly Passing Blocks&lt;/h3&gt;
&lt;p&gt;We can explicitly accept a block in a method by adding it as an argument using an ampersand parameter (usually called &lt;code&gt;&amp;amp;block&lt;/code&gt;). Since the block is now explicit, we can use the &lt;code&gt;#call&lt;/code&gt; method directly on the resulting object instead of relying on &lt;code&gt;yield&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;amp;block&lt;/code&gt; argument is not a proper argument, so calling this method with anything else than a block will produce an &lt;code&gt;ArgumentError&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def each_explicit(&amp;amp;block)
  return to_enum(:each) unless block

  i = 0
  while i &amp;lt; size
    block.call at(i)
    i += 1
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a block is passed like this and stored in a variable, it is automatically converted to a &lt;em&gt;proc&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Procs&lt;/h2&gt;
&lt;p&gt;A &amp;quot;proc&amp;quot; is an instance of the &lt;code&gt;Proc&lt;/code&gt; class, which holds a code block to be executed, and can be stored in a variable. To create a proc, you call &lt;code&gt;Proc.new&lt;/code&gt; and pass it a block.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;proc = Proc.new { |n| puts &amp;quot;#{n}!&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since a proc can be stored in a variable, it can also be passed to a method just like a normal argument. In that case, we don&amp;#39;t use the ampersand, as the proc is passed explicitly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def run_proc_with_random_number(proc)
  proc.call(random)
end

proc = Proc.new { |n| puts &amp;quot;#{n}!&amp;quot; }
run_proc_with_random_number(proc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of creating a proc and passing that to the method, you can use Ruby’s ampersand parameter syntax that we saw earlier and use a block instead.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def run_proc_with_random_number(&amp;amp;proc)
  proc.call(random)
end

run_proc_with_random_number { |n| puts &amp;quot;#{n}!&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the added ampersand to the argument in the method. This will convert a passed block to a proc object and store it in a variable in the method scope.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Tip&lt;/em&gt;: While it&amp;#39;s useful to have the proc in the method in some situations, the conversion of a block to a proc produces a performance hit. Whenever possible, use implicit blocks instead.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;#to_proc&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Symbols, hashes and methods can be converted to procs using their &lt;code&gt;#to_proc&lt;/code&gt; methods. A frequently seen use of this is passing a proc created from a symbol to a method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;[1,2,3].map(&amp;amp;:to_s)
[1,2,3].map {|i| i.to_s }
[1,2,3].map {|i| i.send(:to_s) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example shows three equivalent ways of calling &lt;code&gt;#to_s&lt;/code&gt; on each element of the array. In the first one, a symbol, prefixed with an ampersand, is passed, which automatically converts it to a proc by calling its &lt;code&gt;#to_proc&lt;/code&gt; method. The last two show what that proc could look like.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Symbol
  def to_proc
    Proc.new { |i| i.send(self) }
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although this is a simplified example, the implementation of &lt;code&gt;Symbol#to_proc&lt;/code&gt; shows what’s happening under the hood. The method returns a proc which takes one argument and sends &lt;code&gt;self&lt;/code&gt; to it. Since &lt;code&gt;self&lt;/code&gt; is the symbol in this context, it calls the &lt;code&gt;Integer#to_s&lt;/code&gt; method.&lt;/p&gt;
&lt;h2&gt;Lambdas&lt;/h2&gt;
&lt;p&gt;Lambdas are essentially procs with some distinguishing factors. They are more like &amp;quot;regular&amp;quot; methods in two ways: they enforce the number of arguments passed when they&amp;#39;re called and they use &amp;quot;normal&amp;quot; returns.&lt;/p&gt;
&lt;p&gt;When calling a lambda that expects an argument without one, or if you pass an argument to a lambda that doesn&amp;#39;t expect it, Ruby raises an &lt;code&gt;ArgumentError&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; lambda (a) { a }.call
ArgumentError: wrong number of arguments (given 0, expected 1)
        from (irb):8:in `block in irb_binding&amp;#39;
        from (irb):8
        from /Users/jeff/.asdf/installs/ruby/2.3.0/bin/irb:11:in `&amp;lt;main&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also, a lambda treats the return keyword the same way a method does. When calling a proc, the program yields control to the code block in the proc. So, if the proc returns, the current scope returns. If a proc is called inside a function and calls &lt;code&gt;return&lt;/code&gt;, the function immediately returns as well.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def return_from_proc
  a = Proc.new { return 10 }.call
  puts &amp;quot;This will never be printed.&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function will yield control to the proc, so when it returns, the function returns. Calling the function in this example will never print the output and return 10.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def return_from_lambda
  a = lambda { return 10 }.call
  puts &amp;quot;The lambda returned #{a}, and this will be printed.&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using a lambda, it &lt;em&gt;will&lt;/em&gt; be printed. Calling &lt;code&gt;return&lt;/code&gt; in the lambda will behave like calling &lt;code&gt;return&lt;/code&gt; in a method, so the &lt;code&gt;a&lt;/code&gt; variable is populated with &lt;code&gt;10&lt;/code&gt; and the line is printed to the console.&lt;/p&gt;
&lt;h2&gt;Blocks, procs and lambdas&lt;/h2&gt;
&lt;p&gt;Now that we’ve gone all the way into both blocks, procs and lambdas, let’s zoom back out and summarize the comparison.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Blocks are used extensively in Ruby for passing bits of code to functions. By using the &lt;code&gt;yield&lt;/code&gt; keyword, a block can be implicitly passed without having to convert it to a proc.&lt;/li&gt;
&lt;li&gt;When using parameters prefixed with ampersands, passing a block to a method results in a proc in the method&amp;#39;s context. Procs behave like blocks, but they can be stored in a variable.&lt;/li&gt;
&lt;li&gt;Lambdas are procs that behave like methods, meaning they enforce arity and return as methods instead of in their parent scope.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This concludes our look into closures in Ruby. There&amp;#39;s more to learn about closures like lexical scopes and bindings, but we&amp;#39;ll keep that for a future episode. In the meantime, please let us know what you&amp;#39;d like to read about in a future installment of Ruby Magic, closures or otherwise at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Changing the Way Ruby Creates Objects</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/08/07/ruby-magic-changing-the-way-ruby-creates-objects.html"/>
    <id>https://blog.appsignal.com/2018/08/07/ruby-magic-changing-the-way-ruby-creates-objects.html</id>
    <published>2018-08-07T00:00:00+00:00</published>
    <updated>2018-08-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In this edition of Ruby Magic, we&#039;ll look at how Ruby creates and initializes objects and how we can modify the default behavior.</summary>
    <content type="html">&lt;p&gt;One of the things that makes Ruby great is that we can customize almost anything to our needs. This is both useful and dangerous. It&amp;#39;s easy to shoot ourselves in the foot, but when used carefully, this can result in pretty powerful solutions.&lt;/p&gt;
&lt;p&gt;At Ruby Magic, we think useful and dangerous is an excellent combination. Let&amp;#39;s look at how Ruby creates and initializes objects and how we can modify the default behavior.&lt;/p&gt;
&lt;h2&gt;The Basics of Creating New Objects from Classes&lt;/h2&gt;
&lt;p&gt;To get started, let&amp;#39;s see how to create objects in Ruby. To create a new &lt;em&gt;object&lt;/em&gt; (or &lt;em&gt;instance&lt;/em&gt;), we call &lt;code&gt;new&lt;/code&gt; on the class. Unlike other languages, &lt;code&gt;new&lt;/code&gt; isn&amp;#39;t a keyword of the language itself, but a method that gets called just like any other.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Dog
end

object = Dog.new
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to customize the newly created object, it is possible to pass arguments to the &lt;code&gt;new&lt;/code&gt; method. Whatever is passed as arguments, will get passed to the initializer.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Dog
  def initialize(name)
    @name = name
  end
end

object = Dog.new(&amp;#39;Good boy&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, unlike other languages, the initializer in Ruby is also just a method instead of some special syntax or keyword.&lt;/p&gt;
&lt;p&gt;With that in mind, shouldn&amp;#39;t it be possible to mess around with those methods, just like it is possible with any other Ruby method? Of course it is!&lt;/p&gt;
&lt;h2&gt;Modifying the Behavior of a Single Object&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s say we want to ensure that all objects of a particular class will always print log statements, even if the method is overridden in subclasses. One way to do this is to add a module to the object&amp;#39;s singleton class.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Logging
  def make_noise
    puts &amp;quot;Started making noise&amp;quot;
    super
    puts &amp;quot;Finished making noise&amp;quot;
  end
end

class Bird
  def make_noise
    puts &amp;quot;Chirp, chirp!&amp;quot;
  end
end

object = Bird.new
object.singleton_class.include(Logging)
object.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;In this example, a &lt;code&gt;Bird&lt;/code&gt; object is created using &lt;code&gt;Bird.new&lt;/code&gt;, and the &lt;code&gt;Logging&lt;/code&gt; module is included in the resulting object using its singleton class.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;What&#039;s a Singleton Class?&lt;/h2&gt;
  &lt;p&gt;
    Ruby allows methods that are unique to a single object. To support this,
    Ruby adds an anonymous class between the object and its actual class. When
    methods are called, the ones defined on the singleton class get precedence
    over the methods in the actual class. These singleton classes are unique to
    every object, so adding methods to them doesn&#039;t affect any other objects of
    the actual class.{&quot; &quot;}
    &lt;a href=&quot;https://ruby-doc.com/docs/ProgrammingRuby/html/classes.html&quot;&gt;
      Learn more about classes and objects in the Programming Ruby guide.
    &lt;/a&gt;
  &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;It&amp;#39;s a bit cumbersome to modify the singleton class of each object whenever it is created. So let&amp;#39;s move the inclusion of the &lt;code&gt;Logging&lt;/code&gt; class to the initializer to add it for every created object.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;module Logging
  def make_noise
    puts &amp;quot;Started making noise&amp;quot;
    super
    puts &amp;quot;Finished making noise&amp;quot;
  end
end

class Bird
  def initialize
    singleton_class.include(Logging)
  end

  def make_noise
    puts &amp;quot;Chirp, chirp!&amp;quot;
  end
end

object = Bird.new
object.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While this works well, if we create a subclass of &lt;code&gt;Bird&lt;/code&gt;, like &lt;code&gt;Duck&lt;/code&gt;, its initializer needs to call &lt;code&gt;super&lt;/code&gt; to retain the &lt;code&gt;Logging&lt;/code&gt; behavior. While one can argue that it&amp;#39;s always a good idea to properly call &lt;code&gt;super&lt;/code&gt; whenever a method is overridden, let&amp;#39;s try to find a way that doesn&amp;#39;t &lt;em&gt;require&lt;/em&gt; it.&lt;/p&gt;
&lt;p&gt;If we don&amp;#39;t call &lt;code&gt;super&lt;/code&gt; from the subclass, we lose the inclusion of the &lt;code&gt;Logger&lt;/code&gt; class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Duck &amp;lt; Bird
  def initialize(name)
    @name = name
  end

  def make_noise
    puts &amp;quot;#{@name}: Quack, quack!&amp;quot;
  end
end

object = Duck.new(&amp;#39;Felix&amp;#39;)
object.make_noise
# Felix: Quack, quack!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, Let&amp;#39;s override &lt;code&gt;Bird.new&lt;/code&gt;. As mentioned before, &lt;code&gt;new&lt;/code&gt; is just a method implemented on classes. So we can override it, call super, and modify the newly created object to our needs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Bird
  def self.new(*arguments, &amp;amp;block)
    instance = super
    instance.singleton_class.include(Logging)
    instance
  end
end

object = Duck.new(&amp;#39;Felix&amp;#39;)
object.make_noise
# Started making noise
# Felix: Quack, quack!
# Finished making noise
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But, what happens when we call &lt;code&gt;make_noise&lt;/code&gt; in the initializer? Unfortunately, because the singleton class doesn&amp;#39;t include the &lt;code&gt;Logging&lt;/code&gt; module yet, we won&amp;#39;t get the desired output.&lt;/p&gt;
&lt;p&gt;Luckily, there&amp;#39;s a solution: It&amp;#39;s possible to create the default &lt;code&gt;.new&lt;/code&gt; behavior from scratch by calling &lt;code&gt;allocate&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Bird
  def self.new(*arguments, &amp;amp;block)
    instance = allocate
    instance.singleton_class.include(Logging)
    instance.send(:initialize, *arguments, &amp;amp;block)
    instance
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling &lt;code&gt;allocate&lt;/code&gt; returns a new, uninitialized object of the class. So afterward, we can include the additional behavior and only then, call the &lt;code&gt;initialize&lt;/code&gt; method on that object. (Because &lt;code&gt;initialize&lt;/code&gt; is private by default, we have to resort to using &lt;code&gt;send&lt;/code&gt; for this).&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;
    The Truth About &lt;code&gt;Class#allocate&lt;/code&gt;
  &lt;/h2&gt;
  &lt;p&gt;
    Unlike other methods, it&#039;s not possible to override &lt;code&gt;allocate&lt;/code&gt;.
    Ruby doesn&#039;t use the conventional way of dispatching methods for{&quot; &quot;}
    &lt;code&gt;allocate&lt;/code&gt; internally. As a result, just overriding{&quot; &quot;}
    &lt;code&gt;allocate&lt;/code&gt; without also overriding &lt;code&gt;new&lt;/code&gt; doesn&#039;t work.
    However, if we&#039;re calling &lt;code&gt;allocate&lt;/code&gt; directly, Ruby will call the
    redefined method.{&quot; &quot;}
    &lt;a href=&quot;https://ruby-doc.org/core-2.5.0/Class.html#method-i-allocate&quot;&gt;
      Learn more about &lt;code&gt;Class#new&lt;/code&gt; and &lt;code&gt;Class#allocate&lt;/code&gt; in
      Ruby&#039;s documentation.
    &lt;/a&gt;
  &lt;/p&gt;
&lt;/div&gt;

&lt;h2&gt;Why Would We Do This?&lt;/h2&gt;
&lt;p&gt;As with a lot of things, modifying the way Ruby creates objects from classes can be dangerous and things might break in unexpected ways.&lt;/p&gt;
&lt;p&gt;Nonetheless, there are valid use cases for changing the object creation. For instance, ActiveRecord uses &lt;code&gt;allocate&lt;/code&gt; with a different &lt;code&gt;init_from_db&lt;/code&gt; method to change the initialization process when creating objects from the database as opposed to building unsaved objects. It also uses &lt;code&gt;allocate&lt;/code&gt; to convert records between different &lt;a href=&quot;https://en.wikipedia.org/wiki/Single_Table_Inheritance&quot;&gt;single-table inheritance&lt;/a&gt; types with &lt;code&gt;becomes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Most important, by playing around with object creation, you get a deeper insight into how it works in Ruby and open your mind to different solutions. We hope you enjoyed the article.&lt;/p&gt;
&lt;p&gt;We&amp;#39;d love to hear about the things you implemented by changing Ruby&amp;#39;s default way of creating objects. Please don&amp;#39;t hesitate to tweet your thoughts to &lt;a href=&quot;http://twitter.com/AppSignal&quot;&gt;@AppSignal&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby Magic Summer Special: The Best of AppSignal Academy
</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/07/26/ruby-magic-summer-special-best-of-appsignal-academy.html"/>
    <id>https://blog.appsignal.com/2018/07/26/ruby-magic-summer-special-best-of-appsignal-academy.html</id>
    <published>2018-07-26T00:00:00+00:00</published>
    <updated>2018-07-26T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">The best of AppSignal Academy: 5 of our favorite Ruby articles
</summary>
    <content type="html">&lt;p&gt;Remember when you were a kid, and you bought those omnibus double edition comics? How magical reading them was? We won&amp;#39;t pretend this will be as unforgettable, but we do think we&amp;#39;ve put together a nice extra-chunky Best-of &lt;a href=&quot;/category/academy.html&quot;&gt;AppSignal Academy&lt;/a&gt; selection for you to read at the beach, the park, or just on a lazy Sunday, that will absolutely make your day.&lt;/p&gt;
&lt;p&gt;Here are our top 5 hammock reads:&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;/2018/07/03/custom-exceptions-in-ruby.html&quot;&gt;Custom Exceptions in Ruby&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Almost everything in Ruby is an object and errors are no exception. #dadjokes&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;/2018/05/16/ensure-retry-and-reraise-exceptions-in-ruby.html&quot;&gt;Ensuring Execution, Retrying Failures and Re-raising Exceptions in Ruby&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Besides &lt;code&gt;rescue&lt;/code&gt;, Ruby has more ways to handle exceptions. We&amp;#39;ll use the &lt;code&gt;retry&lt;/code&gt; and &lt;code&gt;ensure&lt;/code&gt; keywords as well as reraised exceptions to build a resilient web API client.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;/2018/06/05/redo-retry-next.html&quot;&gt;Ruby&amp;#39;s Redo, Retry and Next Keywords&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The post about &lt;code&gt;retry&lt;/code&gt; was super popular last holiday. Its little-known counterpart &lt;code&gt;redo&lt;/code&gt; works similarly, but reruns loop iterations instead of whole blocks.&lt;/p&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;h2&gt;&lt;a href=&quot;/2018/04/24/active-record-performance-the-n-1-queries-antipattern.html&quot;&gt;ActiveRecord Performance: The N+1 Queries Anti-Pattern&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Like squirrels in a summer forest, the N+1 queries problem is a common, but usually easy to spot nuisance, ehm, performance anti-pattern that&amp;#39;s sometimes caused by lazy loading associations.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;/2018/04/03/russian-doll-caching-in-rails.html&quot;&gt;Russian Doll Caching in Rails&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By nesting cache fragments, views are almost never rendered completely. Even when data changes, most of the rendered pages are served straight from the cache.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Under the Hood: “Slurping” and Streaming Files in Ruby
</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/07/10/ruby-magic-slurping-and-streaming-files.html"/>
    <id>https://blog.appsignal.com/2018/07/10/ruby-magic-slurping-and-streaming-files.html</id>
    <published>2018-07-10T00:00:00+00:00</published>
    <updated>2018-07-10T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Streaming files line by line is often preferred over “slurping” the whole content at once. In this edition of Ruby Magic, we&#039;ll learn how streaming files and other I/O streams works in Ruby.</summary>
    <content type="html">&lt;p&gt;In this edition of Ruby Magic, we&amp;#39;ll learn about streaming files in Ruby, how the &lt;code&gt;IO&lt;/code&gt; class handles reading files without completely loading them into memory, and how it reads files per line by buffering read bytes. Let&amp;#39;s dive right in!&lt;/p&gt;
&lt;h2&gt;“Slurping” and Streaming Files&lt;/h2&gt;
&lt;p&gt;Ruby&amp;#39;s &lt;code&gt;File.read&lt;/code&gt; method reads a file and returns its full content.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; content = File.read(&amp;quot;log/production.log&amp;quot;)
=&amp;gt; &amp;quot;I, [2018-06-27T16:45:02.843719 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Started GET \&amp;quot;/articles\&amp;quot; for 127.0.0.1 at 2018-06-27 16:45:02 +0200\nI, [2018-06-27T16:45:02.846719 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Processing by ArticlesController#index as HTML\nI, [2018-06-27T16:45:02.848212 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Rendering articles/index.html.erb within layouts/application\nD, [2018-06-27T16:45:02.850020 #9098] DEBUG -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Article Load (0.3ms)  SELECT \&amp;quot;articles\&amp;quot;.* FROM \&amp;quot;articles\&amp;quot;\nI, [2018-06-27T16:45:02.850901 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Rendered articles/index.html.erb within layouts/application (1.7ms)\nI, [2018-06-27T16:45:02.851633 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Completed 200 OK in 5ms (Views: 3.4ms | ActiveRecord: 0.3ms)\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Internally, this opens the file, reads its content, closes the file, and returns the content as a single string. By &amp;quot;slurping&amp;quot; the file&amp;#39;s content at once, it&amp;#39;s kept in memory until it’s cleaned up by Ruby’s garbage collector.&lt;/p&gt;
&lt;p&gt;As an example, let&amp;#39;s say we&amp;#39;d like to uppercase all characters in a file and write it to another file. Using &lt;code&gt;File.read&lt;/code&gt;, we can get the content, call &lt;code&gt;String#upcase&lt;/code&gt; on the resulting string, and pass the uppercased string to &lt;code&gt;File.write&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; upcased = File.read(&amp;quot;log/production.log&amp;quot;).upcase
=&amp;gt; &amp;quot;I, [2018-06-27T16:45:02.843719 #9098]  INFO -- : [86A5D18C-19DD-4CBF-9D7A-461C79E98C22] STARTED GET \&amp;quot;/ARTICLES\&amp;quot; FOR 127.0.0.1 AT 2018-06-27 16:45:02 +0200\nI, [2018-06-27T16:45:02.846719 #9098]  INFO -- : [86A5D18C-19DD-4CBF-9D7A-461C79E98C22] PROCESSING BY ARTICLESCONTROLLER#INDEX AS HTML\nI, [2018-06-27T16:45:02.848212 #9098]  INFO -- : [86A5D18C-19DD-4CBF-9D7A-461C79E98C22]   RENDERING ARTICLES/INDEX.HTML.ERB WITHIN LAYOUTS/APPLICATION\nD, [2018-06-27T16:45:02.850020 #9098] DEBUG -- : [86A5D18C-19DD-4CBF-9D7A-461C79E98C22]   ARTICLE LOAD (0.3MS)  SELECT \&amp;quot;ARTICLES\&amp;quot;.* FROM \&amp;quot;ARTICLES\&amp;quot;\nI, [2018-06-27T16:45:02.850901 #9098]  INFO -- : [86A5D18C-19DD-4CBF-9D7A-461C79E98C22]   RENDERED ARTICLES/INDEX.HTML.ERB WITHIN LAYOUTS/APPLICATION (1.7MS)\nI, [2018-06-27T16:45:02.851633 #9098]  INFO -- : [86A5D18C-19DD-4CBF-9D7A-461C79E98C22] COMPLETED 200 OK IN 5MS (VIEWS: 3.4MS | ACTIVERECORD: 0.3MS)\n&amp;quot;
irb&amp;gt; File.write(&amp;quot;log/upcased.log&amp;quot;, upcased)
=&amp;gt; 896
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While that works for small files, reading the whole file into memory might be problematic when dealing with larger files. For instance, when parsing a 14-gigabyte log file, reading the whole file at once would be an expensive operation. The content of the file is kept in memory, so the app&amp;#39;s memory footprint grows considerably. This can eventually lead to memory swapping and the OS killing the app&amp;#39;s process.&lt;/p&gt;
&lt;p&gt;Luckily, Ruby allows reading files line by line using &lt;code&gt;File.foreach&lt;/code&gt;. Instead of reading the file&amp;#39;s full content at once, it will execute a passed block for each line.&lt;/p&gt;
&lt;p&gt;Its result is &lt;a href=&quot;/2018/05/29/ruby-magic-enumerable-and-enumerator.html&quot;&gt;enumerable&lt;/a&gt;, therefore it either yields a block for each line, or returns an Enumerator object if no block is passed. This enables the reading of bigger files without having to load all their content into memory at once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; File.foreach(&amp;quot;log/production.log&amp;quot;) { |line| p line }
&amp;quot;I, [2018-06-27T16:45:02.843719 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Started GET \&amp;quot;/articles\&amp;quot; for 127.0.0.1 at 2018-06-27 16:45:02 +0200\n&amp;quot;
&amp;quot;I, [2018-06-27T16:45:02.846719 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Processing by ArticlesController#index as HTML\n&amp;quot;
&amp;quot;I, [2018-06-27T16:45:02.848212 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Rendering articles/index.html.erb within layouts/application\n&amp;quot;
&amp;quot;D, [2018-06-27T16:45:02.850020 #9098] DEBUG -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Article Load (0.3ms)  SELECT \&amp;quot;articles\&amp;quot;.* FROM \&amp;quot;articles\&amp;quot;\n&amp;quot;
&amp;quot;I, [2018-06-27T16:45:02.850901 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Rendered articles/index.html.erb within layouts/application (1.7ms)\n&amp;quot;
&amp;quot;I, [2018-06-27T16:45:02.851633 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Completed 200 OK in 5ms (Views: 3.4ms | ActiveRecord: 0.3ms)\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To uppercase a whole file, we read from the input file line by line, uppercase it, and append it to the output file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; File.open(&amp;quot;upcased.log&amp;quot;, &amp;quot;a&amp;quot;) do |output|
irb*   File.foreach(&amp;quot;production.log&amp;quot;) { |line| output.write(line.upcase) }
irb&amp;gt; end
=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, how does reading a file line by line work without having to first read the whole file? To understand that, we’ll have to peel back some of the layers around reading files. Let&amp;#39;s take a closer look at Ruby&amp;#39;s &lt;code&gt;IO&lt;/code&gt; class.&lt;/p&gt;
&lt;h2&gt;I/O and Ruby&amp;#39;s &lt;code&gt;IO&lt;/code&gt; Class&lt;/h2&gt;
&lt;p&gt;Even though &lt;code&gt;File.read&lt;/code&gt; and &lt;code&gt;File.foreach&lt;/code&gt; exist, the &lt;a href=&quot;https://ruby-doc.org/core-2.5.1/File.html&quot;&gt;documentation for the &lt;code&gt;File&lt;/code&gt; class&lt;/a&gt; doesn’t list them. In fact, you won’t find any of the file reading or writing methods in the &lt;code&gt;File&lt;/code&gt; class documentation, because they are inherited from the parent &lt;code&gt;IO&lt;/code&gt; class.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;I/O&lt;/h2&gt;
&lt;p&gt;An &lt;em&gt;I/O device&lt;/em&gt; is a device that transfers data to or from a computer, for example keyboards, displays and hard drives. It performs &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Input/output&quot;&gt;Input/Output&lt;/a&gt;&lt;/em&gt;, or &lt;em&gt;I/O&lt;/em&gt;, by reading or producing streams of data. &lt;/p&gt;

&lt;p&gt;Reading and writing files from the hard drive is the most common I/O you’ll encounter. Other types of I/O include socket communication, logging output to your terminal and input from your keyboard.&lt;/p&gt;
&lt;/div&gt;

&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;The &lt;code&gt;IO&lt;/code&gt; class in Ruby handles all input and output like reading and writing to files. Because reading files isn&amp;#39;t different than reading from any other I/O stream, the &lt;code&gt;File&lt;/code&gt; class directly inherits methods like &lt;code&gt;IO.read&lt;/code&gt; and &lt;code&gt;IO.foreach&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; IO.foreach(&amp;quot;log/production.log&amp;quot;) { |line| p line }
&amp;quot;I, [2018-06-27T16:45:02.843719 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Started GET \&amp;quot;/articles\&amp;quot; for 127.0.0.1 at 2018-06-27 16:45:02 +0200\n&amp;quot;
&amp;quot;I, [2018-06-27T16:45:02.846719 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Processing by ArticlesController#index as HTML\n&amp;quot;
&amp;quot;I, [2018-06-27T16:45:02.848212 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Rendering articles/index.html.erb within layouts/application\n&amp;quot;
&amp;quot;D, [2018-06-27T16:45:02.850020 #9098] DEBUG -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Article Load (0.3ms)  SELECT \&amp;quot;articles\&amp;quot;.* FROM \&amp;quot;articles\&amp;quot;\n&amp;quot;
&amp;quot;I, [2018-06-27T16:45:02.850901 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22]   Rendered articles/index.html.erb within layouts/application (1.7ms)\n&amp;quot;
&amp;quot;I, [2018-06-27T16:45:02.851633 #9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Completed 200 OK in 5ms (Views: 3.4ms | ActiveRecord: 0.3ms)\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;File.foreach&lt;/code&gt; is equivalent to &lt;code&gt;IO.foreach&lt;/code&gt;, so the &lt;code&gt;IO&lt;/code&gt; class version can be used to get the same result we did previously.&lt;/p&gt;
&lt;h2&gt;Reading I/O Streams Via the Kernel&lt;/h2&gt;
&lt;p&gt;Internally, Ruby&amp;#39;s &lt;code&gt;IO&lt;/code&gt; class&amp;#39; reading and writing abilities are based on abstractions around &lt;a href=&quot;https://en.wikipedia.org/wiki/System_call&quot;&gt;kernel system calls&lt;/a&gt;. The operating system&amp;#39;s &lt;a href=&quot;https://en.wikipedia.org/wiki/Kernel_(operating_system)&quot;&gt;kernel&lt;/a&gt; takes care of reading from and writing to I/O devices.&lt;/p&gt;
&lt;h3&gt;Opening Files&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;IO.sysopen&lt;/code&gt; opens a file by asking the kernel to put a reference to the file in the file table and creating a file descriptor in the process&amp;#39; file descriptor table.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;File Descriptors and the File Table&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Open_(system_call)&quot;&gt;Opening&lt;/a&gt; a file returns a &lt;a href=&quot;https://en.wikipedia.org/wiki/File_descriptor&quot;&gt;file descriptor&lt;/a&gt; — an integer used to access the I/O resource.&lt;/p&gt;

&lt;p&gt;
  Each process has its own file descriptor table to keep the file descriptors in
  memory, and each descriptor points to an entry in the system-wide{&quot; &quot;}
  &lt;em&gt;file table&lt;/em&gt;.{&quot; &quot;}
&lt;/p&gt;

&lt;p&gt;To read from or write to an I/O resource, the process passes the file descriptor to the kernel through a system call. The kernel then accesses the file on behalf of the process, as processes don’t have access to the file table.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Opening files will &lt;em&gt;not&lt;/em&gt; keep their content in memory, but the file descriptor table can get filled up, so it’s a good practice to always close files after opening them. Methods that wrap &lt;code&gt;File.open&lt;/code&gt; like &lt;code&gt;File.read&lt;/code&gt; do this automatically, as well as the ones taking a block.&lt;/p&gt;
&lt;p&gt;In this example, we&amp;#39;ll go one step further by calling the &lt;code&gt;IO.sysopen&lt;/code&gt; method directly. By passing a filename, the method creates a file descriptor we can use to reference the open file later.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; IO.sysopen(&amp;quot;log/production.log&amp;quot;)
=&amp;gt; 9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To create an &lt;code&gt;IO&lt;/code&gt; instance for Ruby to read from and write to, we pass the file descriptor to &lt;code&gt;IO.new&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; file_descriptor = IO.sysopen(&amp;quot;log/production.log&amp;quot;)
=&amp;gt; 9
irb&amp;gt; io = IO.new(file_descriptor)
=&amp;gt; #&amp;lt;IO:fd 9&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To close an I/O stream and remove the reference to the file from the files table, we call &lt;code&gt;IO#close&lt;/code&gt; on the &lt;code&gt;IO&lt;/code&gt; instance.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;irb&amp;gt; io.close
=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Reading Bytes and Moving Cursors&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;IO#sysread&lt;/code&gt; reads a number of bytes from an &lt;code&gt;IO&lt;/code&gt; object.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; io.sysread(64)
=&amp;gt; &amp;quot; [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Started GET \&amp;quot;/articles\&amp;quot; &amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example uses the &lt;code&gt;IO&lt;/code&gt; instance we created previously by passing the file descriptor integer to &lt;code&gt;IO.new&lt;/code&gt;. It reads and returns the first 64 bytes from the file by calling &lt;code&gt;IO#sysread&lt;/code&gt; with 64 as its argument.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; io.sysread(64)
=&amp;gt; &amp;quot;for 127.0.0.1 at 2018-06-27 16:45:02 +0200\nI, [2018-06-27T16:45:&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first time we requested bytes from the file, the cursor was moved automatically, so calling &lt;code&gt;IO#sysread&lt;/code&gt; on the same instance again will produce the next 64 bytes of the file.&lt;/p&gt;
&lt;h3&gt;Moving the Cursor&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;IO.sysseek&lt;/code&gt; manually moves the cursor to a location in the file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; io.sysseek(32)
=&amp;gt; 32
irb&amp;gt; io.sysread(64)
=&amp;gt; &amp;quot;9098]  INFO -- : [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Started &amp;quot;
irb&amp;gt; io.sysseek(0)
=&amp;gt; 0
irb&amp;gt; io.sysread(64)
=&amp;gt; &amp;quot; [86a5d18c-19dd-4cbf-9d7a-461c79e98c22] Started GET \&amp;quot;/articles\&amp;quot; &amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we move to position 32, then read 64 bytes using &lt;code&gt;IO#sysread&lt;/code&gt;. By calling &lt;code&gt;IO.sysseek&lt;/code&gt; again with 0, we jump back to the beginning of the file, allowing us to read the first 64 bytes again.&lt;/p&gt;
&lt;h2&gt;Reading Files Line by Line&lt;/h2&gt;
&lt;p&gt;Now, we know how the &lt;code&gt;IO&lt;/code&gt; class&amp;#39;s convenience methods open IO streams, read bytes from them and how they move the cursor&amp;#39;s position.&lt;/p&gt;
&lt;p&gt;Methods like &lt;code&gt;IO.foreach&lt;/code&gt; and &lt;code&gt;IO#gets&lt;/code&gt; can request lines line by line instead of per number of bytes. There&amp;#39;s no performant way of looking ahead to find the next newline and take all bytes until that position, so Ruby needs to take care of splitting the file&amp;#39;s content.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyIO
  def initialize(filename)
    fd = IO.sysopen(filename)
    @io = IO.new(fd)
  end

  def each(&amp;amp;block)
    line = &amp;quot;&amp;quot;

    while (c = @io.sysread(1)) != $/
      line &amp;lt;&amp;lt; c
    end

    block.call(line)
    each(&amp;amp;block)
  rescue EOFError
    @io.close
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example implementation, the &lt;code&gt;#each&lt;/code&gt; method takes bytes from the file using &lt;code&gt;IO#sysread&lt;/code&gt; one at a time, until the byte is &lt;code&gt;$/&lt;/code&gt;, indicating a newline. When it finds a newline, it stops taking bytes and calls the passed block with that line.&lt;/p&gt;
&lt;p&gt;This solution works but is inefficient as it calls &lt;code&gt;IO.sysread&lt;/code&gt; for every byte in the file.&lt;/p&gt;
&lt;h3&gt;Buffering File Content&lt;/h3&gt;
&lt;p&gt;Ruby is smarter about how it does this by keeping an internal buffer of the file&amp;#39;s content. Instead of reading the file one byte at a time, it takes 512 bytes at once and checks if there are any newlines in the returned bytes. If there are, it returns the portion before the newline and keeps the rest in memory as a buffer. If the buffer doesn&amp;#39;t include a newline, it fetches 512 bytes more until it finds one.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyIO
  def initialize(filename)
    fd = IO.sysopen(filename)
    @io = IO.new(fd)
    @buffer = &amp;quot;&amp;quot;
  end

  def each(&amp;amp;block)
    @buffer &amp;lt;&amp;lt; @io.sysread(512) until @buffer.include?($/)

    line, @buffer = @buffer.split($/, 2)

    block.call(line)
    each(&amp;amp;block)
  rescue EOFError
    @io.close
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;#each&lt;/code&gt; method adds bytes to an internal &lt;code&gt;@buffer&lt;/code&gt; variable in chunks of 512 bytes until when the &lt;code&gt;@buffer&lt;/code&gt; variable includes a newline. When that happens, it splits the buffer by the first newline. The first part is the &lt;code&gt;line&lt;/code&gt;, and the second part is the new buffer.&lt;/p&gt;
&lt;p&gt;The passed block is then called with the line and the remaining &lt;code&gt;@buffer&lt;/code&gt; is kept for use in the next loop.&lt;/p&gt;
&lt;p&gt;By buffering the file&amp;#39;s content, the number of I/O calls is reduced while dividing the file in logical chunks.&lt;/p&gt;
&lt;h2&gt;Streaming Files&lt;/h2&gt;
&lt;p&gt;To summarize, streaming files works by asking the operating system&amp;#39;s kernel to open a file, then read bytes from it bit by bit. When reading a file per line in Ruby, data is taken from the file 512 bytes at a time and split up in &amp;quot;lines&amp;quot; after that.&lt;/p&gt;
&lt;p&gt;This concludes our overview of I/O and streaming files in Ruby. We’d love to know what you thought of this article, or if you have any questions. We’re always on the lookout for topics to investigate and explain, so if there’s anything magical in Ruby you’d like to read about, don’t hesitate to let us now at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt;!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ruby&#039;s magical Enumerable module</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/05/29/ruby-magic-enumerable-and-enumerator.html"/>
    <id>https://blog.appsignal.com/2018/05/29/ruby-magic-enumerable-and-enumerator.html</id>
    <published>2018-05-29T00:00:00+00:00</published>
    <updated>2018-05-29T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Ruby&#039;s handling of enumerables is one of it&#039;s most magical features. In this episode of Ruby Magic, we&#039;ll learn how these work and how to make your own objects tenumerable.</summary>
    <content type="html">&lt;p&gt;It&amp;#39;s time for another episode of Ruby Magic! This time, we&amp;#39;ll look at one of Ruby&amp;#39;s most magical features, which provides most of the methods you&amp;#39;ll use when working with Ruby&amp;#39;s enumerable classes like &lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;Hash&lt;/code&gt; and &lt;code&gt;Range&lt;/code&gt;. In the process, we&amp;#39;ll learn what you can do with enumerable objects, how enumeration works, and how to make an object enumerable by implementing a single method.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;Enumerable&lt;/code&gt;, &lt;code&gt;#each&lt;/code&gt; and &lt;code&gt;Enumerator&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Enumeration&lt;/em&gt; refers to traversing over objects. In Ruby, we call an object &lt;em&gt;enumerable&lt;/em&gt; when it describes a set of items and a method to loop over each of them.&lt;/p&gt;
&lt;p&gt;The built-in enumerables get their enumeration features by including the &lt;code&gt;Enumerable&lt;/code&gt; module, which provides methods like &lt;code&gt;#include?&lt;/code&gt;, &lt;code&gt;#count&lt;/code&gt;, &lt;code&gt;#map&lt;/code&gt;, &lt;code&gt;#select&lt;/code&gt; and &lt;code&gt;#uniq&lt;/code&gt;, amongst others. Most of the methods associated with arrays and hashes aren&amp;#39;t actually implemented in these classes themselves, they&amp;#39;re included.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Some methods, like &lt;code&gt;#count&lt;/code&gt; and &lt;code&gt;#take&lt;/code&gt; on the &lt;code&gt;Array&lt;/code&gt; class, &lt;em&gt;are&lt;/em&gt; implemented specifically for arrays instead of using the ones from the &lt;code&gt;Enumerable&lt;/code&gt; module. That&amp;#39;s usually done to make operation faster.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://ruby-doc.org/core-2.5.1/Enumerable.html&quot;&gt;&lt;code&gt;Enumerable&lt;/code&gt; module&lt;/a&gt; relies on a method named &lt;code&gt;#each&lt;/code&gt;, which needs to be implemented in any class it&amp;#39;s included in. When called with a block on an array, the &lt;code&gt;#each&lt;/code&gt; method will execute the block for each of the array&amp;#39;s elements.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; [1,2,3].each { |i| puts &amp;quot;* #{i}&amp;quot; }
* 1
* 2
* 3
=&amp;gt; [1,2,3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we call the &lt;code&gt;#each&lt;/code&gt; method on an array &lt;em&gt;without&lt;/em&gt; passing a block to execute for each of its elements, we&amp;#39;ll receive an instance of &lt;code&gt;Enumerator&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; [1,2,3].each
=&amp;gt; #&amp;lt;Enumerator: [1, 2, 3]:each&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instances of &lt;code&gt;Enumerator&lt;/code&gt; describe how to iterate over an object. Enumerators &lt;a href=&quot;https://ruby-doc.org/core-2.5.1/Enumerator.html#method-i-next&quot;&gt;iterate over objects manually&lt;/a&gt; and chain enumeration.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; %w(dog cat mouse).each.with_index { |a, i| puts &amp;quot;#{a} is at position #{i}&amp;quot; }
dog is at position 0
cat is at position 1
mouse is at position 2
=&amp;gt; [&amp;quot;dog&amp;quot;, &amp;quot;cat&amp;quot;, &amp;quot;mouse&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;#with_index&lt;/code&gt; method is a good example of how changed enumerators work. In this example, &lt;code&gt;#each&lt;/code&gt; is called on the array to return an enumerator. Then, &lt;code&gt;#with_index&lt;/code&gt; is called to add indices to each of the array&amp;#39;s elements to allow printing each element&amp;#39;s index.&lt;/p&gt;
&lt;h2&gt;Making objects enumerable&lt;/h2&gt;
&lt;p&gt;Under the hood, methods like &lt;code&gt;#max&lt;/code&gt;, &lt;code&gt;#map&lt;/code&gt; and &lt;code&gt;#take&lt;/code&gt; rely on the &lt;code&gt;#each&lt;/code&gt; method to function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def max
  max = nil

  each do |item|
    if !max || item &amp;gt; max
      max = item
    end
  end

  max
end
&lt;/code&gt;&lt;/pre&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Internally, &lt;code&gt;Enumerable&lt;/code&gt;&amp;#39;s methods have C implementations, but the example above roughly shows how &lt;code&gt;#max&lt;/code&gt; works. By using &lt;code&gt;#each&lt;/code&gt; to loop over all values and remembering the highest, it returns the maximum value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def map(&amp;amp;block)
  new_list = []

  each do |item|
    new_list &amp;lt;&amp;lt; block.call(item)
  end

  new_list
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;#map&lt;/code&gt; function calls the passed block with each item and puts the result into a new list to return after looping over all values.&lt;/p&gt;
&lt;p&gt;Since all methods in &lt;code&gt;Enumerable&lt;/code&gt; use the &lt;code&gt;#each&lt;/code&gt; method to some extent, our first step in making a custom class enumerable is implementing the &lt;code&gt;#each&lt;/code&gt; method.&lt;/p&gt;
&lt;h2&gt;Implementing &lt;code&gt;#each&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;By implementing the &lt;code&gt;#each&lt;/code&gt; function and including the &lt;code&gt;Enumerable&lt;/code&gt; module in a class, it becomes enumerable and receives methods like &lt;code&gt;#min&lt;/code&gt;, &lt;code&gt;#take&lt;/code&gt; and &lt;code&gt;#inject&lt;/code&gt; for free.&lt;/p&gt;
&lt;p&gt;Although most situations allow falling back to an existing object like an array and calling the &lt;code&gt;#each&lt;/code&gt; method on that, let&amp;#39;s look at an example where we have to write it ourselves from scratch. In this example, we&amp;#39;ll implement &lt;code&gt;#each&lt;/code&gt; on a &lt;em&gt;linked list&lt;/em&gt; to make it enumerable.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h3&gt;Linked lists: lists without arrays&lt;/h3&gt;
  &lt;p&gt;A &lt;a href=&quot;https://en.wikipedia.org/wiki/Linked_list&quot;&gt;linked list&lt;/a&gt; is a collection of data elements, in which each element points to the next. Each element in the list has two values, named the &lt;em&gt;head&lt;/em&gt; and the &lt;em&gt;tail&lt;/em&gt;. The head holds the element’s value, and the tail is a link to the rest of the list.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[42, [12, [73, nil]]
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;For a linked list with three values (42, 12 and 73), the first element’s head is 42, and the tail is a link to the second element. The second element’s head is 12, and the tail holds the third element. The third element’s head is 73, and the tail is &lt;code&gt;nil&lt;/code&gt;, which indicates the end of the list.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In Ruby, a linked list can be created by using a class that holds two instance variables named &lt;code&gt;@head&lt;/code&gt; and &lt;code&gt;@tail&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LinkedList
  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end

  def &amp;lt;&amp;lt;(item)
    LinkedList.new(item, self)
  end

  def inspect
    [@head, @tail].inspect
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;#&amp;lt;&amp;lt;&lt;/code&gt; method is used to add new values to the list, which works by returning a new list with the passed value as the head, and the previous list as the tail.&lt;/p&gt;
&lt;p&gt;In this example, the &lt;code&gt;#inspect&lt;/code&gt; method is added so we can see into the list to check which elements it contains.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; LinkedList.new(73) &amp;lt;&amp;lt; 12 &amp;lt;&amp;lt; 42
=&amp;gt; [42, [12, [73, nil]]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have a linked list, let&amp;#39;s implement &lt;code&gt;#each&lt;/code&gt; on it. The &lt;code&gt;#each&lt;/code&gt; function takes a block and executes it for each value in the object. When implementing it on our linked list, we can use the list&amp;#39;s recursive nature to our advantage by calling the passed block on the list&amp;#39;s &lt;code&gt;@head&lt;/code&gt;, and calling &lt;code&gt;#each&lt;/code&gt; on the &lt;code&gt;@tail&lt;/code&gt;, if it exists.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LinkedList
  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end

  def &amp;lt;&amp;lt;(item)
    LinkedList.new(item, self)
  end

  def inspect
    [@head, @tail].inspect
  end

  def each(&amp;amp;block)
    block.call(@head)
    @tail.each(&amp;amp;block) if @tail
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When calling &lt;code&gt;#each&lt;/code&gt; on an instance of our linked list, it calls the passed block with current &lt;code&gt;@head&lt;/code&gt;. Then, it calls each on the linked list in &lt;code&gt;@tail&lt;/code&gt; unless the tail is &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; list = LinkedList.new(73) &amp;lt;&amp;lt; 12 &amp;lt;&amp;lt; 42
=&amp;gt; [42, [12, [73, nil]]]
irb&amp;gt; list.each { |item| puts item }
42
12
73
=&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that our linked list responds to &lt;code&gt;#each&lt;/code&gt;, we can &lt;code&gt;include Enumberable&lt;/code&gt; to make our list enumerable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LinkedList
  include Enumerable

  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end

  def &amp;lt;&amp;lt;(item)
    LinkedList.new(item, self)
  end

  def inspect
    [@head, @tail].inspect
  end

  def each(&amp;amp;block)
    block.call(@head)
    @tail.each(&amp;amp;block) if @tail
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; list = LinkedList.new(73) &amp;lt;&amp;lt; 12 &amp;lt;&amp;lt; 42
=&amp;gt; [42, [12, [73, nil]]]
irb&amp;gt; list.count
=&amp;gt; 3
irb&amp;gt; list.max
=&amp;gt; 73
irb&amp;gt; list.map { |item| item * item }
=&amp;gt; [1764, 144, 5329]
irb&amp;gt; list.select(&amp;amp;:even?)
=&amp;gt; [42, 12]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Returning &lt;code&gt;Enumerator&lt;/code&gt; instances&lt;/h2&gt;
&lt;p&gt;We can now loop over all values in our linked list, but we can&amp;#39;t chain enumerable functions yet. To do that, we&amp;#39;ll need to return an &lt;code&gt;Enumerator&lt;/code&gt; instance when our &lt;code&gt;#each&lt;/code&gt; function is called without a block.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class LinkedList
  include Enumerable

  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end

  def &amp;lt;&amp;lt;(item)
    LinkedList.new(item, self)
  end

  def inspect
    [@head, @tail].inspect
  end

  def each(&amp;amp;block)
    if block_given?
      block.call(@head)
      @tail.each(&amp;amp;block) if @tail
    else
      to_enum(:each)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To wrap an object in an enumerator, we call the &lt;code&gt;#to_enum&lt;/code&gt; method on it. We pass &lt;code&gt;:each&lt;/code&gt;, as that&amp;#39;s the method the enumerator should be using internally.&lt;/p&gt;
&lt;p&gt;Now, calling our &lt;code&gt;#each&lt;/code&gt; method without a block will allow us to chain enumeration.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;irb&amp;gt; list = LinkedList.new(73) &amp;lt;&amp;lt; 12 &amp;lt;&amp;lt; 42
=&amp;gt; [42, [12, [73, nil]]]
irb&amp;gt; list.each
=&amp;gt; #&amp;lt;Enumerator: [42, [12, [73, nil]]]:each&amp;gt;
irb&amp;gt; list.map.with_index.to_h
=&amp;gt; {42=&amp;gt;0, 12=&amp;gt;1, 73=&amp;gt;2}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Nine lines of code and an include&lt;/h2&gt;
&lt;p&gt;By implementing &lt;code&gt;#each&lt;/code&gt; using the &lt;code&gt;Enumerable&lt;/code&gt; module and returning &lt;code&gt;Enumerator&lt;/code&gt; objects from our own, we were able to supercharge our linked list by adding nine lines of code and an include.&lt;/p&gt;
&lt;p&gt;This concludes our overview of enumerables in Ruby. We&amp;#39;d love to know what you thought of this article, or if you have any questions. We&amp;#39;re always on the lookout for topics to investigate and explain, so if there&amp;#39;s anything magical in Ruby you&amp;#39;d like to read about, don&amp;#39;t hesitate to let us now at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt;!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>ActiveRecord performance: the N+1 queries antipattern</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/04/24/active-record-performance-the-n-1-queries-antipattern.html"/>
    <id>https://blog.appsignal.com/2018/04/24/active-record-performance-the-n-1-queries-antipattern.html</id>
    <published>2018-04-24T00:00:00+00:00</published>
    <updated>2018-04-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">The N+1 queries problem is a common, but usually easy to spot, performance antipattern that&#039;s sometimes caused by lazy loading associations.</summary>
    <content type="html">&lt;p&gt;At AppSignal we help developers with application performance. We&amp;#39;re monitoring a huge number of apps that send billions of requests. We thought we could also help a bit with a few blogposts about Ruby and performance. The N+1 queries problem is a common antipattern in Rails applications.&lt;/p&gt;
&lt;p&gt;A lot of &lt;a href=&quot;https://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORMs&lt;/a&gt;, like Rails&amp;#39; ActiveRecord, have lazy loading built in to allow you to defer querying associations until the moment they&amp;#39;re needed. It allows being implicit about which associations need to be loaded by offloading this decision to the view.&lt;/p&gt;
&lt;p&gt;The N+1 queries problem is a common, but usually easy to spot, performance antipattern that results in running a query for each association, which causes overhead when querying a large number of associations from the database.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;👋 By the way, if you like this article, there is a lot more we wrote about &lt;a href=&quot;https://www.appsignal.com/ruby&quot;&gt;Ruby (on Rails) performance&lt;/a&gt;, check out our &lt;a href=&quot;https://www.appsignal.com/ruby#ruby-monitoring-checklist&quot;&gt;Ruby performance monitoring checklist&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Lazy loading in ActiveRecord&lt;/h2&gt;
&lt;p&gt;ActiveRecord uses implicit lazy loading to make it easier to work with relations. Let&amp;#39;s consider the webshop example, where each &lt;em&gt;Product&lt;/em&gt; can have any number of &lt;em&gt;Variants&lt;/em&gt; which contain the product&amp;#39;s color or size, for example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/models/product.rb
class Product &amp;lt; ActiveRecord::Base
  has_many :variants
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;ProductsController#show&lt;/code&gt;, the detail view for one of the products, we&amp;#39;ll use &lt;code&gt;Product.find(params[:id])&lt;/code&gt; to get the product and assign it to the &lt;code&gt;@product&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/products_controller.rb
class ProductsController &amp;lt; ApplicationController
  def show
    @product = Product.find(params[:id])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the view for this action, we&amp;#39;ll loop over the product&amp;#39;s variants by calling the &lt;code&gt;variants&lt;/code&gt; method on the &lt;code&gt;@product&lt;/code&gt; variable we received from the controller.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/products/show.html.erb
&amp;lt;h1&amp;gt;&amp;lt;%= @product.title %&amp;gt;&amp;lt;/h1&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;%= @product.variants.each do |variant| %&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;%= variant.name %&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By calling &lt;code&gt;@product.variants&lt;/code&gt; in the view, Rails will query the database to get the variants for us to loop over. Aside from the explicit query we did in the controller, we can see another query is executed to fetch the variants if we check Rails&amp;#39; logs for this request.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/products/1&amp;quot; for 127.0.0.1 at 2018-04-19 08:49:13 +0200
Processing by ProductsController#show as HTML
  Parameters: {&amp;quot;id&amp;quot;=&amp;gt;&amp;quot;1&amp;quot;}
  Product Load (1.1ms)  SELECT  &amp;quot;products&amp;quot;.* FROM &amp;quot;products&amp;quot; WHERE &amp;quot;products&amp;quot;.&amp;quot;id&amp;quot; = ? LIMIT ?  [[&amp;quot;id&amp;quot;, 1], [&amp;quot;LIMIT&amp;quot;, 1]]
  Rendering products/show.html.erb within layouts/application
  Variant Load (1.1ms)  SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; = ?  [[&amp;quot;product_id&amp;quot;, 1]]
  Rendered products/show.html.erb within layouts/application (4.4ms)
Completed 200 OK in 64ms (Views: 56.4ms | ActiveRecord: 2.3ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This request executed two queries to show a product with all of its variants.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;SELECT &amp;quot;products&amp;quot;.* FROM &amp;quot;products&amp;quot; WHERE &amp;quot;products&amp;quot;.&amp;quot;id&amp;quot; = 1 LIMIT 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; = 1&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Looped lazy loading&lt;/h2&gt;
&lt;p&gt;Lazy loading has been great so far. By using an implicit query, we don&amp;#39;t have to remember to remove it from the controller when we decide we don&amp;#39;t want to show the variants on this view anymore, for example.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s say we&amp;#39;re working on &lt;code&gt;ProductsController#index&lt;/code&gt;, where we&amp;#39;d like to show a list of all products with each of their variants. We can implement that with lazy loading the same way as we did before.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/products_controller.rb
class ProductsController &amp;lt; ApplicationController
  def index
    @products = Product.all
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-erb&quot;&gt;# app/views/products/index.html.erb
&amp;lt;h1&amp;gt;Products&amp;lt;/h1&amp;gt;

&amp;lt;% @products.each do |product| %&amp;gt;
&amp;lt;article&amp;gt;
  &amp;lt;h1&amp;gt;&amp;lt;%= product.title %&amp;gt;&amp;lt;/h1&amp;gt;

  &amp;lt;ul&amp;gt;
    &amp;lt;% product.variants.each do |variant| %&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;%= variant.description %&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/article&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike the first example we now get a list of products from the controller instead of a single one. The view then loops over each product, and lazy loads each variant for each product.&lt;/p&gt;
&lt;p&gt;While this works, there is one catch. Our query count is now &lt;em&gt;N+1&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;N+1 queries&lt;/h2&gt;
&lt;p&gt;In the first example, we rendered a view for a single product and its variants. The &lt;em&gt;query count&lt;/em&gt; was 2 because we executed two queries. This request returned all products (3, in this example) from the database, and each of their variants, and it did four queries instead of two.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/products&amp;quot; for 127.0.0.1 at 2018-04-19 09:49:02 +0200
Processing by ProductsController#index as HTML
  Rendering products/index.html.erb within layouts/application
  Product Load (0.3ms)  SELECT &amp;quot;products&amp;quot;.* FROM &amp;quot;products&amp;quot;
  Variant Load (0.2ms)  SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; = ?  [[&amp;quot;product_id&amp;quot;, 1]]
  Variant Load (0.2ms)  SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; = ?  [[&amp;quot;product_id&amp;quot;, 2]]
  Variant Load (0.1ms)  SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; = ?  [[&amp;quot;product_id&amp;quot;, 3]]
  Rendered products/index.html.erb within layouts/application (5.6ms)
Completed 200 OK in 36ms (Views: 32.6ms | ActiveRecord: 0.8ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;SELECT &amp;quot;products&amp;quot;.* FROM &amp;quot;products&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; = 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; = 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; = 3&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first query, which is executed by the explicit call to &lt;code&gt;Product.all&lt;/code&gt; in the controller, finds all products. The subsequent ones are lazily executed while looping over each product in the view.&lt;/p&gt;
&lt;p&gt;This example results in a query count of N+1, where N is the number of products, and the added one is the explicit query that fetched all products. In other words; this example does one query, and then another one for each of the results in the first query. Because N = 3 in this example, the resulting query count is &lt;code&gt;N + 1 = 3 + 1 = 4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;While this might not really be a problem when having only three products, the query count goes up with the number of products. Because we know this request has N+1 queries, we can predict a query count of 101 when we have 100 products (&lt;code&gt;N + 1 = 100 + 1 = 101&lt;/code&gt;), for example.&lt;/p&gt;
&lt;h2&gt;Eager loading associations&lt;/h2&gt;
&lt;p&gt;Instead of increasing the number of queries with the number of products like we do now, we&amp;#39;d like to have a static number of requests in this view. We can do that by explicitly preloading the variants in the controller before rendering the view.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# app/controllers/products_controller.rb
class ProductsController &amp;lt; ApplicationController
  def index
    @products = Product.all.includes(:variants)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ActiveRecord&amp;#39;s &lt;code&gt;includes&lt;/code&gt; query method makes sure the associated variants are loaded with their products. Because it knows which variants need to be loaded beforehand, it can fetch all variants of all requested products in one query.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Started GET &amp;quot;/products&amp;quot; for 127.0.0.1 at 2018-04-19 10:33:59 +0200
Processing by ProductsController#index as HTML
  Rendering products/index.html.erb within layouts/application
  Product Load (0.3ms)  SELECT &amp;quot;products&amp;quot;.* FROM &amp;quot;products&amp;quot;
  Variant Load (0.4ms)  SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; IN (?, ?, ?)  [[&amp;quot;product_id&amp;quot;, 1], [&amp;quot;product_id&amp;quot;, 2], [&amp;quot;product_id&amp;quot;, 3]]
  Rendered products/index.html.erb within layouts/application (5.9ms)
  Completed 200 OK in 45ms (Views: 40.8ms | ActiveRecord: 0.7ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By preloading the variants, the query count drops back to 2, even if the number of products increases in the future.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;SELECT &amp;quot;products&amp;quot;.* FROM &amp;quot;products&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SELECT &amp;quot;variants&amp;quot;.* FROM &amp;quot;variants&amp;quot; WHERE &amp;quot;variants&amp;quot;.&amp;quot;product_id&amp;quot; IN (1, 2, 3)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Lazy or eager?&lt;/h2&gt;
&lt;p&gt;In most situations, getting all associated records from the database in a single query is a lot faster than lazy loading them.&lt;/p&gt;
&lt;p&gt;In this example application, the database performance difference is measurable with only three products, each having ten variants. On average, eager loading the products list is about 12.5% faster (0.7 ms vs 0.8 ms) than lazy loading. With ten products, that difference jumps to 59% (1.22 ms vs 2.98 ms). With 1000 products, the difference is almost 80%, as the eager queries clock in at 58.4 ms, while lazy loading them takes about 290.12 ms.&lt;/p&gt;
&lt;p&gt;Although lazily-loaded associations give more flexibility in the view without having to update the controller, a good rule of thumb is to have the controller handle loading the data before passing it off to the view.&lt;/p&gt;
&lt;p&gt;Lazy loading from the view works for views that show one model object and it&amp;#39;s associations (like the &lt;code&gt;ProductsController#show&lt;/code&gt; in our first example) and can be useful when having multiple views that require different data from the same controller, for example.&lt;/p&gt;
&lt;h2&gt;Cats and Dolls&lt;/h2&gt;
&lt;p&gt;Cats might not agree, but sometimes it pays of to be eager rather than lazy. In this post we dove into the lazy loading in ActiveRecord and showed an example of the situations in which this can create a performance issue. Like when it leads to the N+1 queries problem.&lt;/p&gt;
&lt;p&gt;In short: always keep an eye on the development logs, or the &lt;a href=&quot;https://www.appsignal.com/tour/performance&quot;&gt;event timeline in AppSignal&lt;/a&gt;, to make sure you’re not doing queries that could be lazy loaded and keep track of your response times, especially when the amount of data that’s processed increases.&lt;/p&gt;
&lt;p&gt;If you liked this, check out some more things we wrote on performance and monitoring, like this favorite about &lt;a href=&quot;/2018/04/03/russian-doll-caching-in-rails.html&quot;&gt;Russian Doll Caching&lt;/a&gt; or this one about &lt;a href=&quot;/2018/05/01/client-side-caching-in-rails-conditional-get-requests.html&quot;&gt;Conditional Get Requests&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Benchmarking Ruby Code</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/02/27/benchmarking-ruby-code.html"/>
    <id>https://blog.appsignal.com/2018/02/27/benchmarking-ruby-code.html</id>
    <published>2018-02-27T00:00:00+00:00</published>
    <updated>2018-02-27T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">When working on an important piece of code in your codebase, running benchmarks to compare different implementations can give more insight into their execution speeds.</summary>
    <content type="html">&lt;p&gt;Ruby has a benchmarking tool in its standard library to help measure the performance of your code. It&amp;#39;s most useful when comparing two implementations, to find out which is fastest.&lt;/p&gt;
&lt;p&gt;In this example, we&amp;#39;re tasked with converting a Hash with string keys (like &lt;code&gt;{&amp;quot;foo&amp;quot; =&amp;gt; &amp;quot;bar&amp;quot;}&lt;/code&gt; to one with symbols (like &lt;code&gt;{:foo =&amp;gt; &amp;quot;bar&amp;quot;}&lt;/code&gt;). Throughout the examples, we&amp;#39;ll use a hash with a key and a value for each letter in the English alphabet.&lt;/p&gt;
&lt;p&gt;To quickly generate this hash without having to type it out, we&amp;#39;ll convert a range of letters to our testing hash. We&amp;#39;ll put it in the &lt;code&gt;input&lt;/code&gt; variable to use later.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;input = (&amp;quot;a&amp;quot;..&amp;quot;z&amp;quot;).map {|letter| [letter, letter]}.to_h
# =&amp;gt; {&amp;quot;a&amp;quot;=&amp;gt;&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;=&amp;gt;&amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;=&amp;gt;&amp;quot;c&amp;quot;, &amp;quot;d&amp;quot;=&amp;gt;&amp;quot;d&amp;quot;, &amp;quot;e&amp;quot;=&amp;gt;&amp;quot;e&amp;quot;, &amp;quot;f&amp;quot;=&amp;gt;&amp;quot;f&amp;quot;, &amp;quot;g&amp;quot;=&amp;gt;&amp;quot;g&amp;quot;, &amp;quot;h&amp;quot;=&amp;gt;&amp;quot;h&amp;quot;, &amp;quot;i&amp;quot;=&amp;gt;&amp;quot;i&amp;quot;, &amp;quot;j&amp;quot;=&amp;gt;&amp;quot;j&amp;quot;, &amp;quot;k&amp;quot;=&amp;gt;&amp;quot;k&amp;quot;, &amp;quot;l&amp;quot;=&amp;gt;&amp;quot;l&amp;quot;, &amp;quot;m&amp;quot;=&amp;gt;&amp;quot;m&amp;quot;, &amp;quot;n&amp;quot;=&amp;gt;&amp;quot;n&amp;quot;, &amp;quot;o&amp;quot;=&amp;gt;&amp;quot;o&amp;quot;, &amp;quot;p&amp;quot;=&amp;gt;&amp;quot;p&amp;quot;, &amp;quot;q&amp;quot;=&amp;gt;&amp;quot;q&amp;quot;, &amp;quot;r&amp;quot;=&amp;gt;&amp;quot;r&amp;quot;, &amp;quot;s&amp;quot;=&amp;gt;&amp;quot;s&amp;quot;, &amp;quot;t&amp;quot;=&amp;gt;&amp;quot;t&amp;quot;, &amp;quot;u&amp;quot;=&amp;gt;&amp;quot;u&amp;quot;, &amp;quot;v&amp;quot;=&amp;gt;&amp;quot;v&amp;quot;, &amp;quot;w&amp;quot;=&amp;gt;&amp;quot;w&amp;quot;, &amp;quot;x&amp;quot;=&amp;gt;&amp;quot;x&amp;quot;, &amp;quot;y&amp;quot;=&amp;gt;&amp;quot;y&amp;quot;, &amp;quot;z&amp;quot;=&amp;gt;&amp;quot;z&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have our &lt;code&gt;input&lt;/code&gt; variable to test our implementations with, we&amp;#39;ll write one to see how it performs. A nice one-liner to convert all keys in our input hash to symbols instead of strings looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;input.map { |key, value| [key.to_sym, value] }.to_h
# =&amp;gt; {:a=&amp;gt;&amp;quot;a&amp;quot;, :b=&amp;gt;&amp;quot;b&amp;quot;, :c=&amp;gt;&amp;quot;c&amp;quot;, :d=&amp;gt;&amp;quot;d&amp;quot;, :e=&amp;gt;&amp;quot;e&amp;quot;, :f=&amp;gt;&amp;quot;f&amp;quot;, :g=&amp;gt;&amp;quot;g&amp;quot;, :h=&amp;gt;&amp;quot;h&amp;quot;, :i=&amp;gt;&amp;quot;i&amp;quot;, :j=&amp;gt;&amp;quot;j&amp;quot;, :k=&amp;gt;&amp;quot;k&amp;quot;, :l=&amp;gt;&amp;quot;l&amp;quot;, :m=&amp;gt;&amp;quot;m&amp;quot;, :n=&amp;gt;&amp;quot;n&amp;quot;, :o=&amp;gt;&amp;quot;o&amp;quot;, :p=&amp;gt;&amp;quot;p&amp;quot;, :q=&amp;gt;&amp;quot;q&amp;quot;, :r=&amp;gt;&amp;quot;r&amp;quot;, :s=&amp;gt;&amp;quot;s&amp;quot;, :t=&amp;gt;&amp;quot;t&amp;quot;, :u=&amp;gt;&amp;quot;u&amp;quot;, :v=&amp;gt;&amp;quot;v&amp;quot;, :w=&amp;gt;&amp;quot;w&amp;quot;, :x=&amp;gt;&amp;quot;x&amp;quot;, :y=&amp;gt;&amp;quot;y&amp;quot;, :z=&amp;gt;&amp;quot;z&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implementation uses the &lt;code&gt;map&lt;/code&gt; method to loop over the hash to run a block for each key-value pair. In the block, it converts the key to a symbol and returns a two-element array with the newly created symbol key, and the untouched value.&lt;/p&gt;
&lt;p&gt;The result from the &lt;code&gt;map&lt;/code&gt; command is an array with 26 key-value arrays. Since we need a hash, we use &lt;code&gt;#to_h&lt;/code&gt; to convert our new array back into a hash.&lt;/p&gt;
&lt;h2&gt;Benchmark.measure&lt;/h2&gt;
&lt;p&gt;Now that we have a working implementation, we can use &lt;a href=&quot;https://ruby-doc.org/stdlib-2.5.0/libdoc/benchmark/rdoc/Benchmark.html&quot;&gt;Ruby&amp;#39;s Benchmark module&lt;/a&gt; to see how it performs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;benchmark&amp;#39;

input = (&amp;#39;a&amp;#39;..&amp;#39;z&amp;#39;).map { |letter| [letter, letter] }.to_h

puts Benchmark.measure {
  50_000.times do
    input.map { |key, value| [key.to_sym, value] }.to_h
  end
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Benchmark.measure&lt;/code&gt; takes a block, which is executed while keeping track of how long it took to execute. It returns a report string, which is printed to the console using &lt;code&gt;puts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since this is a quick piece of code, we run it 50.000 times to make sure we get some visible results.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ruby bench.rb
  0.810000   0.000000   0.810000 (  0.816964)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The report string shows four numbers, which represent the &lt;em&gt;user CPU time&lt;/em&gt; (the time spent executing your code), the &lt;em&gt;system CPU time&lt;/em&gt; (the time spent in the kernel), both user and system CPU time added up, and the actual time (or wall clock time) it took for the block to execute in brackets.&lt;/p&gt;
&lt;p&gt;The wall time shows us that we can run the block of code above 50.000 times in a little over 800 milliseconds. While that&amp;#39;s an impressive number, we don&amp;#39;t know what that means unless we compare it to another implementation of the code.&lt;/p&gt;
&lt;h2&gt;Benchmark.bm&lt;/h2&gt;
&lt;p&gt;Besides &lt;code&gt;Benchmark.measure&lt;/code&gt;, Ruby provides &lt;code&gt;Benchmark.bm&lt;/code&gt;, which can run multiple code samples and print their results. For each sample, we&amp;#39;ll call &lt;code&gt;Benchmark#report&lt;/code&gt; with a name, and the block to be executed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;require &amp;#39;benchmark&amp;#39;

input = (&amp;quot;a&amp;quot;..&amp;quot;z&amp;quot;).map { |letter| [letter, letter] }.to_h
n = 50_000

Benchmark.bm do |benchmark|
  benchmark.report(&amp;quot;Hash[]&amp;quot;) do
    n.times do
      input.map { |key, value| [key.to_sym, value] }.to_h
    end
  end

  benchmark.report(&amp;quot;{}.tap&amp;quot;) do
    n.times do
      {}.tap do |new_hash|
        input.each do |key, value|
          new_hash[key.to_sym] = value
        end
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this benchmark, we&amp;#39;ll use &lt;code&gt;Benchmark.bm&lt;/code&gt; to test two implementations by running each 50.000 times. The first measurement block is the same as the example from before.&lt;/p&gt;
&lt;p&gt;In the second measurement block, we use a longer implementation, which creates a new hash up front. It loops over the string-key hash, and adds an element to the new hash for every item. This way, it doesn&amp;#39;t have to convert the hash to an array, and back to a hash when it&amp;#39;s finished.&lt;/p&gt;
&lt;p&gt;Running the benchmark again will show us this implementation is more than 25% faster, although the code is longer (and a little less clever) than the one-liner we tried before.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ruby bench.rb
       user     system      total        real
Hash[]  0.850000   0.000000   0.850000 (  0.851106)
{}.tap  0.610000   0.020000   0.630000 (  0.637070)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;More benchmarking&lt;/h2&gt;
&lt;p&gt;When working on an important piece of code in your codebase, running benchmarks to compare different implementations can give more insight into their execution speeds. By comparing different implementations to understand how they impact performance, you&amp;#39;ll be able to avoid anti-patterns and write faster Ruby.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Tip&lt;/em&gt;: A lot of common idioms are pre-benchmarked, and their results are published as &lt;a href=&quot;https://github.com/JuanitoFatas/fast-ruby&quot;&gt;fast-ruby&lt;/a&gt;. Reading through the examples can save you some benchmarking in the future.&lt;/p&gt;
&lt;p&gt;There are more options you can test for this example, and the Ruby&amp;#39;s benchmarking library has a lot more sophisticated features you can try, but this gives a good introduction to how benchmarking works in Ruby. If you&amp;#39;d like to know more about benchmarking, or have any questions or suggestions, please let us know at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Syntactic sugar methods in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2018/02/20/ruby-magic-syntactic-sugar-methods.html"/>
    <id>https://blog.appsignal.com/2018/02/20/ruby-magic-syntactic-sugar-methods.html</id>
    <published>2018-02-20T00:00:00+00:00</published>
    <updated>2018-02-20T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Ever hear about Ruby&#039;s syntactic sugar, but never knew what it meant or how to use it? In this article we&#039;ll explore just how we can use it to our advantage.</summary>
    <content type="html">&lt;p&gt;Welcome to a new Ruby Magic article! In this episode, we&amp;#39;ll look at how Ruby uses &lt;em&gt;syntactic sugar&lt;/em&gt; to make some of its syntax more expressive, or easier to read. At the end, we&amp;#39;ll know how some of Ruby&amp;#39;s tricks work under the hood and how to write our own methods that use a bit of this sugar.&lt;/p&gt;
&lt;p&gt;When writing Ruby apps it&amp;#39;s common to interact with class attributes, arrays and hashes in a way that may feel non-standard. How would we define methods to assign attributes and fetch values from an array or hash?&lt;/p&gt;
&lt;p&gt;Ruby provides a bit of syntactic sugar to make these method work when calling them. In this post we&amp;#39;ll explore how that works.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;person1 = Person.new
person1.name = &amp;quot;John&amp;quot;

array = [:foo, :bar]
array[1]  # =&amp;gt; :bar

hash = { :key =&amp;gt; :foo }
hash[:key] # =&amp;gt; :foo
hash[:key] = :value
&lt;/code&gt;&lt;/pre&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;Syntactic sugar?&lt;/h2&gt;

  &lt;p&gt;Syntactic sugar refers to the little bit of ✨ magic ✨ Ruby provides you in writing easier to read and more concise code. In Ruby this means leaving out certain symbols, spaces or writing some expression with a helper of some kind.&lt;/p&gt;
&lt;/div&gt;

&lt;h2&gt;Method names&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s start with method names. In Ruby, we can use all kinds of characters and special symbols for method names that aren&amp;#39;t commonly supported in other languages. If you&amp;#39;ve ever written a Rails app you&amp;#39;ve probably encountered the &lt;code&gt;save!&lt;/code&gt; method. This isn&amp;#39;t something specific to Rails, but it demonstrates support for the &lt;code&gt;!&lt;/code&gt; character in Ruby method names.&lt;/p&gt;
&lt;p&gt;The same applies to other symbols such as &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;[&lt;/code&gt;, &lt;code&gt;]&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Support for these characters means we can incorporate them into our method names to be more explicit about what they&amp;#39;re for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assigning attributes: &lt;code&gt;person.name = &amp;quot;foo&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ask questions: &lt;code&gt;person.alive?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Call dangerous methods: &lt;code&gt;car.destroy!&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Making objects act like something they&amp;#39;re not: &lt;code&gt;car[:wheels]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Defining attribute methods&lt;/h2&gt;
&lt;p&gt;When defining an attribute on a class with &lt;code&gt;attr_accessor&lt;/code&gt;, Ruby creates a reader and a writer method for an instance variable on the class.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class Person
  attr_accessor :name
end

person = Person.new
person.name = &amp;quot;John&amp;quot;
person.name # =&amp;gt; &amp;quot;John&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Under the hood, Ruby creates two methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Person#name&lt;/code&gt; for reading the attribute/instance variable on the class using &lt;code&gt;attr_reader&lt;/code&gt;, and;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Person#name=&lt;/code&gt; for writing the attribute/instance variable on the class using &lt;code&gt;attr_writer&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;Banner lang=&quot;ruby&quot; /&gt;

&lt;p&gt;Now let&amp;#39;s say we want to customize this behavior. We won&amp;#39;t use the &lt;code&gt;attr_accessor&lt;/code&gt; helper and define the methods ourselves.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class AwesomePerson
  def name
    &amp;quot;Person name: #{@name}&amp;quot;
  end

  def name=(value)
    @name = &amp;quot;Awesome #{value}&amp;quot;
  end
end

person = AwesomePerson.new
person.name = &amp;quot;Jane&amp;quot;
person.name # =&amp;gt; &amp;quot;Person name: Awesome Jane&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method definition for &lt;code&gt;name=&lt;/code&gt; is roughly the same way you would write it when calling the method &lt;code&gt;person.name = &amp;quot;Jane&amp;quot;&lt;/code&gt;. We don&amp;#39;t define the spaces around the equals sign &lt;code&gt;=&lt;/code&gt; and don&amp;#39;t use parentheses when calling the method.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;Optional parentheses and spaces&lt;/h2&gt;

&lt;p&gt;
  You may have seen that in Ruby parentheses are optional a lot of the time.
  When passing an argument to a method, we don&#039;t have to wrap the argument in
  parentheses &lt;code&gt;()&lt;/code&gt;, but we can if it&#039;s easier to read.
&lt;/p&gt;

&lt;p&gt;
  The if-statement is a good example. In many languages you wrap the expression
  the if-statement evaluates with parentheses. In Ruby, they can be omitted.
&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;puts &amp;quot;Hello!&amp;quot; if (true) # With optional parentheses
puts &amp;quot;Hello!&amp;quot; if true   # Without parentheses
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same applies to method definitions and other expressions.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def greeting name # Parentheses omitted
  &amp;quot;Hello #{name}!&amp;quot;
end

greeting(&amp;quot;Robin&amp;quot;) # With parentheses
greeting &amp;quot;Robin&amp;quot;  # Without parentheses
greeting&amp;quot;Robin&amp;quot;   # Without parentheses and spaces
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
  The last line is difficult to read, but it works. The parentheses and spaces
  are optional even when calling methods.
&lt;/p&gt;

  &lt;p&gt;Just be careful not to omit every parentheses and space, some of these help Ruby understand what you mean! When in doubt, wrap your arguments in parentheses so you and Ruby know what arguments belongs to what method call.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;All the following ways of calling the method are supported, but we commonly omit the parentheses and add spaces to make the code a bit more readable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Previous method definition:
# def name=(value)
#   @name = &amp;quot;Awesome #{value}&amp;quot;
# end

person.name = &amp;quot;Jane&amp;quot;
person.name=&amp;quot;Jane&amp;quot;
person.name=(&amp;quot;Jane&amp;quot;) # That looks a lot like the method definition!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ve now defined custom attribute reader and writer methods for the &lt;code&gt;name&lt;/code&gt; attribute. We can customize the behavior as needed and perform transformations on the value directly when assigning the attribute rather than having to use callbacks.&lt;/p&gt;
&lt;h2&gt;Defining &lt;code&gt;[ ]&lt;/code&gt; methods&lt;/h2&gt;
&lt;p&gt;The next thing we&amp;#39;ll look at are the square bracket methods &lt;code&gt;[ ]&lt;/code&gt; in Ruby. These are commonly used to fetch and assign values to Array indexes and Hash keys.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;hash = { :foo =&amp;gt; :bar, :abc =&amp;gt; :def }
hash[:foo]        # =&amp;gt; :bar
hash[:foo] = :baz # =&amp;gt; :baz

array = [:foo, :bar]
array[1] # =&amp;gt; :bar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&amp;#39;s look at how these methods are defined. When calling &lt;code&gt;hash[:foo]&lt;/code&gt; we are using some Ruby syntactic sugar to make that work. Another way of writing this is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;hash = { :foo =&amp;gt; :bar }
hash.[](:foo)
hash.[]=(:foo, :baz)
# or even:
hash.send(:[], :foo)
hash.send(:[]=, :foo, :baz)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compared with the way we normally write this (&lt;code&gt;hash[:foo]&lt;/code&gt; and &lt;code&gt;hash[:foo] = :baz&lt;/code&gt;) we can already see some differences. In the first example (&lt;code&gt;hash.[](:foo)&lt;/code&gt;) Ruby moves the first argument between the square brackets (&lt;code&gt;hash[:foo]&lt;/code&gt;). When calling &lt;code&gt;hash.[]=(:foo, :baz)&lt;/code&gt; the second argument is passed to the method as the value &lt;code&gt;hash[:foo] = :baz&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Knowing this, we can now define our own &lt;code&gt;[ ]&lt;/code&gt; and &lt;code&gt;[ ]=&lt;/code&gt; methods the way Ruby will understand it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyHash
  def initialize
    @internal_hash = {}
  end

  def [](key)
    @internal_hash[key]
  end

  def []=(key, value)
    @internal_hash[key] = value
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we know these methods are normal Ruby methods, we can apply the same logic to them as any other method. We can even make it do weird things like allow multiple keys in the &lt;code&gt;[ ]&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;class MyHash
  def initialize
    @internal_hash = { :foo =&amp;gt; :bar, :abc =&amp;gt; :def }
  end

  def [](*keys)
    @internal_hash.values_at(*keys)
  end
end

hash = MyHash.new
hash[:foo, :abc] # =&amp;gt; [:bar, :def]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Create your own&lt;/h2&gt;
&lt;p&gt;Now that we know a bit about Ruby&amp;#39;s syntactic sugar, we can apply this knowledge to create our own methods such as custom writers, Hash-like classes and more.&lt;/p&gt;
&lt;p&gt;You may be surprised how many gems define methods such as the square brackets methods to make something feel like an Array or Hash when it really isn&amp;#39;t. One example is setting a flash message in a Rails application with:
&lt;code&gt;flash[:alert] = &amp;quot;An error occurred&amp;quot;&lt;/code&gt;. In the AppSignal gem we use this ourselves on the &lt;code&gt;Config&lt;/code&gt; class &lt;a href=&quot;https://github.com/appsignal/appsignal-ruby/blob/2ebe7fe8b5b0708363aa71c27b2f5cc1941d9ad0/lib/appsignal/config.rb#L99-L105&quot;&gt;as a shorthand&lt;/a&gt; for fetching the configuration.&lt;/p&gt;
&lt;p&gt;This concludes our brief look at the syntactic sugar for method definition and calling in Ruby. We&amp;#39;d love to know how you liked this article, if you have any questions about it, and what you&amp;#39;d like to read about next, so be sure to let us know at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A look at how Ruby interprets your code</title>
    <link rel="alternate" href="https://blog.appsignal.com/2017/08/01/ruby-magic-code-interpretation.html"/>
    <id>https://blog.appsignal.com/2017/08/01/ruby-magic-code-interpretation.html</id>
    <published>2017-08-01T00:00:00+00:00</published>
    <updated>2017-08-01T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">A look at how Ruby interprets your code and how you can use this knowledge to your advantage.</summary>
    <content type="html">&lt;p&gt;Welcome to a new Ruby Magic article! This time we&amp;#39;ll be looking at how Ruby interprets our code, and how we can use this knowledge to our advantage. This post will help you understand how code is interpreted, and how this can help lead to faster code.&lt;/p&gt;
&lt;h2&gt;A subtle difference between symbols&lt;/h2&gt;
&lt;p&gt;In a previous Ruby Magic article about &lt;a href=&quot;/2016/12/21/ruby-magic-escaping-in-ruby.html&quot;&gt;Escaping characters in Ruby&lt;/a&gt; there was an example about escaping line breaks.&lt;/p&gt;
&lt;p&gt;In the example below you see how two strings are combined as one String across multiple lines, with either the plus &lt;code&gt;+&lt;/code&gt; symbol or with the backslash &lt;code&gt;\ &lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;foo&amp;quot; +
  &amp;quot;bar&amp;quot;
=&amp;gt; &amp;quot;foobar&amp;quot;

# versus

&amp;quot;foo&amp;quot; \
  &amp;quot;bar&amp;quot;
=&amp;gt; &amp;quot;foobar&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These two examples may look similar, but they behave quite differently. To know the difference between how these are read and interpreted, you&amp;#39;d normally need to know the nitty-gritty about the Ruby interpreter. Or, we can just ask Ruby what the difference is.&lt;/p&gt;
&lt;h2&gt;InstructionSequence&lt;/h2&gt;
&lt;p&gt;Using the &lt;a href=&quot;http://ruby-doc.org/core-2.4.1/RubyVM/InstructionSequence.html&quot;&gt;&lt;code&gt;RubyVM::InstructionSequence&lt;/code&gt;&lt;/a&gt; class we can ask Ruby how it interprets some code we give it. This class gives us a tool set which we can use to get a glimpse of Ruby&amp;#39;s internals.&lt;/p&gt;
&lt;p&gt;What is returned in the example below is Ruby code as it&amp;#39;s understood by the &lt;a href=&quot;https://en.wikipedia.org/wiki/YARV&quot;&gt;YARV interpreter&lt;/a&gt;.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;YARV interpreter&lt;/h2&gt;

&lt;p&gt;
  YARV (Yet Another Ruby VM) is the Ruby interpreter introduced in Ruby version
  1.9, replacing the original interpreter: MRI (Matz&#039;s Ruby Interpreter).
&lt;/p&gt;

&lt;p&gt;
  Languages that use interpreters directly execute code without an intermediate
  compilation step. This means that Ruby does not first compile a program to an
  optimized machine language program, which compiled languages such as{&quot; &quot;}
  &lt;a href=&quot;https://en.wikipedia.org/wiki/C_(programming_language)&quot;&gt;C&lt;/a&gt;,{&quot; &quot;}
  &lt;a href=&quot;https://www.rust-lang.org/&quot;&gt;Rust&lt;/a&gt; and{&quot; &quot;}
  &lt;a href=&quot;https://golang.org/&quot;&gt;Go&lt;/a&gt; do.
&lt;/p&gt;

&lt;p&gt;
  In Ruby, a program is first translated to an instruction set for the Ruby VM,
  and is then executed immediately after. These instructions are an intermediate
  step between your Ruby code and the code being executed in the Ruby VM.
&lt;/p&gt;

&lt;p&gt;
  These instructions make it easier for the Ruby VM to understand Ruby code
  without having to deal with syntax specific interpretation. That&#039;s handled
  while creating these instructions. Instruction sequences are optimized
  operations which represent the interpreted code.
&lt;/p&gt;

  &lt;p&gt;During the normal execution of a Ruby program we don&#039;t see these instructions, but by viewing them we can review if Ruby has interpreted our code correctly. With &lt;code&gt;InstructionSequence&lt;/code&gt; it&#039;s possible to see what kind of instructions YARV creates before it executes them.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;It&amp;#39;s not necessary to understand all of the YARV instructions that make up the Ruby interpreter. Most commands will speak for themselves.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;foo&amp;quot; +
  &amp;quot;bar&amp;quot;
RubyVM::InstructionSequence.compile(&amp;#39;&amp;quot;foo&amp;quot; + &amp;quot;bar&amp;quot;&amp;#39;).to_a
# ... [:putstring, &amp;quot;foo&amp;quot;], [:putstring, &amp;quot;bar&amp;quot;] ...

# versus

&amp;quot;foo&amp;quot; \
  &amp;quot;bar&amp;quot;
RubyVM::InstructionSequence.compile(&amp;#39;&amp;quot;foo&amp;quot; &amp;quot;bar&amp;quot;&amp;#39;).to_a
# ... [:putstring, &amp;quot;foobar&amp;quot;] ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The real output contains a bit more setup commands that we will look at later, but here we can see the real difference between &lt;code&gt;&amp;quot;foo&amp;quot; + &amp;quot;bar&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;foo&amp;quot; &amp;quot;bar&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The former creates two strings and combines them. The latter creates one string. This means that with &lt;code&gt;&amp;quot;foo&amp;quot; &amp;quot;bar&amp;quot;&lt;/code&gt; we only create one string, rather than three with &lt;code&gt;&amp;quot;foo&amp;quot; + &amp;quot;bar&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  1       2           3
  ↓       ↓           ↓
&amp;quot;foo&amp;quot; + &amp;quot;bar&amp;quot; # =&amp;gt; &amp;quot;foobar&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, this is just about the most basic example we can use, but it shows a good use case of how a small detail in the Ruby language could potentially have a lot of impact:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More allocations: every String object is allocated separately.&lt;/li&gt;
&lt;li&gt;More memory usage: every allocated String object takes up memory.&lt;/li&gt;
&lt;li&gt;Longer garbage collection: every object, even when short-lived, takes up time to be cleaned by the garbage collector. More allocations means longer garbage collection times.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Disassembling&lt;/h2&gt;
&lt;p&gt;Another use case is debugging a logic issue. The following is an easy mistake to make, which can have big consequences. Can you spot the difference?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;1 + 2 * 3
# versus
(1 + 2) * 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use Ruby to help us find out the difference in this slightly more complex example.&lt;/p&gt;
&lt;p&gt;By disassembling this code example we can get Ruby to print a more readable table of the commands it&amp;#39;s performing.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;1 + 2 * 3
# =&amp;gt; 7
puts RubyVM::InstructionSequence.compile(&amp;quot;1 + 2 * 3&amp;quot;).disasm
# == disasm: &amp;lt;RubyVM::InstructionSequence:&amp;lt;compiled&amp;gt;@&amp;lt;compiled&amp;gt;&amp;gt;==========
# 0000 trace            1                                               (   1)
# 0002 putobject_OP_INT2FIX_O_1_C_
# 0003 putobject        2
# 0005 putobject        3
# 0007 opt_mult         &amp;lt;callinfo!mid:*, argc:1, ARGS_SIMPLE&amp;gt;
# 0009 opt_plus         &amp;lt;callinfo!mid:+, argc:1, ARGS_SIMPLE&amp;gt;
# 0011 leave

# versus

(1 + 2) * 3
# =&amp;gt; 9
puts RubyVM::InstructionSequence.compile(&amp;quot;(1 + 2) * 3&amp;quot;).disasm
# == disasm: &amp;lt;RubyVM::InstructionSequence:&amp;lt;compiled&amp;gt;@&amp;lt;compiled&amp;gt;&amp;gt;==========
# 0000 trace            1                                               (   1)
# 0002 putobject_OP_INT2FIX_O_1_C_
# 0003 putobject        2
# 0005 opt_plus         &amp;lt;callinfo!mid:+, argc:1, ARGS_SIMPLE&amp;gt;
# 0007 putobject        3
# 0009 opt_mult         &amp;lt;callinfo!mid:*, argc:1, ARGS_SIMPLE&amp;gt;
# 0011 leave
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The example above is a bit more involved with the number of YARV instructions, but just from the order in which things are printed and executed we see the difference a pair of parentheses can make.&lt;/p&gt;
&lt;p&gt;With the parentheses around &lt;code&gt;1 + 2&lt;/code&gt; we make sure the addition is performed first, by moving it up the &lt;a href=&quot;http://mathworld.wolfram.com/Precedence.html&quot;&gt;order of operations&lt;/a&gt; in mathematics.&lt;/p&gt;
&lt;p&gt;Note that you don&amp;#39;t actually see the parentheses in the disassembly output itself, only their effect on the rest of the code.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;Disassembly&lt;/h2&gt;

&lt;p&gt;
  The Disassembly output prints a lot of things that might not immediately be
  understandable.
&lt;/p&gt;

&lt;p&gt;
  In the table format that is printed, every line starts with an operation
  number. After which it mentions the operation and finally the argument to the
  operation.
&lt;/p&gt;

&lt;p&gt;A small sample of operations we&#039;ve seen so far:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;code&gt;trace&lt;/code&gt; - start a trace. See the docs on &lt;a href=&quot;http://ruby-doc.org/core-2.4.1/TracePoint.html&quot;&gt;TracePoint&lt;/a&gt; for more information.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;putobject&lt;/code&gt; - push an object on the stack.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;putobject_OP_INT2FIX_O_1_C_&lt;/code&gt; - push the Integer &lt;code&gt;1&lt;/code&gt; on the stack. Optimized operation. (&lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; are optimized.)&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;putstring&lt;/code&gt; - push a string on the stack.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;opt_plus&lt;/code&gt; - addition operation (internally optimized).&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;opt_mult&lt;/code&gt; - multiply operation (internally optimized).&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;leave&lt;/code&gt; - leave the current code context.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;

&lt;p&gt;Now that we know how the Ruby interpreter converts our developer friendly and readable Ruby code to YARV instructions, we can use this to optimize our applications.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s possible to pass along entire methods and even entire files to &lt;code&gt;RubyVM::InstructionSequence&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;puts RubyVM::InstructionSequence.disasm(method(:foo))
puts RubyVM::InstructionSequence.compile_file(&amp;quot;/tmp/hello.rb&amp;quot;).disasm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find out why some piece of code works and why another doesn&amp;#39;t. Learn why certain symbols make code behave differently than others. The devil is in the details, and it&amp;#39;s good to know how your Ruby code is behaving in your app and if you can optimize it in any way.&lt;/p&gt;
&lt;h2&gt;Optimizations&lt;/h2&gt;
&lt;p&gt;Other than being able to view your code on interpreter level and optimize for it, you can use &lt;code&gt;InstructionSequence&lt;/code&gt; to optimize your code even further.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;InstructionSequence&lt;/code&gt;, it&amp;#39;s possible to optimize certain instructions with Ruby&amp;#39;s built-in performance optimizations. The full list of available optimizations is available in the &lt;a href=&quot;http://ruby-doc.org/core-2.4.1/RubyVM/InstructionSequence.html#method-c-compile_option-3D&quot;&gt;&lt;code&gt;RubyVM::InstructionSequence.compile_option =&lt;/code&gt;&lt;/a&gt; method documentation.&lt;/p&gt;
&lt;p&gt;One of these optimizations is &lt;strong&gt;Tail Call Optimization&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;RubyVM::InstructionSequence.compile&lt;/code&gt; method accepts options to enable this optimization as such:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;some_code = &amp;lt;&amp;lt;-EOS
def fact(n, acc=1)
  return acc if n &amp;lt;= 1
  fact(n-1, n*acc)
end
EOS
puts RubyVM::InstructionSequence.compile(some_code, nil, nil, nil, tailcall_optimization: true, trace_instruction: false).disasm
RubyVM::InstructionSequence.compile(some_code, nil, nil, nil, tailcall_optimization: true, trace_instruction: false).eval
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can even turn this optimization on for all your code with &lt;code&gt;RubyVM::InstructionSequence.compile_option =&lt;/code&gt;. Just make sure to load this before any of your other code.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;RubyVM::InstructionSequence.compile_option = {
  tailcall_optimization: true,
  trace_instruction: false
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more information about how Tail Call Optimization works in Ruby check out these articles: &lt;a href=&quot;http://nithinbekal.com/posts/ruby-tco/&quot;&gt;Tail Call Optimization in Ruby&lt;/a&gt; and &lt;a href=&quot;https://blog.tdg5.com/tail-call-optimization-in-ruby-background/&quot;&gt;Tail Call Optimization in Ruby: Background&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Learn more about how Ruby interprets your code with &lt;code&gt;RubyVM::InstructionSequence&lt;/code&gt; and see what your code is really doing so you can make it more performant.&lt;/p&gt;
&lt;p&gt;This introduction to InstructionSequence might also be a fun way to learn more about how Ruby works under the hood. Who knows? You might even be interested in working on some of &lt;a href=&quot;https://github.com/ruby/ruby&quot;&gt;Ruby&amp;#39;s code&lt;/a&gt; itself.&lt;/p&gt;
&lt;p&gt;That concludes our short introduction to code compilation in Ruby. We&amp;#39;d love to know how you liked this article, if you have any questions about it, and what you&amp;#39;d like to read about next, so be sure to let us know at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Concurrency Deep Dive: Event loops</title>
    <link rel="alternate" href="https://blog.appsignal.com/2017/06/06/ruby-magic-concurrency-event-loop.html"/>
    <id>https://blog.appsignal.com/2017/06/06/ruby-magic-concurrency-event-loop.html</id>
    <published>2017-06-06T00:00:00+00:00</published>
    <updated>2017-06-06T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">A deep dive into achieving concurrency using an event loop.</summary>
    <content type="html">&lt;p&gt;Welcome to the last Ruby Magic article in our series about concurrency. In the previous editions we implemented a chat server &lt;a href=&quot;/2017/03/07/ruby-magic-concurrency-processes.html&quot;&gt;using multiple processes&lt;/a&gt; and &lt;a href=&quot;/2017/04/18/ruby-magic-concurrency-threads.html&quot;&gt;multiple threads&lt;/a&gt;. This time we&amp;#39;re going to do the same thing using an event loop.&lt;/p&gt;
&lt;h2&gt;Recap&lt;/h2&gt;
&lt;p&gt;We&amp;#39;re going to use the same client and the same server setup we used in the earlier articles. Our aim is to build a chat system that looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2017-03/chat_example.png&quot; alt=&quot;Chat example&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Please see the previous articles for more details on the basic setup. The &lt;a href=&quot;https://github.com/thijsc/three-chat-servers&quot;&gt;full source code that is used in the examples in this article&lt;/a&gt; is available on GitHub, so you can experiment with it yourself.&lt;/p&gt;
&lt;h2&gt;Chat server using an event loop&lt;/h2&gt;
&lt;p&gt;Using an event loop for our chat server requires you to have a different mental model than using threads or processes. In the classic approach, a thread or process is responsible for handling a single connection. Using an event loop you have a single thread in a single process that handles multiple connections. Let&amp;#39;s see how this works by breaking it down.&lt;/p&gt;
&lt;h2&gt;Event loop&lt;/h2&gt;

&lt;p&gt;
  An event loop used by EventMachine or NodeJS for example works as follows. We
  start with informing the operating system we&#039;re interested in certain events.
  For example, when a connection to a socket is opened. We do this by calling a
  function that registers interest on some IO object, such as a connection or
  socket.
&lt;/p&gt;

&lt;p&gt;
  When something happens on this IO object, the operating system sends an event
  to our program. We put these events on a queue. The event loop keeps popping
  events off the list and handles them one by one.
&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2016-02/event-loops.svg&quot; alt=&quot;Event Loops&quot;/&gt;&lt;/p&gt;
&lt;p&gt;
  In a sense an event loop is not truly concurrent. It works sequentially in
  very small batches to simulate the effect.
&lt;/p&gt;

&lt;p&gt;To register interest and have the operating system pass IO events to us we&amp;#39;d have to write a C extension, as there is no API present for that in the Ruby standard library. Diving into that is outside of the scope of this article, so we&amp;#39;re going to use &lt;code&gt;IO.select&lt;/code&gt; instead to generate events. &lt;code&gt;IO.select&lt;/code&gt; takes an array of &lt;code&gt;IO&lt;/code&gt; objects to monitor. It waits until one or more of the objects from the array are ready for reading or writing, and it returns an array with just those &lt;code&gt;IO&lt;/code&gt; objects.&lt;/p&gt;
&lt;p&gt;The code that takes care of everything related to a connection is implemented as a &lt;code&gt;Fiber&lt;/code&gt;: we&amp;#39;ll call this code the &amp;quot;handler&amp;quot; from now on. A &lt;code&gt;Fiber&lt;/code&gt; is a code block that can be paused and resumed. The Ruby VM doesn&amp;#39;t do this automatically, so we have to resume and yield manually. We&amp;#39;ll use the input from &lt;code&gt;IO.select&lt;/code&gt; to inform our handlers when their connections are ready for reading or writing.&lt;/p&gt;
&lt;p&gt;Like in the threaded and multi-process examples from the previous posts, we need some storage to keep track of the clients and the messages that are sent. We don&amp;#39;t need a &lt;code&gt;Mutex&lt;/code&gt; this time. Our event loop is running in a single thread, so there is no risk of objects being mutated at the same time by different threads.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;client_handlers = {}
messages = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The client handler is implemented in the following &lt;code&gt;Fiber&lt;/code&gt;. When the socket can be read from or written to, an event is triggered to which the &lt;code&gt;Fiber&lt;/code&gt; responds. When the state is &lt;code&gt;:readable&lt;/code&gt; it reads a line from the socket and pushes this onto the &lt;code&gt;messages&lt;/code&gt; array. When the state is &lt;code&gt;:writable&lt;/code&gt; it writes any messages that have been received from other clients since the last write to the client. After handling an event it calls &lt;code&gt;Fiber.yield&lt;/code&gt;, so it will pause and wait for the next event.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def create_client_handler(nickname, socket)
  Fiber.new do
    last_write = Time.now
    loop do
      state = Fiber.yield

      if state == :readable
        # Read a message from the socket
        incoming = read_line_from(socket)
        # All good, add it to the list to write
        $messages.push(
          :time =&amp;gt; Time.now,
          :nickname =&amp;gt; nickname,
          :text =&amp;gt; incoming
        )
      elsif state == :writable
        # Write messages to the socket
        get_messages_to_send(last_write, nickname, $messages).each do |message|
          socket.puts &amp;quot;#{message[:nickname]}: #{message[:text]}&amp;quot;
        end
        last_write = Time.now
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So how do we trigger the &lt;code&gt;Fiber&lt;/code&gt; to read or write at the right time when the &lt;code&gt;Socket&lt;/code&gt; is ready? We use an event loop that has four steps:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;loop do
  # Step 1: Accept incoming connections
  accept_incoming_connections

  # Step 2: Get connections that are ready for reading or writing
  get_ready_connections

  # Step 3: Read from readable connections
  read_from_readable_connections

  # Step 4: Write to writable connections
  write_to_writable_connections
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that there is no magic here. This is a normal Ruby loop.&lt;/p&gt;
&lt;h3&gt;Step 1: Accept incoming connections&lt;/h3&gt;
&lt;p&gt;See if we have any new incoming connections. We use &lt;code&gt;accept_nonblock&lt;/code&gt;, which will not wait for a client to connect. It will instead raise an error if there is no new client, and if that error occurs we catch it and go to the next step. If there is a new client we create the handler for it and put that on the &lt;code&gt;clients&lt;/code&gt; store. We&amp;#39;ll use the socket object as the key of that &lt;code&gt;Hash&lt;/code&gt; so we can find the client handler later.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  socket = server.accept_nonblock
  nickname = socket.gets.chomp
  $client_handlers[socket] = create_client_handler(nickname, socket)
  puts &amp;quot;Accepted connection from #{nickname}&amp;quot;
rescue IO::WaitReadable, Errno::EINTR
  # No new incoming connections at the moment
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2: Get connections that are ready for reading or writing&lt;/h3&gt;
&lt;p&gt;Next, we ask the OS to inform us when a connection is ready. We pass in the keys of the &lt;code&gt;client_handlers&lt;/code&gt; store for reading, writing and error handling. These keys are the socket objects we accepted in step 1. We wait for 10 milliseconds for this to happen.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;readable, writable = IO.select(
  $client_handlers.keys,
  $client_handlers.keys,
  $client_handlers.keys,
  0.01
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3: Read from readable connections&lt;/h3&gt;
&lt;p&gt;If any of our connections are readable, we&amp;#39;ll trigger the client handlers and resume them with a &lt;code&gt;readable&lt;/code&gt; state. We can look up these client handlers because the &lt;code&gt;Socket&lt;/code&gt; object that is returned by &lt;code&gt;IO.select&lt;/code&gt; is used as the key of the handlers store.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if readable
  readable.each do |ready_socket|
    # Get the client from storage
    client = $client_handlers[ready_socket]

    client.resume(:readable)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 4: Write to writable connections&lt;/h3&gt;
&lt;p&gt;If any of our connections are writable, we&amp;#39;ll trigger the client handlers and resume them with a &lt;code&gt;writable&lt;/code&gt; state.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;if writable
  writable.each do |ready_socket|
    # Get the client from storage
    client = $client_handlers[ready_socket]
    next unless client

    client.resume(:writable)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using these four steps in a loop which creates handlers, and calling &lt;code&gt;readable&lt;/code&gt; and &lt;code&gt;writable&lt;/code&gt; on these handlers at the right time, we have created a fully functional evented chat server. There&amp;#39;s very little overhead per connection, and we could scale this up to a large number of concurrent clients.&lt;/p&gt;
&lt;p&gt;This approach works very well as long as we keep the amount of work per tick of the loop small. This is especially important for work that involves calculations, since an event loop runs in a single thread and thus can only utilize a single CPU. In production systems there are often multiple processes running an event loop to work around this limitation.&lt;/p&gt;
&lt;h2&gt;Concluding&lt;/h2&gt;
&lt;p&gt;After all this you might ask, which of these three methods should I use?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For most apps, threading makes sense. It&amp;#39;s the simplest approach to work with.&lt;/li&gt;
&lt;li&gt;If you run highly concurrent apps with long-running streams, event loops allow you to scale.&lt;/li&gt;
&lt;li&gt;If you expect your processes to crash, go for good old multi-process, as it&amp;#39;s the most robust approach.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This concludes our series on concurrency. If you want a full recap &lt;a href=&quot;/2016/03/17/ruby-magic-mastering-concurrency.html&quot;&gt;check the original mastering concurrency article&lt;/a&gt; as well as the detailed articles on &lt;a href=&quot;/2017/03/07/ruby-magic-concurrency-processes.html&quot;&gt;using multiple processes&lt;/a&gt; and &lt;a href=&quot;/2017/04/18/ruby-magic-concurrency-threads.html&quot;&gt;multiple threads&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Concurrency Deep Dive: Multi-threading</title>
    <link rel="alternate" href="https://blog.appsignal.com/2017/04/18/ruby-magic-concurrency-threads.html"/>
    <id>https://blog.appsignal.com/2017/04/18/ruby-magic-concurrency-threads.html</id>
    <published>2017-04-18T00:00:00+00:00</published>
    <updated>2017-04-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">A deep dive into achieving concurrency using threads.</summary>
    <content type="html">&lt;p&gt;In the previous edition of Ruby Magic we showed how you can &lt;a href=&quot;/2017/03/07/ruby-magic-concurrency-processes.html&quot;&gt;implement a chat system using multiple processes&lt;/a&gt;. This time we&amp;#39;ll show you how you can do the same thing using multiple threads.&lt;/p&gt;
&lt;h2&gt;Quick recap&lt;/h2&gt;
&lt;p&gt;If you want to get a full explanation of the basic setup check out the
&lt;a href=&quot;/2017/03/07/ruby-magic-concurrency-processes.html&quot;&gt;previous article&lt;/a&gt;. But to remind you quickly: this is what our chat system looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2017-03/chat_example.png&quot; alt=&quot;Chat example&quot;/&gt;&lt;/p&gt;
&lt;p&gt;We&amp;#39;re using the same client we used earlier:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# client.rb
# $ ruby client.rb
require &amp;#39;socket&amp;#39;
client = TCPSocket.open(ARGV[0], 2000)

Thread.new do
  while line = client.gets
    puts line.chop
  end
end

while input = STDIN.gets.chomp
  client.puts input
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The basic setup for the server is the same:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# server_threads.rb
# $ ruby server_threads.rb
require &amp;#39;socket&amp;#39;

puts &amp;#39;Starting server on port 2000&amp;#39;

server = TCPServer.open(2000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/thijsc/three-chat-servers&quot;&gt;full source code that is used in the examples in this article&lt;/a&gt; is available on GitHub, so you can experiment with it yourself.&lt;/p&gt;
&lt;h2&gt;Multi-threaded chat server&lt;/h2&gt;
&lt;p&gt;Now we&amp;#39;re getting to the part that is different compared to the
multi-process implementation. Using &lt;strong&gt;Multi-threading&lt;/strong&gt; we can do multiple things at the same time
with just one Ruby process. We will do this by spawning multiple threads that do the work.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;Threads&lt;/h2&gt;

  &lt;p&gt;
    A thread runs independently, executing code within a process. Multiple
threads can live in the same process and they can share memory.

&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&amp;quot;/images/blog/2017-04/threads.png&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
  &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Some storage will be needed to store the incoming chat messages. We&amp;#39;ll be using a plain &lt;code&gt;Array&lt;/code&gt;, but we also need a &lt;code&gt;Mutex&lt;/code&gt; to make sure that only one thread changes the messages at the same time (we&amp;#39;ll see how the &lt;code&gt;Mutex&lt;/code&gt; works in a bit).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;mutex = Mutex.new
messages = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next up we start a loop in which we&amp;#39;ll accept incoming connections from chat clients. Once a connection has been established, we&amp;#39;ll spawn a thread to handle the incoming and outgoing messages from that client connection.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Thread.new&lt;/code&gt; call blocks until &lt;code&gt;server.accept&lt;/code&gt; returns something, and then
yields the following block in the newly created thread. The code in the thread then proceeds to read the first line that&amp;#39;s sent and stores this as the nickname. Finally it starts sending and reading messages.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;loop do
  Thread.new(server.accept) do |socket|
    nickname = read_line_from(socket)

    # Send incoming message (coming up)

    # Read incoming messages (coming up)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;Mutex&lt;/h2&gt;

  &lt;p&gt;A mutex is an object that lets multiple threads coordinate how they
use shared resources, such as an array. A thread can indicate that it
needs access, and during this time other threads cannot access the shared
resource.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The server reads incoming messages from the socket. It uses &lt;code&gt;synchronize&lt;/code&gt; to get a lock on
the messages store, so it can safely add a message to the messages &lt;code&gt;Array&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Read incoming messages
while incoming = read_line_from(socket)
  mutex.synchronize do
    messages.push(
      :time =&amp;gt; Time.now,
      :nickname =&amp;gt; nickname,
      :text =&amp;gt; incoming
    )
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, a &lt;code&gt;Thread&lt;/code&gt; is spawned that runs continuously in a loop, to make sure all the new messages that have been received by the server are being sent to the client. Again it gets a lock so it knows that other threads are not interfering. After it&amp;#39;s done with a tick of the loop it sleeps for a bit and then continues.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Send incoming message
Thread.new do
  sent_until = Time.now
  loop do
    messages_to_send = mutex.synchronize do
      get_messages_to_send(nickname, messages, sent_until).tap do
        sent_until = Time.now
      end
    end
    messages_to_send.each do |message|
      socket.puts &amp;quot;#{message[:nickname]}: #{message[:text]}&amp;quot;
    end
    sleep 0.2
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;Global interpreter lock&lt;/h2&gt;

  &lt;p&gt;
    You might have heard the story that Ruby cannot do &quot;real&quot; threading
    because of &lt;a href=&quot;https://en.wikipedia.org/wiki/Global_interpreter_lock&quot;&gt;Ruby&#039;s Global Interpreter Lock (GIL)&lt;/a&gt;. This is partially true. The
GIL is a lock around the execution of all Ruby code and prevents a
Ruby process from using multiple CPUs concurrently. IO operations (such as the network connections we used in this article) operate outside of the GIL, which means you can actually achieve decent concurrency in this case.
  &lt;/p&gt;
&lt;/div&gt;

&lt;h2&gt;Concluding&lt;/h2&gt;
&lt;p&gt;Now we have a chat server running within a single process using a
thread per connection. This will use a lot less resources than the
multi-process implementation. If you want to see the details of the code
or try it &lt;a href=&quot;https://github.com/thijsc/three-chat-servers&quot;&gt;you can find the example code here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/2017/06/06/ruby-magic-concurrency-event-loop.html&quot;&gt;final article&lt;/a&gt; in this series we&amp;#39;ll implement this same chat server
using a single thread and an event loop. Theoretically this should even
use less resources than the thread implementation!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Concurrency Deep Dive: Multi-process</title>
    <link rel="alternate" href="https://blog.appsignal.com/2017/03/07/ruby-magic-concurrency-processes.html"/>
    <id>https://blog.appsignal.com/2017/03/07/ruby-magic-concurrency-processes.html</id>
    <published>2017-03-07T00:00:00+00:00</published>
    <updated>2017-03-07T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">A deep dive into achieving concurrency using multi-process.</summary>
    <content type="html">&lt;p&gt;In a previous Ruby Magic &lt;a href=&quot;/2016/03/17/ruby-magic-mastering-concurrency.html&quot;&gt;article on Mastering Concurrency&lt;/a&gt;, we gave an introduction to the three methods of achieving concurrency that are available to us as Ruby developers. This article is the first in a three-part series where we take a deep dive into each method.&lt;/p&gt;
&lt;p&gt;First up: &lt;strong&gt;Multi-process&lt;/strong&gt;. With this method a master process forks itself to multiple worker processes. The worker process does the actual work, while the master manages the workers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/thijsc/three-chat-servers&quot;&gt;full source code that is used in the examples in this article&lt;/a&gt; is available on GitHub, so you can experiment with it yourself.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Let&amp;#39;s build a chat system!&lt;/h2&gt;
&lt;p&gt;Building a chat system is a good way to dive into concurrency. We&amp;#39;ll need a server component of a chat system that&amp;#39;s able to maintain connections with multiple clients. This will allow us to distribute the messages it receives from one client to all the other connected clients.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2017-03/chat_example.png&quot; alt=&quot;Chat example&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Our chat server is running in the left tab. There are two chat clients running in the right tabs. Any message that is sent by a client will be received by all other clients.&lt;/p&gt;
&lt;h2&gt;The chat client&lt;/h2&gt;
&lt;p&gt;This article focuses on the chat server, but to communicate with it we&amp;#39;ll need a chat client first. The following code will be our very simple client. (A &lt;a href=&quot;https://github.com/thijsc/three-chat-servers/blob/master/client.rb&quot;&gt;more complete example&lt;/a&gt; can be found on GitHub.)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# client.rb
# $ ruby client.rb
require &amp;#39;socket&amp;#39;
client = TCPSocket.open(ARGV[0], 2000)

Thread.new do
  while line = client.gets
    puts line.chop
  end
end

while input = STDIN.gets.chomp
  client.puts input
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The client opens a TCP connection to a server running on port 2000. When connected, it spawns a thread that will &lt;code&gt;puts&lt;/code&gt; anything the server sends, so the chat is visible in the terminal output. Finally, there&amp;#39;s a while loop that sends any line you type to the server, which it will send to all other connected clients.&lt;/p&gt;
&lt;h2&gt;The chat server&lt;/h2&gt;
&lt;p&gt;In this example a client connects to a chat server in order to communicate with other clients. For all three concurrency approaches we will use the same TCP server from Ruby&amp;#39;s standard library.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# server_processes.rb
# $ ruby server_processes.rb
require &amp;#39;socket&amp;#39;

puts &amp;#39;Starting server on port 2000&amp;#39;

server = TCPServer.open(2000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Up to this point the code is the same for all three concurrency models. The chat server in every model will then need to handle two scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Accept new connections from clients.&lt;/li&gt;
&lt;li&gt;Receive messages from clients and send them to all the other clients.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;A multi-process chat server&lt;/h1&gt;
&lt;p&gt;To handle these two scenarions with a multi-process chat server, we will be spawning a process per client connection. This process will handle all the messages being sent and received for that client. We can create these processes by forking the original server process.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;Forking processes&lt;/h2&gt;

  &lt;p&gt;When you call the &lt;a href=&quot;http://ruby-doc.org/core-2.4.0/Kernel.html#method-i-fork&quot;&gt;fork method&lt;/a&gt;, it creates a copy of the current process with the exact same state that the process is in.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;A forked process has its own process id, and will be visible separately in a tool like &lt;code&gt;top&lt;/code&gt; or Activity Monitor. That looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2016-02/multi-process.svg&quot; alt=&quot;Multiple processes&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The process you start with is called the master process, and the processes that are forked out of the master process are called worker processes.&lt;/p&gt;
&lt;p&gt;Since these newly forked worker processes are truly separate processes, we cannot share memory between them and the master process. We need something to communicate between them.&lt;/p&gt;
&lt;div className=&quot;header ruby_magic&quot;&gt;
  &lt;h2&gt;Unix pipes&lt;/h2&gt;

  &lt;p&gt;To communicate between processes we will use Unix pipes. A Unix pipe sets up a two-way stream of bytes between two processes, and you can use it to to send data from one process to the other. Luckily, Ruby offers a nice wrapper around these pipes so we don&#039;t need to re-invent the wheel.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the following example we set up a pipe in Ruby –with a reading and a writing end– and we &lt;code&gt;fork&lt;/code&gt; the master process. The code within the block that&amp;#39;s passed to &lt;code&gt;fork&lt;/code&gt; is running in the forked process. The original process continues after this block. We then write a message to the original process from the forked one.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;reader, writer = IO.pipe

fork do
  # This is running in the forked process.
  writer.puts &amp;#39;Hello from the forked process&amp;#39;
end

# This is running in the original process, it will puts the
# message from the forked process.
puts reader.gets
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using pipes we can communicate between separate processes even though the processes are completely isolated from each other.&lt;/p&gt;
&lt;h1&gt;The chat server&amp;#39;s implementation&lt;/h1&gt;
&lt;p&gt;First we set up an array to keep track of the pipes for all clients and their &amp;quot;writers&amp;quot; (the writing end of the pipe), so we can communicate with the clients. Then we make sure that all incoming messages from the clients are sent to all the other clients.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;client_writers = []
master_reader, master_writer = IO.pipe

write_incoming_messages_to_child_processes(master_reader, client_writers)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find &lt;a href=&quot;https://github.com/thijsc/three-chat-servers/blob/master/lib/processes.rb#L3-L11&quot;&gt;the implementation of &lt;code&gt;write_incoming_messages_to_child_processes&lt;/code&gt;&lt;/a&gt; on GitHub if you want to see the details of how it operates.&lt;/p&gt;
&lt;h3&gt;Accepting new connections&lt;/h3&gt;
&lt;p&gt;We will need to accept incoming connections and set up the pipes. The new writer will be pushed onto the &lt;code&gt;client_writers&lt;/code&gt; array. The main process will be able to loop through the array and send a message to each worker process by writing to its pipe.&lt;/p&gt;
&lt;p&gt;We then fork the master process, and the code within the forked worker process will handle the client connection.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;loop do
  while socket = server.accept
    # Create a client reader and writer so that the master
    # process can write messages back to us.
    client_reader, client_writer = IO.pipe

    # Put the client writer on the list of writers so the
    # master process can write to them.
    client_writers.push(client_writer)

    # Fork child process, everything in the fork block
    # only runs in the child process.
    fork do
      # Handle connection
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Handling client connections&lt;/h3&gt;
&lt;p&gt;We also need to handle the client connection.&lt;/p&gt;
&lt;p&gt;The forked process starts by getting the nickname from the client (&lt;a href=&quot;https://github.com/thijsc/three-chat-servers/blob/master/client.rb#L19&quot;&gt;the client sends the nickname by default&lt;/a&gt;). After that it starts a thread in &lt;code&gt;write_incoming_messages_to_client&lt;/code&gt; that &lt;a href=&quot;https://github.com/thijsc/three-chat-servers/blob/master/lib/processes.rb#L15&quot;&gt;listens for messages from the main process&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, the forked process starts a loop that listens for incoming messages and sends them to the master process. The master process makes sure the other worker process receive the message.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;nickname = read_line_from(socket)
puts &amp;quot;#{Process.pid}: Accepted connection from #{nickname}&amp;quot;

write_incoming_messages_to_client(nickname, client_reader, socket)

# Read incoming messages from the client.
while incoming = read_line_from(socket)
  master_writer.puts &amp;quot;#{nickname}: #{incoming}&amp;quot;
end

puts &amp;quot;#{Process.pid}: Disconnected #{nickname}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A working chat system&lt;/h2&gt;
&lt;p&gt;Now the whole chat system works! But as you can see, writing a program that uses multiprocessing is quite complex and uses a lot of resources. The upside is that it&amp;#39;s very robust. If one of the child processes crashes the rest of the system just keeps working. You can try that by &lt;a href=&quot;https://github.com/thijsc/three-chat-servers&quot;&gt;running the example code&lt;/a&gt; and running &lt;code&gt;kill -9 &amp;lt;process-id&amp;gt;&lt;/code&gt; on one of the processes (you can find the process id in the server&amp;#39;s log output).&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/2017/04/18/ruby-magic-concurrency-threads.html&quot;&gt;next article&lt;/a&gt; we&amp;#39;ll implement the same chat system only using threads, so we can run a server with the same features using just one process and less memory.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Running Rack: How Ruby HTTP servers run Rails apps</title>
    <link rel="alternate" href="https://blog.appsignal.com/2017/01/24/ruby-magic-building-a-ruby-http-server-part-2-running-a-rails-app.html"/>
    <id>https://blog.appsignal.com/2017/01/24/ruby-magic-building-a-ruby-http-server-part-2-running-a-rails-app.html</id>
    <published>2017-01-24T00:00:00+00:00</published>
    <updated>2017-01-24T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">After starting work on a simple HTTP server a while back, we&#039;re diving back in to learn how to handle POST requests and what we need to do to serve a Rails application.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;In the Ruby Magic series we love to take software apart to learn how it functions under the hood. It&amp;#39;s all about the process; the end result isn&amp;#39;t something you&amp;#39;d use in production, we learn about the internal workings of the Ruby language and its popular libraries. We publish a new article about once a month, so be sure to &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to our newsletter&lt;/a&gt; if you&amp;#39;re into this sort of thing too.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In an earlier edition of Ruby Magic we &lt;a href=&quot;/2016/11/23/ruby-magic-building-a-30-line-http-server-in-ruby.html&quot;&gt;implemented a 30-line HTTP server in Ruby&lt;/a&gt;. Without having to write a lot of code, we were able to handle HTTP GET requests and serve a simple Rack application. This time, we&amp;#39;ll take our home made server a bit further. When we&amp;#39;re done, we&amp;#39;ll have a web server that can serve Rails&amp;#39; famous fifteen minute blog that allows you to create, update and delete posts.&lt;/p&gt;
&lt;h2&gt;Where we left off&lt;/h2&gt;
&lt;p&gt;Last time, we implemented just enough of a server to have it serve &lt;a href=&quot;https://github.com/rack/rack/blob/master/lib/rack/lobster.rb&quot;&gt;Rack::Lobster&lt;/a&gt; as an example application.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our implementation opened a TCP server and waited for a request to come in.&lt;/li&gt;
&lt;li&gt;When that happened, the request-line (&lt;code&gt;GET /?flip=left HTTP/1.1\r\n&lt;/code&gt;) was parsed to get the request method (&lt;code&gt;GET&lt;/code&gt;), the path (&lt;code&gt;/&lt;/code&gt;), and the query parameters (&lt;code&gt;flip=left&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The request method, the path and the query string were passed to the Rack app, which returned a triplet with a status, some response headers and the response body.&lt;/li&gt;
&lt;li&gt;Using those, we were able to build an HTTP response to send back to the browser, before closing the connection to wait for a new request to come in.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
require &amp;#39;socket&amp;#39;
require &amp;#39;rack&amp;#39;
require &amp;#39;rack/lobster&amp;#39;

app = Rack::Lobster.new
server = TCPServer.new 5678

#1
while session = server.accept
  request = session.gets
  puts request

  #2
  method, full_path = request.split(&amp;#39; &amp;#39;)
  path, query = full_path.split(&amp;#39;?&amp;#39;)

  #3
  status, headers, body = app.call({
    &amp;#39;REQUEST_METHOD&amp;#39; =&amp;gt; method,
    &amp;#39;PATH_INFO&amp;#39; =&amp;gt; path,
    &amp;#39;QUERY_STRING&amp;#39; =&amp;gt; query
  })

  #4
  session.print &amp;quot;HTTP/1.1 #{status}\r\n&amp;quot;
  headers.each do |key, value|
    session.print &amp;quot;#{key}: #{value}\r\n&amp;quot;
  end
  session.print &amp;quot;\r\n&amp;quot;
  body.each do |part|
    session.print part
  end
  session.close
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ll be continuing with the code we wrote last time. If you want to follow along, here&amp;#39;s the &lt;a href=&quot;https://gist.github.com/jeffkreeftmeijer/7f08d1f7e381b9c552666750914925eb&quot;&gt;code we ended up with&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Rack and Rails&lt;/h2&gt;
&lt;p&gt;Ruby frameworks like Rails and Sinatra are built on top of the Rack interface. Just like the instance of &lt;code&gt;Rack::Lobster&lt;/code&gt; we&amp;#39;re using to test our server right now, Rails&amp;#39; &lt;code&gt;Rails.application&lt;/code&gt; is a Rack application object. In theory, this would mean that our server should already be able to serve a Rails application.&lt;/p&gt;
&lt;p&gt;To test that, I&amp;#39;ve prepared &lt;a href=&quot;https://github.com/jeffkreeftmeijer/wups&quot;&gt;a simple Rails application&lt;/a&gt;. Let&amp;#39;s clone that into the same directory as our server.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls
http_server.rb
$ git clone https://github.com/jeffkreeftmeijer/wups.git blog
Cloning into &amp;#39;blog&amp;#39;...
remote: Counting objects: 162, done.
remote: Compressing objects: 100% (112/112), done.
remote: Total 162 (delta 32), reused 162 (delta 32), pack-reused 0
Receiving objects: 100% (162/162), 29.09 KiB | 0 bytes/s, done.
Resolving deltas: 100% (32/32), done.
Checking connectivity... done.
$ ls
blog           http_server.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in our server, require the Rails application&amp;#39;s environment file instead of &lt;code&gt;rack&lt;/code&gt; and &lt;code&gt;rack/lobster&lt;/code&gt;, and put the &lt;code&gt;Rails.application&lt;/code&gt; in the &lt;code&gt;app&lt;/code&gt; variable instead of &lt;code&gt;Rack::Lobster.new&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
require &amp;#39;socket&amp;#39;
require_relative &amp;#39;blog/config/environment&amp;#39;

app = Rails.application
server = TCPServer.new 5678
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Starting the server (&lt;code&gt;ruby http_server.rb&lt;/code&gt;) and opening &lt;a href=&quot;http://localhost:5678&quot;&gt;http://localhost:5678&lt;/a&gt; shows us we&amp;#39;re not quite there yet. The server doesn&amp;#39;t crash, but we&amp;#39;re greeted with an internal server error in the browser.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2017-01/500.png&quot; alt=&quot;500 Internal Server Error. If you are the administrator of this website, then please read this web application&amp;#39;s log file and/or the web server&amp;#39;s log file to find out what went wrong.&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Checking our server&amp;#39;s logs, we can see that we&amp;#39;re missing something called &lt;code&gt;rack.input&lt;/code&gt;. It turns out that we&amp;#39;ve been lazy while implementing our server last time, so there&amp;#39;s more work to do before we can get this Rails application to work.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ruby http_server.rb
GET / HTTP/1.1
Error during failsafe response: Missing rack.input
  ...
  http_server.rb:15:in `&amp;lt;main&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Rack environment&lt;/h2&gt;
&lt;p&gt;Back when we implemented our server, we glossed over &lt;a href=&quot;http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Environment&quot;&gt;the Rack environment&lt;/a&gt; and ignored most of the variables that are required to properly serve Rack applications. We ended up only implementing the &lt;code&gt;REQUEST_METHOD&lt;/code&gt;, &lt;code&gt;PATH_INFO&lt;/code&gt;, and &lt;code&gt;QUERY_STRING&lt;/code&gt; variables, as those were sufficient for our simple Rack app.&lt;/p&gt;
&lt;p&gt;As we&amp;#39;ve already seen from the exception when we tried to start our new application, Rails needs &lt;code&gt;rack.input&lt;/code&gt;, which is used as an input stream for raw HTTP POST data. Besides that, there are some more variables we need to pass, like the server&amp;#39;s port number, and the request cookie data.&lt;/p&gt;
&lt;p&gt;Luckily, Rack provides &lt;a href=&quot;https://github.com/rack/rack/blob/master/lib/rack/lint.rb&quot;&gt;&lt;code&gt;Rack::Lint&lt;/code&gt;&lt;/a&gt; to help make sure all variables in the Rack environment are present and valid. We can use it to test our server by wrapping our Rails app in it by calling &lt;code&gt;Rack::Lint.new&lt;/code&gt; and passing the &lt;code&gt;Rails.application&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
require &amp;#39;socket&amp;#39;
require_relative &amp;#39;blog/config/environment&amp;#39;

app = Rack::Lint.new(Rails.application)
server = TCPServer.new 5678
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Rack::Lint&lt;/code&gt; will throw an exception when a variable in the environment is missing or invalid. Right now, starting our server again and opening &lt;a href=&quot;http://localhost:5678&quot;&gt;http://localhost:5678&lt;/a&gt; will crash the server and &lt;code&gt;Rack::Lint&lt;/code&gt; will notify us of the first error: the &lt;code&gt;SERVER_NAME&lt;/code&gt; variable wasn&amp;#39;t set.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~/Appsignal/http-server (master) $ ruby http_server.rb
GET / HTTP/1.1
/Users/jeff/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/lint.rb:20:in `assert&amp;#39;: env missing required key SERVER_NAME (Rack::Lint::LintError)
        ...
        from http_server.rb:15:in `&amp;lt;main&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By fixing each error that is thrown at us, we can keep adding variables until &lt;code&gt;Rack::Lint&lt;/code&gt; stops crashing our server. Let&amp;#39;s go over each of the variables &lt;code&gt;Rack::Lint&lt;/code&gt; requires.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SERVER_NAME&lt;/code&gt;: the server&amp;#39;s hostname. We&amp;#39;re only running this server locally right now, so we&amp;#39;ll use &amp;quot;localhost&amp;quot;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SERVER_PORT&lt;/code&gt;: the port our server is running on. We&amp;#39;ve hardcoded the port number (5678), so we&amp;#39;ll just pass that to the Rack environment.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rack.version&lt;/code&gt;: the targeted Rack &lt;em&gt;protocol&lt;/em&gt; version number as an array of integers. &lt;a href=&quot;https://github.com/rack/rack/blob/master/lib/rack.rb#L14&quot;&gt;&lt;code&gt;[1,3]&lt;/code&gt;&lt;/a&gt; at the time of writing.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rack.input&lt;/code&gt;: the input stream containing the raw HTTP post data. We&amp;#39;ll get to this later, but we&amp;#39;ll pass an empty &lt;code&gt;StringIO&lt;/code&gt; instance (with an ASCII-8BIT encoding) for now.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rack.errors&lt;/code&gt;: the error stream for &lt;code&gt;Rack::Logger&lt;/code&gt; to write to. We&amp;#39;re using &lt;code&gt;$stderr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rack.multithread&lt;/code&gt;: our server is single-threaded, so this can be set to &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rack.multiprocess&lt;/code&gt;: our server is running in a single process, so this can be set to &lt;code&gt;false&lt;/code&gt; as well.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rack.run_once&lt;/code&gt;: our server can handle multiple sequential requests in one process, so this is &lt;code&gt;false&lt;/code&gt; too.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rack.url_scheme&lt;/code&gt;: no SSL support, so this can be set to &amp;quot;http&amp;quot; instead of &amp;quot;https&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After adding all missing variables, &lt;code&gt;Rack::Lint&lt;/code&gt; will notify us of one more problem in our environment.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ruby http_server.rb
GET / HTTP/1.1
/Users/jeff/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/lint.rb:20:in `assert&amp;#39;: env variable QUERY_STRING has non-string value nil (Rack::Lint::LintError)
        ...
        from http_server.rb:18:in `&amp;lt;main&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When there&amp;#39;s no query string in the request, we&amp;#39;ll now pass &lt;code&gt;nil&lt;/code&gt; as the &lt;code&gt;QUERY_STRING&lt;/code&gt;, which is not allowed. In that case, Rack expects an empty string instead. After implementing the missing variables and updating the query string, this is what our environment looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
# ...
  method, full_path = request.split(&amp;#39; &amp;#39;)
  path, query = full_path.split(&amp;#39;?&amp;#39;)

  input = StringIO.new
  input.set_encoding &amp;#39;ASCII-8BIT&amp;#39;

  status, headers, body = app.call({
    &amp;#39;REQUEST_METHOD&amp;#39; =&amp;gt; method,
    &amp;#39;PATH_INFO&amp;#39; =&amp;gt; path,
    &amp;#39;QUERY_STRING&amp;#39; =&amp;gt; query || &amp;#39;&amp;#39;,
    &amp;#39;SERVER_NAME&amp;#39; =&amp;gt; &amp;#39;localhost&amp;#39;,
    &amp;#39;SERVER_PORT&amp;#39; =&amp;gt; &amp;#39;5678&amp;#39;,
    &amp;#39;rack.version&amp;#39; =&amp;gt; [1,3],
    &amp;#39;rack.input&amp;#39; =&amp;gt; input,
    &amp;#39;rack.errors&amp;#39; =&amp;gt; $stderr,
    &amp;#39;rack.multithread&amp;#39; =&amp;gt; false,
    &amp;#39;rack.multiprocess&amp;#39; =&amp;gt; false,
    &amp;#39;rack.run_once&amp;#39; =&amp;gt; false,
    &amp;#39;rack.url_scheme&amp;#39; =&amp;gt; &amp;#39;http&amp;#39;
  })

  session.print &amp;quot;HTTP/1.1 #{status}\r\n&amp;quot;
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restarting the server and visiting &lt;a href=&quot;http://localhost:5678&quot;&gt;http://localhost:5678&lt;/a&gt; again, we&amp;#39;ll be greeted with Rails&amp;#39; &amp;quot;You&amp;#39;re on Rails!&amp;quot;-page, meaning we&amp;#39;re now running an actual Rails application on our home made server!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2017-01/youre-on-rails.png&quot; alt=&quot;Yay! You&amp;#39;re on Rails!&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Parsing HTTP POST bodies&lt;/h2&gt;
&lt;p&gt;This application is more than just that index page. Visiting &lt;a href=&quot;http://localhost:5678/posts&quot;&gt;http://localhost:5678/posts&lt;/a&gt; will display an empty list of posts. If we try to create a new post by filling in the new post form and pressing &amp;quot;Create post&amp;quot;, we&amp;#39;re greeted by an &lt;code&gt;ActionController::InvalidAuthenticityToken&lt;/code&gt; exception.&lt;/p&gt;
&lt;p&gt;The authenticity token is sent along when posting a form and is used to check if the request came from a trusted source. Our server is completely ignoring POST data right now, so the token isn&amp;#39;t sent, and the request can&amp;#39;t be verified.&lt;/p&gt;
&lt;p&gt;Back when we first implemented our HTTP server, we used &lt;code&gt;session.gets&lt;/code&gt; to get the first line (called the &lt;a href=&quot;https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1&quot;&gt;Request-Line&lt;/a&gt;), and parsed the HTTP method and path from that. Besides parsing the Request-Line, we ignored the rest of the request.&lt;/p&gt;
&lt;p&gt;To be able to extract the POST data, we&amp;#39;ll first need to understand how an HTTP request is structured. Looking at an example, we can see that the structure resembles an HTTP response:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /posts HTTP/1.1\r\n
Host: localhost:5678\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate\r\n
Accept-Language: en-us\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Origin: http://localhost:5678\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14\r\n
Cookie: _wups_session=LzE0Z2hSZFNseG5TR3dEVEwzNE52U0lFa0pmVGlQZGtZR3AveWlyMEFvUHRPeXlQUzQ4L0xlKzNLVWtqYld2cjdiWkpmclZIaEhJd1R6eDhaZThFbVBlN2p6QWpJdllHL2F4Z3VseUZ6NU1BRTU5Y1crM2lLRVY0UzdSZkpwYkt2SGFLZUQrYVFvaFE0VjZmZlIrNk5BPT0tLUpLTHQvRHQ0T3FycWV0ZFZhVHZWZkE9PQ%3D%3D--4ef4508c936004db748da10be58731049fa190ee\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
Referer: http://localhost:5678/posts/new\r\n
Content-Length: 369\r\n
\r\n
utf8=%E2%9C%93&amp;amp;authenticity_token=3fu7e8v70K0h9o%2FGNiXxaXSVg3nZ%2FuoL60nlhssUEHpQRz%2BM4ZIHjQduQMexvXrNoC2pjmhNPI4xNNA0Qkh5Lg%3D%3D&amp;amp;post%5Btitle%5D=My+first+post&amp;amp;post%5Bcreated_at%281i%29%5D=2017&amp;amp;post%5Bcreated_at%282i%29%5D=1&amp;amp;post%5Bcreated_at%283i%29%5D=23&amp;amp;post%5Bcreated_at%284i%29%5D=18&amp;amp;post%5Bcreated_at%285i%29%5D=47&amp;amp;post%5Bbody%5D=It+works%21&amp;amp;commit=Create+Post
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much like a response, an HTTP request consists of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Request-Line (&lt;code&gt;POST /posts HTTP/1.1\r\n&lt;/code&gt;), consisting of a method token (&lt;code&gt;POST&lt;/code&gt;), a request URI (&lt;code&gt;/posts/&lt;/code&gt;), and the HTTP version (&lt;code&gt;HTTP/1.1&lt;/code&gt;), followed by a CRLF (a carriage return: \r, followed by line feed: \n) to indicate the end of the line&lt;/li&gt;
&lt;li&gt;Header lines (&lt;code&gt;Host: localhost:5678\r\n&lt;/code&gt;). The header key, followed by a colon, then the value, and a CRLF.&lt;/li&gt;
&lt;li&gt;A newline (or a double CRLF) to separate the request line and headers from the body: (&lt;code&gt;\r\n\r\n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The URL encoded POST body&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After using &lt;code&gt;session.gets&lt;/code&gt; to take the first line of the request (the Request-Line), we&amp;#39;re left with some header lines and a body. To get the header lines, we need to retrieve lines from the session until we find a newline (&lt;code&gt;\r\n&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;For each header line, we&amp;#39;ll split on the first colon. Everything before the colon is the key, and everything after is the value. We &lt;code&gt;#strip&lt;/code&gt; the value to remove the newline from the end.&lt;/p&gt;
&lt;p&gt;To know how many bytes we need to read from the request to get the body, we use the &amp;quot;Content-Length&amp;quot; header, which the browser automatically includes when sending a request.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
# ...
  headers = {}
  while (line = session.gets) != &amp;quot;\r\n&amp;quot;
    key, value = line.split(&amp;#39;:&amp;#39;, 2)
    headers[key] = value.strip
  end

  body = session.read(headers[&amp;quot;Content-Length&amp;quot;].to_i)
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, instead of sending an empty object, we&amp;#39;ll send a &lt;code&gt;StringIO&lt;/code&gt; instance with the body we received via the request. Also, since we&amp;#39;re now parsing the cookies from the request&amp;#39;s header, we can add them to the Rack environment in the &lt;code&gt;HTTP_COOKIE&lt;/code&gt; variable to pass the request authenticity check.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
# ...
  status, headers, body = app.call({
    # ...
    &amp;#39;REMOTE_ADDR&amp;#39; =&amp;gt; &amp;#39;127.0.0.1&amp;#39;,
    &amp;#39;HTTP_COOKIE&amp;#39; =&amp;gt; headers[&amp;#39;Cookie&amp;#39;],
    &amp;#39;rack.version&amp;#39; =&amp;gt; [1,3],
    &amp;#39;rack.input&amp;#39; =&amp;gt; StringIO.new(body),
    &amp;#39;rack.errors&amp;#39; =&amp;gt; $stderr,
    # ...
  })
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There we go. If we restart the server and try to submit the form again, you&amp;#39;ll see that we successfully created the first post on our blog!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2017-01/first-post.png&quot; alt=&quot;Post was successfully created.&quot;/&gt;&lt;/p&gt;
&lt;p&gt;We seriously upgraded our web server this time. Instead of just accepting GET requests from a Rack app, we&amp;#39;re now serving a complete Rails app that handles POST requests. And we still haven&amp;#39;t written more than fifty lines of code in total!&lt;/p&gt;
&lt;p&gt;If you want to play around with our new and improved server, &lt;a href=&quot;https://gist.github.com/jeffkreeftmeijer/43d46bca63de648436d6018c73bd436b&quot;&gt;here&amp;#39;s the code&lt;/a&gt;. Let us know at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt; if you want to know more, or have a specific question.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Escaping characters in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2016/12/21/ruby-magic-escaping-in-ruby.html"/>
    <id>https://blog.appsignal.com/2016/12/21/ruby-magic-escaping-in-ruby.html</id>
    <published>2016-12-21T00:00:00+00:00</published>
    <updated>2016-12-21T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Escaping characters in Ruby can be quite confusing. In this post we&#039;ll cover the power of the backslash symbol <code>\</code> and how you can use it.</summary>
    <content type="html">&lt;p&gt;In this post we&amp;#39;re going to be talking about escaping characters in Ruby. We&amp;#39;ll learn how to escape characters, how it works, and how to avoid escaping altogether for some use cases. If you think you know all about the &lt;code&gt;\ &lt;/code&gt;, make sure to read it all and be surprised...&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s dive right in with character escaping in strings.&lt;/p&gt;
&lt;h2&gt;Escaping quotes&lt;/h2&gt;
&lt;p&gt;When using strings in Ruby, we sometimes need to put the quote we used to define the string inside the string itself.
When we do, we can escape the quote character with a backslash &lt;code&gt;\ &lt;/code&gt; symbol.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Escape quotes in double quoted strings
&amp;quot;Hello \&amp;quot;world\&amp;quot;!&amp;quot;
=&amp;gt; &amp;quot;Hello \&amp;quot;world\&amp;quot;!&amp;quot;

# Escape quotes in single quoted strings
&amp;#39;Hello \&amp;#39;world\&amp;#39;!&amp;#39;
=&amp;gt; &amp;quot;Hello &amp;#39;world&amp;#39;!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we can see that Ruby defaults to double quoted strings for output and only escapes those double quotes in the output.&lt;/p&gt;
&lt;h2&gt;Escape sequences&lt;/h2&gt;
&lt;p&gt;Besides quotes, there are more symbols we can escape in strings. For example, a newline is represented by &lt;code&gt;\n&lt;/code&gt;. This is called an &amp;quot;escape sequence&amp;quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;Hello\nworld&amp;quot;
=&amp;gt; &amp;quot;Hello\nworld&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What gets returned is the same string as we created, but as you can see the &lt;code&gt;\n&lt;/code&gt; is highlighted, indicating it&amp;#39;s an escape sequence. If we would now print this string we see that the literal &lt;code&gt;\n&lt;/code&gt; is not printed, but an actual newline is printed instead.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;puts &amp;quot;Hello\nworld&amp;quot;
=&amp;gt; Hello
world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This doesn&amp;#39;t work in single quoted strings. The &lt;code&gt;\n&lt;/code&gt; sequence is interpreted as a literal &lt;code&gt;\n&lt;/code&gt;. In double quoted strings you would have to escape the backslash symbol to accomplish the same result.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;#39;\n&amp;#39;
=&amp;gt; &amp;quot;\\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Examples of &lt;a href=&quot;https://en.wikipedia.org/wiki/Escape_sequences_in_C&quot;&gt;other escape sequences&lt;/a&gt; that work the same way are: &lt;code&gt;\t&lt;/code&gt;, &lt;code&gt;\s&lt;/code&gt; and &lt;code&gt;\b&lt;/code&gt;, which represent a tab, a space and a backspace respectively.&lt;/p&gt;
&lt;header className=&quot;ruby_magic&quot;&gt;
  &lt;h2&gt;Single quotes &lt;strike&gt;vs&lt;/strike&gt; and double quotes&lt;/h2&gt;
  &lt;p&gt;The difference between single and double quoted strings in Ruby is the way the string definitions represent escape sequences.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    In &lt;strong&gt;double&lt;/strong&gt; quoted strings, you can write escape sequences
    and Ruby will output their translated meaning. A &lt;code&gt;\n&lt;/code&gt; becomes a
    newline.
  &lt;/li&gt;
  &lt;li&gt;
    In &lt;strong&gt;single&lt;/strong&gt; quoted strings however, escape sequences are
    escaped and return their literal definition. A &lt;code&gt;\n&lt;/code&gt; remains a{&quot; &quot;}
    &lt;code&gt;\n&lt;/code&gt;.
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
  Both string types have their use cases of course. It&#039;s likely you&#039;ll use
  double quoted strings with escape sequences to use their translated meaning, a
  newline or a tab.
&lt;/p&gt;

  &lt;p&gt;Single quotes are useful for avoiding escaping escape sequences themselves. Useful for demonstrating their usage or avoiding accidentally using escape sequences you didn&#039;t intent to.&lt;/p&gt;
&lt;/header&gt;

&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;puts &amp;quot;Line 1\nLine 2&amp;quot;
=&amp;gt; Line 1
Line 2

puts &amp;#39;Using a \n we can indicate a newline.&amp;#39;
=&amp;gt; Using a \n we can indicate a newline.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Escaping interpolation&lt;/h2&gt;
&lt;p&gt;Ruby supports interpolation inside strings. But once again, not all string definitions are created equal. Interpolation only works in double quoted strings.&lt;/p&gt;
&lt;p&gt;In the code example below we see that interpolation works in a double quoted string, but that Ruby escapes the interpolation sequence in a single quoted string, rendering it useless.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;name = &amp;quot;world&amp;quot;

&amp;quot;Hello #{name}&amp;quot;
=&amp;gt; &amp;quot;Hello world&amp;quot;

&amp;#39;Hello #{name}&amp;#39;
=&amp;gt; &amp;quot;Hello \#{name}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This behavior applies to almost every kind of string &amp;quot;magic&amp;quot;. Double quotes support it, single quotes don&amp;#39;t.&lt;/p&gt;
&lt;header className=&quot;ruby_magic&quot;&gt;
  &lt;h2&gt;Percent notation for String&lt;/h2&gt;
  &lt;p&gt;The &lt;a href=&quot;https://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals#The_.25_Notation&quot;&gt;percent notation&lt;/a&gt; in Ruby is something inspired by the Perl programming language and gives us many shorthands for some common type definitions.&lt;/p&gt;

&lt;p&gt;
  The same behavior for single and double quoted strings applies to the percent
  notation as well.
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;code&gt;%()&lt;/code&gt; and &lt;code&gt;%Q()&lt;/code&gt; behave the same way as a double
    quoted string.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;code&gt;%q()&lt;/code&gt; behaves the same way as a single quoted string.
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
  The problem with the percent notation is that you now need to escape the
  parenthesis characters &lt;code&gt;()&lt;/code&gt; instead of the quotes when the
  parenthesis aren&#039;t balanced.
&lt;/p&gt;

  &lt;p&gt;Which is why Ruby allows you to use other symbols for the percent notation as well: &lt;code&gt;%[foo]&lt;/code&gt;, &lt;code&gt;{`%{foo}`}&lt;/code&gt;, &lt;code&gt;%-foo-&lt;/code&gt;, &lt;code&gt;%?foo?&lt;/code&gt;, etc. It even supports &lt;code&gt;%&quot;foo&quot;&lt;/code&gt; and &lt;code&gt;%&#039;foo&#039;&lt;/code&gt;.&lt;/p&gt;
&lt;/header&gt;

&lt;p&gt;Here we see that Ruby escapes the string for us when using the percent notation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;%(Hello &amp;quot;world&amp;quot;!)
=&amp;gt; &amp;quot;Hello \&amp;quot;world\&amp;quot;&amp;quot;

# With unbalanced parenthesis
%(Hello world\)!)
=&amp;gt; &amp;quot;Hello world)!&amp;quot;

# With balanced parenthesis
%(Hello (world)!)
=&amp;gt; &amp;quot;Hello (world)!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, the same rules apply here as with normal single and double quoted strings. Escape sequences and interpolation are escaped by default in single quoted strings.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;name = &amp;quot;world&amp;quot;

%(Hello\n &amp;quot;#{name}&amp;quot;!)
=&amp;gt; &amp;quot;Hello\n \&amp;quot;world\&amp;quot;!&amp;quot;

%q(Hello\n &amp;quot;#{name}&amp;quot;!)
=&amp;gt; &amp;quot;Hello\\n \&amp;quot;\#{name}\&amp;quot;!&amp;quot;

%Q(Hello\n &amp;quot;#{name}&amp;quot;!)
=&amp;gt; &amp;quot;Hello\n \&amp;quot;world\&amp;quot;!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Escaping characters in regular expressions&lt;/h2&gt;
&lt;p&gt;Escaping characters also works in regular expressions. In regular expressions many characters represent more than just their literal definition.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.&lt;/code&gt; is a wild card for any character, brackets &lt;code&gt;[]&lt;/code&gt; represent a range or a selection, parenthesis &lt;code&gt;()&lt;/code&gt; match an expression, etc. To use their literal definitions we can also escape them with the backslash symbol.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;/Hello \[world\]/
=&amp;gt; /Hello \[world\]/

/\[world\]/ =~ &amp;quot;Hello [world]&amp;quot;
=&amp;gt; 6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we didn&amp;#39;t escape the brackets, it would instead look for any of the characters between the brackets and find the first match in the character &lt;code&gt;l&lt;/code&gt; in &amp;quot;Hello&amp;quot; on position 3.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;/[world]/ =~ &amp;quot;Hello [world]&amp;quot;
=&amp;gt; 2 # zero index
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To avoid having to escape the forward slash symbol &lt;code&gt;/&lt;/code&gt;, used to define the regular expression, we can use another percent notation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;%r{/world/}
=&amp;gt; /\/world\//
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;%r{}&lt;/code&gt; we can define a regular expression where we don&amp;#39;t have to escape the forward slash. In the result we can see that Ruby escapes the forward slash for us.&lt;/p&gt;
&lt;h2&gt;Escaping line breaks&lt;/h2&gt;
&lt;p&gt;Previously we escaped the &amp;quot;newline&amp;quot; escape sequence &lt;code&gt;\n&lt;/code&gt; in a string in Ruby. Did you know you can also escape a line break in Ruby itself?&lt;/p&gt;
&lt;p&gt;For example, we have a very long line for a method call and this breaks our code style guide&amp;#39;s max line length.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ruby_method_with_many_arguments &amp;quot;Hello world&amp;quot;, split: &amp;quot; &amp;quot;, join: &amp;quot;\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can place the arguments on separate lines to make each individual line shorter. This works as long as the previous line ends with a comma &lt;code&gt;,&lt;/code&gt;, operators like the plus symbol &lt;code&gt;+&lt;/code&gt;, or is part of a method call wrapped in parenthesis &lt;code&gt;()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ruby_method_with_many_arguments &amp;quot;Hello world&amp;quot;,
  split: &amp;quot; &amp;quot;,
  join: &amp;quot;\n&amp;quot;

ruby_method_with_many_arguments(
  &amp;quot;Hello world&amp;quot;,
  split: &amp;quot; &amp;quot;,
  join: &amp;quot;\n&amp;quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If instead we&amp;#39;d want to align all arguments on the same indentation level without wrapping it in parenthesis &lt;code&gt;()&lt;/code&gt; we can use a backslash &lt;code&gt;\ &lt;/code&gt; to escape the line break.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;ruby_method_with_many_arguments \
  my_string,
  split: &amp;quot; &amp;quot;,
  join: &amp;quot;\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What we do here is not really telling Ruby to escape the newline, but that the statement will continue on the next line.&lt;/p&gt;
&lt;h2&gt;Escaping line breaks in string definitions&lt;/h2&gt;
&lt;p&gt;Escaping the line ending also works for things like string definitions. Normally if we have a multi-line string we can use the plus symbol &lt;code&gt;+&lt;/code&gt; to combine the strings on the two lines.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;foo&amp;quot; +
  &amp;quot;bar&amp;quot;
=&amp;gt; &amp;quot;foobar&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates two strings on two lines and then combines as one. So it really creates three string objects.&lt;/p&gt;
&lt;p&gt;Instead of using a plus symbol, we can escape the line break with a backslash again.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;&amp;quot;foo&amp;quot; \
  &amp;quot;bar&amp;quot;
=&amp;gt; &amp;quot;foobar&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result is the same, or is it?&lt;/p&gt;
&lt;p&gt;In the first example we&amp;#39;re actually combining two new strings, while in the second example Ruby will only create one. The Ruby interpreter will see the backslash &lt;code&gt;\ &lt;/code&gt; as a continuation of the string definition and only create one string based on the two lines. So it&amp;#39;s even better for your app&amp;#39;s memory usage.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Escaping characters in Ruby, and many other programming languages, can be quite confusing. Suddenly certain combinations of characters get a different meaning inside a double quoted string compared to being used inside a single quoted string.&lt;/p&gt;
&lt;p&gt;Hopefully this little guide has helped you with understanding how to properly escape characters and sequences in Ruby. The difference between a double and single quoted string is key here.&lt;/p&gt;
&lt;p&gt;The string &amp;quot;magic&amp;quot; of escape sequences and interpolation only really works in double quoted strings, but not in single quoted strings.&lt;/p&gt;
&lt;p&gt;There are also other ways of escaping code in Ruby itself. The percent notation helps us with avoiding to escape certain characters all the time, in strings and regular expressions.&lt;/p&gt;
&lt;p&gt;But the backslash is also very useful for writing multi-line strings and continuing a line of Ruby code on the next line. Something I use a lot to keep my code clean and readable.&lt;/p&gt;
&lt;p&gt;Let us know at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt; if you want to know more, or have a specific question about escaping characters in Ruby.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Fun side-note: for this post, which is written in Markdown, I had to use the backslash symbol quite a lot. Just like in Ruby, Markdown allows escaping of certain characters. Normally in Markdown you can&amp;#39;t really wrap a backslash in a code block as it escapes the code block itself. Instead I had it escape a space instead.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;`\` # Doesn&amp;#39;t work
`\ ` # Works :)
&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <title>Building a 30 line HTTP server in Ruby</title>
    <link rel="alternate" href="https://blog.appsignal.com/2016/11/23/ruby-magic-building-a-30-line-http-server-in-ruby.html"/>
    <id>https://blog.appsignal.com/2016/11/23/ruby-magic-building-a-30-line-http-server-in-ruby.html</id>
    <published>2016-11-23T00:00:00+00:00</published>
    <updated>2016-11-23T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Web servers, and HTTP in general, might seem difficult to understand. In this Ruby Magic episode we&#039;ll learn how a to build a minimal Ruby HTTP server in 30 lines of code</summary>
    <content type="html">&lt;p&gt;Web servers, and HTTP in general, might seem difficult to understand. How does the browser format a request, and how does the response get sent to the user? In this Ruby Magic episode we&amp;#39;ll learn how a to build a Ruby HTTP server in 30 lines of code. When we&amp;#39;re done, our server will handle HTTP GET requests and we&amp;#39;ll use it to serve a Rack app.&lt;/p&gt;
&lt;h2&gt;How HTTP and TCP work together&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Transmission_Control_Protocol&quot;&gt;TCP&lt;/a&gt; is a transport protocol that describes how a server and a client exchange data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol&quot;&gt;HTTP&lt;/a&gt; is a request-response protocol that specifically describes how web servers exchange data with HTTP clients or web browsers. HTTP commonly uses TCP as its transport protocol. In essence, an HTTP server is a TCP server that &amp;quot;speaks&amp;quot; HTTP.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# tcp_server.rb
require &amp;#39;socket&amp;#39;
server = TCPServer.new 5678

while session = server.accept
  session.puts &amp;quot;Hello world! The time is #{Time.now}&amp;quot;
  session.close
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example of a TCP server, the server binds to port &lt;code&gt;5678&lt;/code&gt; and waits for a client to connect. When that happens, it sends a message to the client, and then closes the connection. After it&amp;#39;s done talking to the first client, the server waits for another client to connect to send its message to again.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# tcp_client.rb
require &amp;#39;socket&amp;#39;
server = TCPSocket.new &amp;#39;localhost&amp;#39;, 5678

while line = server.gets
  puts line
end

server.close
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To connect to our server, we&amp;#39;ll need a TCP client. This example client connects to the same port (&lt;code&gt;5678&lt;/code&gt;) and uses &lt;code&gt;server.gets&lt;/code&gt; to receive data from the server, which is then printed. When it stops receiving data, it closes the connection to the server and the program will exit.&lt;/p&gt;
&lt;p&gt;When you start the server server is running (&lt;code&gt;$ ruby tcp_server.rb&lt;/code&gt;), you can start the client in a separate tab to receive the server&amp;#39;s message.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ruby tcp_client.rb
Hello world! The time is 2016-11-23 15:17:11 +0100
$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a bit of imagination, our TCP server and client work somewhat like a web server and a browser. The client sends a request, the server responds, and the connection is closed. That&amp;#39;s how the &lt;a href=&quot;https://en.wikipedia.org/wiki/Request%E2%80%93response&quot;&gt;request-response pattern&lt;/a&gt; works, which is exactly what we need to build an HTTP server.&lt;/p&gt;
&lt;p&gt;Before we get to the good part, let&amp;#39;s look at what HTTP requests and responses look like.&lt;/p&gt;
&lt;h2&gt;A basic HTTP GET request&lt;/h2&gt;
&lt;p&gt;The most basic &lt;a href=&quot;https://tools.ietf.org/html/rfc2616#section-5&quot;&gt;HTTP GET request&lt;/a&gt; is a &lt;a href=&quot;https://tools.ietf.org/html/rfc2616#section-5.1&quot;&gt;request-line&lt;/a&gt; without any additional headers or a request body.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET / HTTP/1.1\r\n
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Request-Line consists of four parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A method token (&lt;code&gt;GET&lt;/code&gt;, in this example)&lt;/li&gt;
&lt;li&gt;The Request-URI (&lt;code&gt;/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The protocol version (&lt;code&gt;HTTP/1.1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A CRLF (a carriage return: &lt;code&gt;\r&lt;/code&gt;, followed by line feed: &lt;code&gt;\n&lt;/code&gt;) to indicate the end of the line&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The server will respond with an &lt;a href=&quot;https://tools.ietf.org/html/rfc2616#section-6&quot;&gt;HTTP response&lt;/a&gt;, which may look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200\r\nContent-Type: text/html\r\n\r\n\Hello world!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This response consists of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A status line: the protocol version (&amp;quot;HTTP/1.1&amp;quot;), followed by a space, the response&amp;#39;s status code (&amp;quot;200&amp;quot;), and terminated with a CRLF (&lt;code&gt;\r\n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Optional header lines. In this case, there&amp;#39;s only one header line (&amp;quot;Content-Type: text/html&amp;quot;), but there could be multiple (separated with with a CRLF: &lt;code&gt;\r\n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A newline (or a double CRLF) to separate the status line and header from the body: (&lt;code&gt;\r\n\r\n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The body: &amp;quot;Hello world!&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A Minimal Ruby HTTP server&lt;/h2&gt;
&lt;p&gt;Enough talk. Now that we know how to create a TCP server in Ruby and what some HTTP requests and responses look like, we can build a minimal HTTP server. You&amp;#39;ll notice that the web server looks mostly the same as the TCP server we discussed earlier. The general idea is the same, we&amp;#39;re just using the HTTP protocol to format our message. Also, because we&amp;#39;ll use a browser to send requests and parse responses, we won&amp;#39;t have to implement a client this time.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
require &amp;#39;socket&amp;#39;
server = TCPServer.new 5678

while session = server.accept
  request = session.gets
  puts request

  session.print &amp;quot;HTTP/1.1 200\r\n&amp;quot; # 1
  session.print &amp;quot;Content-Type: text/html\r\n&amp;quot; # 2
  session.print &amp;quot;\r\n&amp;quot; # 3
  session.print &amp;quot;Hello world! The time is #{Time.now}&amp;quot; #4

  session.close
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the server receives a request, like before, it uses &lt;code&gt;session.print&lt;/code&gt; to send a message back to the client: Instead of just our message, it prefixes the response with a status line, a header and a newline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The status line (&lt;code&gt;HTTP 1.1 200\r\n&lt;/code&gt;) to tell the browser that the HTTP version is 1.1 and the response code is &amp;quot;200&amp;quot;&lt;/li&gt;
&lt;li&gt;A header to indicate that the response has a text/html content type (&lt;code&gt;Content-Type: text/html\r\n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The newline (&lt;code&gt;\r\n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The body: &amp;quot;Hello world! …&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Like before, it closes the connection after sending the message. We&amp;#39;re not reading the request yet, so it just prints it to the console for now.&lt;/p&gt;
&lt;p&gt;If you start the server and open &lt;a href=&quot;http://localhost:5678&quot;&gt;http://localhost:5678&lt;/a&gt; in your browser, you should see the &amp;quot;Hello world! …&amp;quot;-line with the current time, like we received from our TCP client earlier. 🎉&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2016-11/http_server.png&quot; alt=&quot;Our minimal Ruby HTTP server returning our &amp;quot;Hello world&amp;quot;-line&quot;/&gt;&lt;/p&gt;
&lt;h2&gt;Serving a Rack app&lt;/h2&gt;
&lt;p&gt;Until now, our server has been returning a single response for each request. To make it a little more useful, we could add more responses to our server. Instead of adding these to the server directly, we&amp;#39;ll use a &lt;a href=&quot;http://rack.github.io&quot;&gt;Rack&lt;/a&gt; app. Our server will parse HTTP requests and pass them to the Rack app, which will then return a response for the server to send back to the client.&lt;/p&gt;
&lt;p&gt;Rack is an interface between web servers that support Ruby and most Ruby web frameworks like Rails and Sinatra. In its simplest form, a Rack app is an object that responds to &lt;code&gt;call&lt;/code&gt; and returns a &amp;quot;tiplet&amp;quot;, an array with three items: an HTTP response code, a hash of HTTP headers and a body.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;app = Proc.new do |env|
  [&amp;#39;200&amp;#39;, {&amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;text/html&amp;#39;}, [&amp;quot;Hello world! The time is #{Time.now}&amp;quot;]]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the response code is &amp;quot;200&amp;quot;, we&amp;#39;re passing &amp;quot;text/html&amp;quot; as the content type through the headers, and the body is an array with a string.&lt;/p&gt;
&lt;p&gt;To allow our server to serve responses from this app, we&amp;#39;ll need to turn the returned triplet into a HTTP response string. Instead of always returning a static response, like we did before, we&amp;#39;ll now have to build the response from the triplet returned by the Rack app.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
require &amp;#39;socket&amp;#39;

app = Proc.new do
  [&amp;#39;200&amp;#39;, {&amp;#39;Content-Type&amp;#39; =&amp;gt; &amp;#39;text/html&amp;#39;}, [&amp;quot;Hello world! The time is #{Time.now}&amp;quot;]]
end

server = TCPServer.new 5678

while session = server.accept
  request = session.gets
  puts request

  # 1
  status, headers, body = app.call({})

  # 2
  session.print &amp;quot;HTTP/1.1 #{status}\r\n&amp;quot;

  # 3
  headers.each do |key, value|
    session.print &amp;quot;#{key}: #{value}\r\n&amp;quot;
  end

  # 4
  session.print &amp;quot;\r\n&amp;quot;

  # 5
  body.each do |part|
    session.print part
  end
  session.close
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To serve the response we&amp;#39;ve received from the Rack app, there&amp;#39;s some changes we&amp;#39;ll make to our server:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the status code, headers, and body from the triplet returned by &lt;code&gt;app.call&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the status code to build the status line&lt;/li&gt;
&lt;li&gt;Loop over the headers and add a header line for each key-value pair in the hash&lt;/li&gt;
&lt;li&gt;Print a newline to separate the status line and headers from the body&lt;/li&gt;
&lt;li&gt;Loop over the body and print each part. Since there&amp;#39;s only one part in our body array, it&amp;#39;ll simply print our &amp;quot;Hello world&amp;quot;-message to the session before closing it.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Reading requests&lt;/h2&gt;
&lt;p&gt;Until now, our server has been ignoring the &lt;code&gt;request&lt;/code&gt; variable. We didn&amp;#39;t need to as our Rack app always returned the same response.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Rack::Lobster&lt;/code&gt; is an example app that ships with Rack and uses request URL parameters in order to function. Instead of the Proc we used as an app before, we&amp;#39;ll use that as our testing app from now on.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
require &amp;#39;socket&amp;#39;
require &amp;#39;rack&amp;#39;
require &amp;#39;rack/lobster&amp;#39;

app = Rack::Lobster.new
server = TCPServer.new 5678

while session = server.accept
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Opening the browser will now show a lobster instead of the boring string it printed before. Lobstericious!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/2016-11/http_server_lobster.png&quot; alt=&quot;Our minimal Ruby HTTP server running Rack::Lobster&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The &amp;quot;flip!&amp;quot; and &amp;quot;crash!&amp;quot; links link to &lt;code&gt;/?flip=left&lt;/code&gt; and &lt;code&gt;/?flip=crash&lt;/code&gt; respectively. However, when following the links, the lobster doesn&amp;#39;t flip and nothing crashes just yet. That&amp;#39;s because our server doesn&amp;#39;t handle query strings right now. Remember the &lt;code&gt;request&lt;/code&gt; variable we ignored before? If we look at our server&amp;#39;s logs, we&amp;#39;ll see the request strings for each of the pages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET / HTTP/1.1
GET /?flip=left HTTP/1.1
GET /?flip=crash HTTP/1.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The HTTP request strings include the request method (&amp;quot;GET&amp;quot;), the request path (&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/?flip=left&lt;/code&gt; and &lt;code&gt;/?flip=crash&lt;/code&gt;), and the HTTP version. We can use this information to determine what we need to serve.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# http_server.rb
require &amp;#39;socket&amp;#39;
require &amp;#39;rack&amp;#39;
require &amp;#39;rack/lobster&amp;#39;

app = Rack::Lobster.new
server = TCPServer.new 5678

while session = server.accept
  request = session.gets
  puts request

  # 1
  method, full_path = request.split(&amp;#39; &amp;#39;)
  # 2
  path, query = full_path.split(&amp;#39;?&amp;#39;)

  # 3
  status, headers, body = app.call({
    &amp;#39;REQUEST_METHOD&amp;#39; =&amp;gt; method,
    &amp;#39;PATH_INFO&amp;#39; =&amp;gt; path,
    &amp;#39;QUERY_STRING&amp;#39; =&amp;gt; query
  })

  session.print &amp;quot;HTTP/1.1 #{status}\r\n&amp;quot;
  headers.each do |key, value|
    session.print &amp;quot;#{key}: #{value}\r\n&amp;quot;
  end
  session.print &amp;quot;\r\n&amp;quot;
  body.each do |part|
    session.print part
  end
  session.close
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To parse the request and send the request parameters to the Rack app, we&amp;#39;ll split the request string up and send it to the Rack app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Split the request string into a method and a full path&lt;/li&gt;
&lt;li&gt;Split the full path into a path and a query&lt;/li&gt;
&lt;li&gt;Pass those to our app in a &lt;a href=&quot;http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Environment&quot;&gt;Rack environment hash&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, a request like &lt;code&gt;GET /?flip=left HTTP/1.1\r\n&lt;/code&gt; will be passed to the app like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;{
  &amp;#39;REQUEST_METHOD&amp;#39; =&amp;gt; &amp;#39;GET&amp;#39;,
  &amp;#39;PATH_INFO&amp;#39; =&amp;gt; &amp;#39;/&amp;#39;,
  &amp;#39;QUERY_STRING&amp;#39; =&amp;gt; &amp;#39;?flip=left&amp;#39;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restarting our server, visiting &lt;a href=&quot;http://localhost:5678&quot;&gt;http://localhost:5678&lt;/a&gt;, and clicking the &amp;quot;flip!&amp;quot;-link will now flip the lobster, and clicking the &amp;quot;crash!&amp;quot; link will crash our web server.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve just scratched the surface of implementing a HTTP server, and ours is only 30 lines of code, but it explains the basic idea. It accepts GET requests, passes the request&amp;#39;s attributes to a Rack app, and sends back responses to the browser. Although it doesn&amp;#39;t handle things like request streaming and POST requests, our server could theoretically be used to serve other Rack apps too.&lt;/p&gt;
&lt;p&gt;This concludes our quick look into building an HTTP server in Ruby. If you want to play around with our server, here&amp;#39;s the &lt;a href=&quot;https://gist.github.com/jeffkreeftmeijer/7f08d1f7e381b9c552666750914925eb&quot;&gt;code&lt;/a&gt;. Let us know at &lt;a href=&quot;https://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt; if you want to know more, or have a specific question.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you enjoyed this article, &lt;a href=&quot;https://blog.appsignal.com/ruby-magic&quot;&gt;subscribe to the Ruby Magic newsletter&lt;/a&gt;: a (roughly) monthly po(r)tion of Ruby.&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Rescuing Exceptions in Ruby:
A Primer
</title>
    <link rel="alternate" href="https://blog.appsignal.com/2016/10/18/ruby-magic-exceptions-primer.html"/>
    <id>https://blog.appsignal.com/2016/10/18/ruby-magic-exceptions-primer.html</id>
    <published>2016-10-18T00:00:00+00:00</published>
    <updated>2016-10-18T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Make sure your exception handling is done right. Get up to speed with this Ruby Magic primer and make sure you only rescue the exceptions you need to.</summary>
    <content type="html">&lt;p&gt;At AppSignal we provide &lt;a href=&quot;https://www.appsignal.com/tour/errors&quot;&gt;error tracking&lt;/a&gt; for Ruby applications. To do so, we capture all exceptions applications throw at us and notify developers as they happen.&lt;/p&gt;
&lt;p&gt;It can be difficult to get exception handling right. In this article we&amp;#39;ll explain how it works, what problems bad handling can cause and how to rescue exceptions properly.&lt;/p&gt;
&lt;h2&gt;Rescuing exceptions&lt;/h2&gt;
&lt;p&gt;By rescuing exceptions in Ruby you can prevent your application from crashing the moment something goes wrong. With a &lt;code&gt;begin .. rescue&lt;/code&gt; block you can specify an alternative path for your application when an error occurs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  File.read &amp;quot;config.yml&amp;quot;
rescue
  puts &amp;quot;No config file found. Using defaults.&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s also possible to specify which exceptions should be rescued. When specifying an exception class, all subclasses of this exception will also be captured.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  File.read &amp;quot;config.yml&amp;quot;
rescue SystemCallError =&amp;gt; e
  puts e.class # =&amp;gt; Errno::ENOENT
  puts e.class.superclass # =&amp;gt; SystemCallError
  puts e.class.superclass.superclass # =&amp;gt; StandardError
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above you can see the exception &lt;code&gt;Errno::ENOENT&lt;/code&gt; is caught when its parent &lt;code&gt;SystemCallError&lt;/code&gt; is being rescued.&lt;/p&gt;
&lt;h2&gt;Rescuing too high up in the exception chain&lt;/h2&gt;
&lt;p&gt;It&amp;#39;s important not to rescue exceptions too high up the &lt;a href=&quot;http://ruby-doc.org/core-2.3.1/Exception.html&quot;&gt;Exception chain&lt;/a&gt;. When you do, all subclassed exceptions will also be caught, making the rescue block&amp;#39;s capture too generic.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s a program that reads a config file based on the argument passed to the program.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# $ ruby example.rb config.yml
def config_file
  ARGV.firs # Note the typo here, we meant `ARGV.first`.
end

begin
  File.read config_file
rescue
  puts &amp;quot;Couldn&amp;#39;t read the config file&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The error message says it couldn&amp;#39;t read the config file, but the real problem was a typo in the code.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  File.read config_file
rescue =&amp;gt; e
  puts e.inspect
end
#&amp;lt;NoMethodError: undefined method `firs&amp;#39; for []:Array&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The default exception class caught by a &lt;code&gt;begin .. rescue&lt;/code&gt; block is &lt;a href=&quot;http://ruby-doc.org/core-2.3.1/StandardError.html&quot;&gt;StandardError&lt;/a&gt;. If we don&amp;#39;t pass in a specific class, Ruby will rescue StandardError and all subclassed errors. &lt;a href=&quot;http://ruby-doc.org/core-2.3.1/NoMethodError.html&quot;&gt;NoMethodError&lt;/a&gt; is one of these errors.&lt;/p&gt;
&lt;p&gt;Rescuing a specific exception class will help prevent unrelated errors from accidentally prompting a failure state. It also allows for more specific custom error messages that are more helpful for the end user.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;config_file = &amp;quot;config.yml&amp;quot;
begin
  File.read config_file
rescue Errno::ENOENT =&amp;gt; e
  puts &amp;quot;File or directory #{config_file} doesn&amp;#39;t exist.&amp;quot;
rescue Errno::EACCES =&amp;gt; e
  puts &amp;quot;Can&amp;#39;t read from #{config_file}. No permission.&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Rescuing Exception&lt;/h2&gt;
&lt;p&gt;It might still be tempting to rescue high up in the exception chain. Rescuing all errors an application can raise will prevent it from crashing. (100% uptime here we come!) However, it can cause a lot of problems.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://ruby-doc.org/core-2.3.1/Exception.html&quot;&gt;Exception&lt;/a&gt; class is the main exception class in Ruby. All other exceptions are subclasses of this class; if Exception is rescued all errors will be caught.&lt;/p&gt;
&lt;p&gt;Two exceptions that most applications won&amp;#39;t want to rescue are are SignalException and SystemExit.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://ruby-doc.org/core-2.3.1/SignalException.html&quot;&gt;SignalException&lt;/a&gt; is used when an outside source is telling the application to stop. This can be the Operating System when it wants to shut down, or a system administrator that wants to stop the application. &lt;a href=&quot;https://gist.github.com/tombruijn/a4181e217b8e1c46ebcc2c116223cb6a&quot;&gt;Example&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://ruby-doc.org/core-2.3.1/SystemExit.html&quot;&gt;SystemExit&lt;/a&gt; is used when &lt;code&gt;exit&lt;/code&gt; is being called from the Ruby application. When this is raised the developer wants the application to stop. &lt;a href=&quot;https://gist.github.com/tombruijn/b75dcd6722d67f7a982fddc23f295b02&quot;&gt;Example&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;If we rescue Exception and these exceptions are raised while an application is currently running the &lt;code&gt;begin ... rescue ... end&lt;/code&gt; block it cannot exit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&amp;#39;s generally a bad idea to rescue Exception in normal situations. When rescuing Exception, you&amp;#39;ll prevent SignalException and SystemExit to function, but also &lt;a href=&quot;http://ruby-doc.org/core-2.3.1/LoadError.html&quot;&gt;LoadError&lt;/a&gt;, &lt;a href=&quot;http://ruby-doc.org/core-2.3.1/SyntaxError.html&quot;&gt;SyntaxError&lt;/a&gt; and &lt;a href=&quot;http://ruby-doc.org/core-2.3.1/NoMemoryError.html&quot;&gt;NoMemoryError&lt;/a&gt;, to name a few. It&amp;#39;s better to rescue more specific exceptions instead.&lt;/p&gt;
&lt;h2&gt;Failures in tests&lt;/h2&gt;
&lt;p&gt;When Exception is rescued, using &lt;code&gt;rescue Exception =&amp;gt; e&lt;/code&gt;, other things beside your application could break. The test suite could actually be hiding some errors.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://github.com/seattlerb/minitest&quot;&gt;minitest&lt;/a&gt; and &lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt; assertions that fail will raise an exception to inform you about the failed assertion, failing the test. When they do, they raise their own custom exceptions, subclassed from Exception.&lt;/p&gt;
&lt;p&gt;If Exception is rescued in a test or in the application code, it could be silencing an assertion failure.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# RSpec example
def foo(bar)
  bar.baz
rescue Exception =&amp;gt; e
  puts &amp;quot;This test should actually fail&amp;quot;
  # Failure/Error: bar.baz
  #   &amp;lt;Double (anonymous)&amp;gt; received unexpected message :baz with (no args)
end

describe &amp;quot;#foo&amp;quot; do
  it &amp;quot;hides an &amp;#39;unexpected message&amp;#39; exception&amp;quot; do
    bar = double(to_s: &amp;quot;&amp;quot;)
    foo(bar)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Expecting exceptions&lt;/h2&gt;
&lt;p&gt;Some code is meant to raise exceptions. In a test suite it&amp;#39;s possible to simply silence the exception in order to have the test not fail when they are raised.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def foo
  raise RuntimeError, &amp;quot;something went wrong&amp;quot;
end

foo rescue RuntimeError
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this doesn&amp;#39;t test if an exception was raised or not. When the exception is not raised, your test won&amp;#39;t be able to tell if the behavior is still correct.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s possible to assert if the exception is raised, and if not, which exception was.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# expecting_exceptions_spec.rb
# RSpec example
def foo
  raise NotImplementedError, &amp;quot;foo method not implemented&amp;quot;
end

describe &amp;quot;#foo&amp;quot; do
  it &amp;quot;raises a RuntimeError&amp;quot; do
    expect { foo }.to raise_error(RuntimeError)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;1) #foo raises a RuntimeError
   Failure/Error: expect { foo }.to raise_error(RuntimeError)

     expected RuntimeError, got #&amp;lt;NotImplementedError: foo method not implemented&amp;gt; with backtrace:
       # ./expecting_exceptions_spec.rb:4:in `foo&amp;#39;
       # ./expecting_exceptions_spec.rb:9:in `block (3 levels) in &amp;lt;top (required)&amp;gt;&amp;#39;
       # ./expecting_exceptions_spec.rb:9:in `block (2 levels) in &amp;lt;top (required)&amp;gt;&amp;#39;
       # ./expecting_exceptions_spec.rb:9:in `block (2 levels) in &amp;lt;top (required)&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Re-raise Exception&lt;/h2&gt;
&lt;p&gt;An application should only capture exceptions as high up in the chain as the Exception class when there&amp;#39;s a very good reason. For example, when there&amp;#39;s some cleanup involved before exiting a block of code, like removing temporary files that really need to be removed.&lt;/p&gt;
&lt;p&gt;One recommendation for when you absolutely have to rescue Exception, re-raise it after you&amp;#39;re done handling the error. This way the Ruby exception handling can decide the fate of the process afterward.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;File.open(&amp;quot;/tmp/my_app.status&amp;quot;, &amp;quot;w&amp;quot;) { |f| &amp;quot;running&amp;quot; }

begin
  foo
rescue Exception =&amp;gt; e
  Appsignal.add_error e
  File.open(&amp;quot;/tmp/my_app.status&amp;quot;, &amp;quot;w&amp;quot;) { |f| &amp;quot;stopped&amp;quot; }
  raise e
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Unsure what to rescue?&lt;/h2&gt;
&lt;p&gt;As mentioned earlier, it&amp;#39;s good to be specific in what errors to rescue.&lt;/p&gt;
&lt;p&gt;When you&amp;#39;re unsure what exceptions an operation can raise, rescuing &lt;a href=&quot;http://ruby-doc.org/core-2.3.1/StandardError.html&quot;&gt;StandardError&lt;/a&gt; can be a good place to start. Run your code in different scenarios and see what exceptions it raises.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  File.open(&amp;#39;/tmp/appsignal.log&amp;#39;, &amp;#39;a&amp;#39;) { |f| f.write &amp;quot;Starting AppSignal&amp;quot; }
rescue =&amp;gt; e
  puts e.inspect
end
#&amp;lt;Errno::EACCES: Permission denied @ rb_sysopen - /tmp/appsignal.log&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every time you come across a new exception, add specific rescue cases for those exceptions or its relevant parent class. It&amp;#39;s better to be specific in what to rescue than to rescue too many exceptions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;begin
  file = &amp;#39;/tmp/appsignal.log&amp;#39;
  File.open(file, &amp;#39;a&amp;#39;) { |f| f.write(&amp;quot;AppSignal started!&amp;quot;) }
rescue Errno::ENOENT =&amp;gt; e
  puts &amp;quot;File or directory #{file} doesn&amp;#39;t exist.&amp;quot;
rescue Errno::EACCES =&amp;gt; e
  puts &amp;quot;Cannot write to #{file}. No permissions.&amp;quot;
end

# Or, using the parent error class
begin
  file = &amp;#39;/tmp/appsignal.log&amp;#39;
  File.open(file, &amp;#39;a&amp;#39;)
rescue SystemCallError =&amp;gt; e
  puts &amp;quot;Error while writing to file #{file}.&amp;quot;
  puts e
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This concludes our primer on exceptions handling in Ruby. Let us know at &lt;a href=&quot;http://twitter.com/appsignal&quot;&gt;@AppSignal&lt;/a&gt; if you want to know more, or have a specific question. If you want to get a better insight in where and how often exceptions are raised in your app, &lt;a href=&quot;https://www.appsignal.com/&quot;&gt;give AppSignal a try&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Introduction to Garbage Collection (Part II)</title>
    <link rel="alternate" href="https://blog.appsignal.com/2016/07/28/ruby-magic-garbage-collection-part-2.html"/>
    <id>https://blog.appsignal.com/2016/07/28/ruby-magic-garbage-collection-part-2.html</id>
    <published>2016-07-28T00:00:00+00:00</published>
    <updated>2016-07-28T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">In the previous episode of Ruby Magic we talked about why we need Garbage Collection (GC) and how it works in general. Now we&#039;ll dive a bit deeper into how this is implemented in Ruby.</summary>
    <content type="html">&lt;p&gt;In the &lt;a href=&quot;/2016/07/12/ruby-magic-garbage-collection-part-1.html&quot;&gt;previous episode&lt;/a&gt; of Ruby Magic we talked about why we need Garbage Collection (GC) and how it works in general. In this post we&amp;#39;ll dive a bit deeper into how this is implemented in Ruby.&lt;/p&gt;
&lt;h2&gt;Different Ruby implementations&lt;/h2&gt;
&lt;p&gt;There are a number of implementations of Ruby. Three popular ones are: MRI (Matz&amp;#39;s Ruby Interpreter), Rubinius and JRuby. Different Ruby implementations use different methods of GC. In this article we&amp;#39;ll focus on MRI, which is what most Ruby developers use.&lt;/p&gt;
&lt;h3&gt;Ruby&amp;#39;s heap&lt;/h3&gt;
&lt;p&gt;A computer has two types of memory: Stack and Heap. The stack is very fast and is local to the context of a function call. This means that every variable that is declared in the stack is immediately freed once the function is done. The stack is very limited in size, so you cannot store larger objects that contain an image or file&amp;#39;s data, for example.&lt;/p&gt;
&lt;p&gt;This is a bad fit for storing Ruby objects. These objects very often stick around for longer than a method call. Also, it&amp;#39;s almost impossible to predict whether an object will be too big for the stack.&lt;/p&gt;
&lt;p&gt;Therefore Ruby uses the other type of memory: the heap. On the heap a program can claim some memory and is then responsible for cleanup once it&amp;#39;s done with that memory. Ruby uses this by claiming a single slab of memory to use to store Ruby objects. This is referred to as Ruby&amp;#39;s heap.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colSpan={2}&gt;Stack and Heap summary&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;Stack memory&lt;/th&gt;
      &lt;td&gt;
        Very fast
        &lt;br /&gt;
        Used memory is freed automatically when the function call ends
        &lt;br /&gt;
        Very limited in size
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Heap memory&lt;/th&gt;
      &lt;td&gt;
        Slightly slower than stack
        &lt;br /&gt;
        No automatic cleanup
        &lt;br /&gt;
        Size is only limited by the available memory of the computer
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;So Ruby objects are always allocated on Ruby&amp;#39;s heap. If they&amp;#39;re smaller than 40 bytes their content can be directly embedded in the object. Otherwise the object points to a separate segment of memory on Ruby&amp;#39;s heap. So your Ruby object is often stored in two completely different locations in memory. Once Ruby&amp;#39;s heap is full it will create a new heap, which is used for new objects.&lt;/p&gt;
&lt;h2&gt;Mark and Sweep&lt;/h2&gt;
&lt;p&gt;MRI uses a GC algorithm called Mark and Sweep. This operates by first doing a mark phase. In the mark phase the Garbage Collector scans all currently existing objects and sets a marked flag on every object it believes can be cleaned up.&lt;/p&gt;
&lt;p&gt;The mark phase halts the execution of your code. The reason for this is that the Garbage Collector has to understand all the relationships between objects that exist. If the program would be running during the mark process things might change in the meantime and the Garbage Collector wouldn&amp;#39;t be sure what the current state of an object is.&lt;/p&gt;
&lt;p&gt;Secondly the sweep phase starts. This runs in the background on Ruby 1.9 and up. The Garbage Collector quietly frees every object that was marked in the mark phase. The memory is only available again after the sweep.&lt;/p&gt;
&lt;p&gt;Since the mark phase halts the execution of your code, this is where problems in production can occur. The sweep phase is relatively benign.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colSpan={2}&gt;Mark and Sweep summary&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;Mark phase&lt;/th&gt;
      &lt;td&gt;
        Scans existing objects
        &lt;br /&gt;
        Sets mark flag if object can be cleaned
        &lt;br /&gt;
        Halts code execution
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Sweep phase&lt;/th&gt;
      &lt;td&gt;
        Runs in the background
        &lt;br /&gt;
        Cleans up marked objects
        &lt;br /&gt;
        Memory available again after sweep is competed
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;Major and minor GC runs&lt;/h2&gt;
&lt;p&gt;In Ruby 2.1+ the Garbage Collector does major and minor runs. It keeps track of which objects are new. If an object survives a few GC runs it&amp;#39;s marked as old. Old objects are then ignored in minor runs. This makes the minor runs much less intrusive since the Garbage Collector only has to scan objects that have just been allocated.&lt;/p&gt;
&lt;p&gt;This is useful because often a part of your memory should never be cleaned up. When you boot up Rails it loads the entire Rails framework into memory. That will stay there for the entire lifetime, so it would be a waste of resources to check all these objects every time.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colSpan={2}&gt;Major and minor runs&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;Major run&lt;/th&gt;
      &lt;td&gt;
        Runs less often
        &lt;br /&gt;
        More intensive to run
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Minor run&lt;/th&gt;
      &lt;td&gt;
        Ignores old objects
        &lt;br /&gt;
        Runs more often
        &lt;br /&gt;
        Less intrusive to run
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3&gt;For those who want to dive deeper&lt;/h3&gt;
&lt;p&gt;We learned a lot about this from Aman Gupta&amp;#39;s great &lt;a href=&quot;http://tmm1.net/&quot;&gt;blog&lt;/a&gt;. Check that out if you want a deeper dive into this subject.&lt;/p&gt;
&lt;h2&gt;Up next: Practical Garbage Collection tuning&lt;/h2&gt;
&lt;p&gt;There are a number of metrics you can measure and configuration changes you can make to tune how the Garbage Collector operates. In the next installment of this GC series we will discuss these metrics and configuration parameters.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Introduction to Garbage Collection (Part I)</title>
    <link rel="alternate" href="https://blog.appsignal.com/2016/07/12/ruby-magic-garbage-collection-part-1.html"/>
    <id>https://blog.appsignal.com/2016/07/12/ruby-magic-garbage-collection-part-1.html</id>
    <published>2016-07-12T00:00:00+00:00</published>
    <updated>2016-07-12T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Whenever you run your code, you use memory. But you never have to think about how to clear memory again afterwards. How does this work?</summary>
    <content type="html">&lt;p&gt;Whenever you run your code, you use memory. When you write in a language like Ruby, it seems like the memory available to you is infinite. You can just keep going without thinking about the fixed amount of memory the system running your code has. In this Ruby Magic episode we&amp;#39;ll explain how this works!&lt;/p&gt;
&lt;h2&gt;A bit of history&lt;/h2&gt;
&lt;p&gt;Back in the day, scripting languages such as Ruby did not exist yet. People only wrote code in languages such as C, a low level programming language. One of the things that makes these languages low level is that you have to clean up after yourself. For example, whenever you allocate memory to store a &lt;code&gt;String&lt;/code&gt;, you also have to decide when to clean it up.&lt;/p&gt;
&lt;h3&gt;Manual cleanup&lt;/h3&gt;
&lt;p&gt;This looks a little something like the following mock Ruby code. It declares a variable and uses the method &lt;code&gt;free&lt;/code&gt; –this method does not actually exist in Ruby– to clean up the memory we&amp;#39;ve used after we&amp;#39;re done with the variable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ruby 1_000_000.times do |i| variable = &amp;quot;Variable #{i}&amp;quot; puts variable free(variable) end &lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;A tedious way of programming&lt;/h3&gt;
&lt;p&gt;You might have already realized there&amp;#39;s a risk here: what if you forget to{&amp;quot; &amp;quot;} &lt;code&gt;free&lt;/code&gt; the variable? In that case the content of that variable will just stick around in memory until the process exits. If you do this often enough, you will be out of memory and your process crashes.&lt;/p&gt;
&lt;p&gt;The next example demonstrates another common issue:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ruby 1_000_000.times do |i| variable = &amp;quot;Variable #{i}&amp;quot; free(variable) puts variable end &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We declare the variable and &lt;code&gt;free&lt;/code&gt; it. But then we try to use it again, which is impossible because it doesn&amp;#39;t exist anymore. If this were C, your program would now crash with a &lt;code&gt;segfault&lt;/code&gt;. Oops!&lt;/p&gt;
&lt;h3&gt;Humans are mistake machines&lt;/h3&gt;
&lt;p&gt;Humans are notoriously bad at not making these kinds of mistakes all of the time. Hence the need for a way to automatically clean up memory. The most popular way to do this –also used in Ruby– is Garbage Collection (GC).&lt;/p&gt;
&lt;h2&gt;How Garbage Collection (GC) works&lt;/h2&gt;
&lt;p&gt;In a language that uses GC, you can create objects without manually cleaning them up. Whenever you create an object, it&amp;#39;s registered with the Garbage Collector. GC tries to keep track of all references you make to this object. When it determines you&amp;#39;re not using the object any more, it is marked for cleanup. Every once in a while the Garbage Collector pauses your program and cleans up all the marked objects.&lt;/p&gt;
&lt;h3&gt;Looking at some examples&lt;/h3&gt;
&lt;p&gt;In the simple loop we used earlier the GC&amp;#39;s job is fairly easy. With every iteration of the loop, the variable isn&amp;#39;t used anywhere anymore. The variable can immediately be marked for cleanup.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ruby 1_000_000.times do |i| variable = &amp;quot;Variable #{i}&amp;quot; puts variable end &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In the next example we pass the variable into the &lt;code&gt;puts_later&lt;/code&gt;{&amp;quot; &amp;quot;} method which waits for 30 seconds and then &lt;code&gt;puts&lt;/code&gt; the variable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ruby def puts_later(variable) Thread.new do sleep 30 puts variable end end 1_000_000.times do |i| variable = &amp;quot;Variable #{i}&amp;quot; puts_later variable end &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The Garbage Collector&amp;#39;s job is already pretty complicated in this relatively simple example. It has to understand that we reference the variable in the{&amp;quot; &amp;quot;} &lt;code&gt;puts_later&lt;/code&gt; method. Because the method starts a thread, the Garbage Collector has to keep track of the thread and wait for it to finish. Only then can the variable can be marked for cleanup.&lt;/p&gt;
&lt;h3&gt;When it gets complicated&lt;/h3&gt;
&lt;p&gt;Without getting into complex examples, trust me when I say the Garbage Collector&amp;#39;s job is really hard. This also explains why GC can cause overhead and problems in your production environment. It needs to have a very detailed understanding of what&amp;#39;s happening in your program to properly clear memory, which takes quite a few CPU cycles to get right. But hey, it beats cleaning up after yourself!&lt;/p&gt;
&lt;h3&gt;There&amp;#39;s more to Garbage Collection&lt;/h3&gt;
&lt;p&gt;This was only our introduction to Garbage Collection. In a future article we&amp;#39;ll look at how exactly this works in Ruby, and how you can measure and tune GC to improve the performance of your application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; The next episode is available &lt;a href=&quot;/2016/07/28/ruby-magic-garbage-collection-part-2.html&quot;&gt;here&lt;/a&gt; .&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Mastering Concurrency</title>
    <link rel="alternate" href="https://blog.appsignal.com/2016/03/17/ruby-magic-mastering-concurrency.html"/>
    <id>https://blog.appsignal.com/2016/03/17/ruby-magic-mastering-concurrency.html</id>
    <published>2016-03-17T00:00:00+00:00</published>
    <updated>2016-03-17T00:00:00+00:00</updated>
    <author>Roy Tomeij</author>
    <summary type="html">Multiple people will use your app at the same time, and you want to deliver your app as fast as possible. So you&#039;ll need some way to handle concurrency. Fear not! Most webservers already do this by default. But when you need to scale, you want to use concurrency in the most efficient way possible.</summary>
    <content type="html">&lt;h2&gt;Mastering concurrency&lt;/h2&gt;
&lt;p&gt;
  Multiple people will use your app at the same time, and you want to deliver
  your app as fast as possible. So you&#039;ll need some way to handle concurrency.
  Fear not! Most web servers already do this by default. But when you need to
  scale, you want to use concurrency in the most efficient way possible.
&lt;/p&gt;
&lt;h2&gt;Different types of concurrency&lt;/h2&gt;
&lt;p&gt;
  There are multiple ways to handle concurrency: multi-process, multi-threading
  and event-driven. Each of these have their uses, pros and cons. In this
  article, you&#039;ll learn how they differ and when to use which.
&lt;/p&gt;
&lt;header className=&quot;ruby_magic&quot; id=&quot;multi-process&quot;&gt;
  &lt;h2&gt;Multi-process (Unicorn)&lt;/h2&gt;
  &lt;p&gt;
    This is the easiest way to handle concurrency. A master process{&quot; &quot;}
    &lt;a href=&quot;https://en.wikipedia.org/wiki/Fork_(system_call)&quot;&gt;forks&lt;/a&gt; itself
    to multiple worker processes. The worker process handles the actual
    requests, while the master manages the workers.
  &lt;/p&gt;
&lt;/header&gt;

&lt;p&gt;
  Each worker process has the full codebase in memory. This makes this method
  pretty memory-intensive, and makes it hard to scale to larger infrastructures.
&lt;/p&gt;
&lt;table className=&quot;collapse&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colSpan={2}&gt;Multi-process summary&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;Use&amp;nbsp;case&lt;/th&gt;
      &lt;td&gt;
        One non-ruby example you probably know is the{&quot; &quot;}
        &lt;a href=&quot;https://www.google.com/googlebooks/chrome/small_04.html&quot;&gt;
          Chrome browser
        &lt;/a&gt;
        . It uses multi-process concurrency to give each tab their own process.
        It allows a single tab to crash without taking the full application
        down. In their case, it also helps to isolate exploits to a single tab.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th className=&quot;green&quot;&gt;Pros&lt;/th&gt;
      &lt;td&gt;
        Most simple to implement.
        &lt;br /&gt;
        Ignores difficulties with thread safety.
        &lt;br /&gt;
        Each worker can crash without damaging the rest of the system.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th className=&quot;red&quot;&gt;Cons&lt;/th&gt;
      &lt;td&gt;
        Each process loads the full codebase in memory. This makes it
        memory-intensive.
        &lt;br /&gt;
        Hence, it does not scale to large amounts of concurrent connections.
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;header className=&quot;ruby_magic&quot; id=&quot;multi-threading&quot;&gt;
  &lt;h2&gt;Multi-threading (Puma)&lt;/h2&gt;
  &lt;p&gt;
    This threading model allows one process to handle multiple requests at the
    same time. It does so by running multiple threads within a single process.
  &lt;/p&gt;
&lt;/header&gt;
&lt;p&gt;
  As opposed to the multi-process approach, all threads run within the same
  process. This means they share data such as global variables. Therefore, only
  small chunks of extra memory are used per thread.
&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2016-02/threaded.svg&quot; alt=&quot;Threaded&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Global Interpreter Lock&lt;/h3&gt;
&lt;p&gt;
  This brings us to the global interpreter lock (GIL) in MRI. The GIL is a lock
  around the execution of all Ruby code. Even though our threads appear to run
  in parallel, only one thread is active at a time.
&lt;/p&gt;
&lt;p&gt;
  IO operates outside of the GIL. When you execute a database query waiting for
  the result to come back, it won&#039;t lock. Another thread will have a chance to
  do some work in the meantime. If you do a lot of math and operations on hashes
  or arrays in threads, you will only utilize a single core if you use MRI. In
  most cases you still need multiple processes to fully utilize your machine. Or
  you could use Rubinius or jRuby, which don&#039;t have a GIL.
&lt;/p&gt;
&lt;h3&gt;Thread safety&lt;/h3&gt;
&lt;p&gt;
  If you use multiple threads you have to be careful to write all code that
  manipulates shared data in a thread safe way. You can do this for example by
  using a &lt;a href=&quot;http://ruby-doc.org/core-2.2.0/Mutex.html&quot;&gt;Mutex&lt;/a&gt; to lock
  shared data structures before you manipulate them. This will ensure that other
  threads are not basing their work on stale data while you&#039;re changing the
  data.
&lt;/p&gt;
&lt;table className=&quot;collapse&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colSpan={2}&gt;Multi-threaded summary&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;Use&amp;nbsp;case&lt;/th&gt;
      &lt;td&gt;
        This is the &quot;middle of the road&quot; option. Used for a lot of standard web
        applications which should handle loads of short requests (such as a busy
        web application).
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th className=&quot;green&quot;&gt;Pros&lt;/th&gt;
      &lt;td&gt;Uses less memory than multi-process.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th className=&quot;red&quot;&gt;Cons&lt;/th&gt;
      &lt;td&gt;
        You have to make sure your code is thread safe.
        &lt;br /&gt;
        If a thread causes a a crash, it can potentially take down your process.
        &lt;br /&gt;
        The GIL locks all operations except I/O.
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;header className=&quot;ruby_magic&quot; id=&quot;event-driven&quot;&gt;
  &lt;h2&gt;Event-loop (Thin)&lt;/h2&gt;
  &lt;p&gt;
    Event-loops are used when you need to do a lot of concurrent I/O operations.
    The model itself doesn&#039;t force multiple requests to be executed at the same
    time, but it is an efficient way to handle a lot of concurrent users.
  &lt;/p&gt;
&lt;/header&gt;
&lt;p&gt;
  Below you&#039;ll see a very simple event loop written in Ruby. The loop will take
  the event from the &lt;code&gt;event_queue&lt;/code&gt; and handle it. If there is no
  event, it will sleep and repeat to see if there are new events in the queue.
&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;loop do
  if event_queue.any?
    handle_event(event_queue.pop)
  else
    sleep 0.1
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Illustrated version&lt;/h3&gt;
&lt;p&gt;
  In this illustration, we&#039;re taking it a step further. The event loop now does
  a beautiful dance with the OS, queue and some memory.
&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2016-02/event-loops.svg&quot; alt=&quot;Event loops&quot;/&gt;&lt;/p&gt;
&lt;h3&gt;Step by step&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;The OS keeps track of network and disk availability.&lt;/li&gt;
  &lt;li&gt;When the OS sees the I/O is ready, it sends an event to the queue.&lt;/li&gt;
  &lt;li&gt;
    The queue is a list of events from which the event loop takes the top one.
  &lt;/li&gt;
  &lt;li&gt;The event loop handles the event.&lt;/li&gt;
  &lt;li&gt;It uses some memory to store meta data about the connections.&lt;/li&gt;
  &lt;li&gt;
    It can send a new event directly into the event queue again. For example, a
    message to shut down the queue based on the contents of an event.
  &lt;/li&gt;
  &lt;li&gt;
    If it wants to do an I/O operation, it tells the OS that it&#039;s interested in
    a specific I/O operation. The OS keeps track of the network and disk (see
    [1]) and adds an event again when I/O is ready.
  &lt;/li&gt;
&lt;/ol&gt;
&lt;table className=&quot;collapse&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colSpan={2}&gt;Event-loop summary&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th className=&quot;green&quot;&gt;Use&amp;nbsp;case&lt;/th&gt;
      &lt;td&gt;
        When using a lot of concurrent connections to your users. Think of
        services like Slack. Chrome notifications.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Pros&lt;/th&gt;
      &lt;td&gt;
        Almost no memory overhead per connection.
        &lt;br /&gt;
        Scales to a huge number of parallel connections.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th className=&quot;red&quot;&gt;Cons&lt;/th&gt;
      &lt;td&gt;
        It&#039;s a difficult mental model to understand.
        &lt;br /&gt;
        Batch sizes must be small and predictable to avoid queues building up.
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;header className=&quot;ruby_magic&quot;&gt;
  &lt;h2&gt;Which one should you use?&lt;/h2&gt;
  &lt;p&gt;
    We hope this article has given you a better understanding of the different
    concurrency models. It&#039;s some of the more difficult subject matter to grasp
    as a developer, but understanding it will give you the tools to experiment
    and use the right setup for your app.
  &lt;/p&gt;
&lt;/header&gt;
&lt;h3&gt;In summary&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;
    For most apps threading makes sense, Ruby/Rails ecosystem seems to (slowly)
    be moving this way.
  &lt;/li&gt;
  &lt;li&gt;
    If you run highly concurrent apps with long-running streams, event-loop
    allows you to scale.
  &lt;/li&gt;
  &lt;li&gt;
    If you don&#039;t have a high traffic site, or you expect your workers to break
    go for good old multi-process.
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  And, it is possible to run an event loop, inside a thread, inside a
  multi-process setup. So yes, you can have your stroopwafel and eat it too!
&lt;/p&gt;

&lt;p&gt;If you want to read more more about these concurrency models check out our detailed articles on &lt;a href=&quot;/2017/03/07/ruby-magic-concurrency-processes.html&quot;&gt;multi-process&lt;/a&gt;, &lt;a href=&quot;/2017/04/18/ruby-magic-concurrency-threads.html&quot;&gt;multi-threading&lt;/a&gt; and &lt;a href=&quot;/2017/06/06/ruby-magic-concurrency-event-loop.html&quot;&gt;event loops&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  </feed>
