<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Unathi Chonco</title>
    <description>The latest articles on DEV Community by Unathi Chonco (@choncou).</description>
    <link>https://dev.to/choncou</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F376599%2F38164151-58cb-460f-9879-4fef5468eda8.png</url>
      <title>DEV Community: Unathi Chonco</title>
      <link>https://dev.to/choncou</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/choncou"/>
    <language>en</language>
    <item>
      <title>Ruby Method Lookup Demystified: Inheritance, Mixins, and Super</title>
      <dc:creator>Unathi Chonco</dc:creator>
      <pubDate>Mon, 15 May 2023 08:37:10 +0000</pubDate>
      <link>https://dev.to/choncou/ruby-method-lookup-demystified-inheritance-mixins-and-super-4k68</link>
      <guid>https://dev.to/choncou/ruby-method-lookup-demystified-inheritance-mixins-and-super-4k68</guid>
      <description>&lt;p&gt;If youve worked on Ruby projects for a while, you would have encountered times when you were digging to find out how and why a certain method behaved the way it did, because it didnt seem to use the code in the method definition youre looking at. And what happens when there are multiple methods with the same name?&lt;/p&gt;

&lt;p&gt;This can seem confusing, but Ruby does have a specific way or path that is used to determine what methods to actually call. This process is called method lookup. In this guide we will delve into the details of method lookup, covering inheritance, mixins using &lt;code&gt;include&lt;/code&gt;, &lt;code&gt;prepend&lt;/code&gt;, and &lt;code&gt;extend&lt;/code&gt;, as well as the &lt;code&gt;super&lt;/code&gt; method. With clear examples and explanations, this article will help you build a strong foundation and understanding that enables you to write more maintainable code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inheritance and Method Lookup in Ruby
&lt;/h2&gt;

&lt;p&gt;In Ruby, inheritance allows a class to inherit the methods and attributes of another class. The class that inherits is called a subclass, and the class that is inherited from is called a superclass. Method lookup in Ruby begins by searching the instance methods of the object's class, and if not found, it moves up the class hierarchy until it reaches the superclass. Let's consider an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="s2"&gt;"I'm an animal!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Animal&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt; &lt;span class="c1"&gt;# Output: I'm an animal!&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In the example above, the &lt;code&gt;Dog&lt;/code&gt; class inherits from the &lt;code&gt;Animal&lt;/code&gt; class. When we call the &lt;code&gt;speak&lt;/code&gt; method on a &lt;code&gt;Dog&lt;/code&gt; object, Ruby first looks for the method in the &lt;code&gt;Dog&lt;/code&gt; class. Since it doesn't find it there, it searches the superclass &lt;code&gt;Animal&lt;/code&gt;, finds the &lt;code&gt;speak&lt;/code&gt; method, and executes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixins and Method Lookup
&lt;/h2&gt;

&lt;p&gt;Ruby uses modules as a way to implement multiple inheritance through mixins. Mixins are used to share methods among different classes, making your code more modular and easier to maintain. There are three primary ways to include a module in your class: &lt;code&gt;include&lt;/code&gt;, &lt;code&gt;prepend&lt;/code&gt;, and &lt;code&gt;extend&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Include
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;include&lt;/code&gt; adds the module's methods as instance methods in the class. When a method is called on an object, Ruby first searches the object's class, then the included module, and finally the superclass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Speak&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="s2"&gt;"I'm a speaking mixin!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Animal&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Speak&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt; &lt;span class="c1"&gt;# Output: I'm a speaking mixin!&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this example, we've included the &lt;code&gt;Speak&lt;/code&gt; module in the &lt;code&gt;Dog&lt;/code&gt; class. When we call the &lt;code&gt;speak&lt;/code&gt; method on a &lt;code&gt;Dog&lt;/code&gt; object, Ruby first looks for the method in the &lt;code&gt;Dog&lt;/code&gt; class, then in the included &lt;code&gt;Speak&lt;/code&gt; module, and finally in the superclass &lt;code&gt;Animal&lt;/code&gt;. Since it finds the &lt;code&gt;speak&lt;/code&gt; method in the &lt;code&gt;Speak&lt;/code&gt; module, it executes that method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepend
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;prepend&lt;/code&gt; is similar to &lt;code&gt;include&lt;/code&gt;, but it inserts the module's methods &lt;em&gt;before&lt;/em&gt; the class in the method lookup chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Animal&lt;/span&gt;
  &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;Speak&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt; &lt;span class="c1"&gt;# Output: I'm a speaking mixin!&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this case, when we call the &lt;code&gt;speak&lt;/code&gt; method on a &lt;code&gt;Dog&lt;/code&gt; object, Ruby first looks in the prepended &lt;code&gt;Speak&lt;/code&gt; module, then in the &lt;code&gt;Dog&lt;/code&gt; class, and finally in the superclass &lt;code&gt;Animal&lt;/code&gt;. Since it finds the &lt;code&gt;speak&lt;/code&gt; method in the &lt;code&gt;Speak&lt;/code&gt; module, it executes that method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extend
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;extend&lt;/code&gt; adds the module's methods as class methods, rather than instance methods. This means that the methods can be called directly on the class itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Animal&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Speak&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt; &lt;span class="c1"&gt;# Output: I'm a speaking mixin&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this example, by using &lt;code&gt;extend&lt;/code&gt; instead of &lt;code&gt;include&lt;/code&gt; or &lt;code&gt;prepend&lt;/code&gt;, we've added the &lt;code&gt;speak&lt;/code&gt; method as a class method of &lt;code&gt;Dog&lt;/code&gt;. This allows us to call the &lt;code&gt;speak&lt;/code&gt; method directly on the &lt;code&gt;Dog&lt;/code&gt; class.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;super&lt;/code&gt; Method
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;super&lt;/code&gt; method in Ruby allows a subclass to call a method from its superclass. This can be useful when you want to extend the behaviour of a superclass method in a subclass without completely overriding it. Let's see an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="s2"&gt;"I'm an animal!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Animal&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and I'm a dog!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt; &lt;span class="c1"&gt;# Output: I'm an animal! and I'm a dog!&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;Dog&lt;/code&gt; class, we've defined a &lt;code&gt;speak&lt;/code&gt; method that calls the &lt;code&gt;speak&lt;/code&gt; method from its superclass &lt;code&gt;Animal&lt;/code&gt; using the &lt;code&gt;super&lt;/code&gt; keyword. This allows us to extend the behaviour of the &lt;code&gt;Animal#speak&lt;/code&gt; method in the &lt;code&gt;Dog&lt;/code&gt; class.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;super&lt;/code&gt; Method with Mixins
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;super&lt;/code&gt; methods can still be used when mixins are involved, and for the purposes of this post is a great showcase of the method lookup flow.&lt;/p&gt;

&lt;p&gt;In this example, we'll demonstrate the use of &lt;code&gt;super&lt;/code&gt; in an object that uses both &lt;code&gt;prepend&lt;/code&gt; and &lt;code&gt;include&lt;/code&gt; to mixin the same method. We'll have three modules with a &lt;code&gt;greet&lt;/code&gt; method, and a &lt;code&gt;Person&lt;/code&gt; class that will include and prepend those modules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;GreetInEnglish&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;
    &lt;span class="s2"&gt;"Hello! &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;GreetInSpanish&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;
    &lt;span class="s2"&gt;"Hola! &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;GreetInFrench&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;
    &lt;span class="s2"&gt;"Bonjour! &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LivingBeing&lt;/span&gt;
  &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;GreetInEnglish&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;
    &lt;span class="s2"&gt;"I'm a living being."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;LivingBeing&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;GreetInFrench&lt;/span&gt;
  &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;GreetInSpanish&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;
    &lt;span class="s2"&gt;"I'm a person. &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this example, we've added a &lt;code&gt;LivingBeing&lt;/code&gt; class that includes &lt;code&gt;GreetInEnglish&lt;/code&gt; and has its own &lt;code&gt;greet&lt;/code&gt; method. The &lt;code&gt;Person&lt;/code&gt; class now inherits from &lt;code&gt;LivingBeing&lt;/code&gt;, includes &lt;code&gt;GreetInFrench&lt;/code&gt;, and prepends &lt;code&gt;GreetInSpanish&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When we call &lt;code&gt;person.greet&lt;/code&gt;, the method lookup order is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GreetInSpanish#greet&lt;/code&gt; (prepended in &lt;code&gt;Person&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Person#greet&lt;/code&gt; (defined in the class)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GreetInFrench#greet&lt;/code&gt; (included in &lt;code&gt;Person&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GreetInEnglish#greet&lt;/code&gt; (prepended in &lt;code&gt;LivingBeing&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;LivingBeing#greet&lt;/code&gt; (inherited from the superclass)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The output will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hola! I'm a person. Bonjour! Hello! I'm a living being.

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

&lt;/div&gt;



&lt;p&gt;Now, the method lookup chain correctly includes all mixins and the inherited method from &lt;code&gt;LivingBeing&lt;/code&gt;. The &lt;code&gt;super&lt;/code&gt; keyword is used in each of the mixin methods and the &lt;code&gt;Person#greet&lt;/code&gt; method to call the next method in the lookup chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Understanding Ruby's method lookup process helps you write more maintainable code and tackle complex Ruby projects and libraries. Use inheritance, mixins, and the &lt;code&gt;super&lt;/code&gt; method to override and extend capabilities in Ruby when needed. Be mindful of code complexity and avoid hiding implementation details in too many places. Happy coding!&lt;/p&gt;

&lt;p&gt;🧑🏾🚀🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Ruby Bang (!) Methods</title>
      <dc:creator>Unathi Chonco</dc:creator>
      <pubDate>Thu, 04 May 2023 12:54:17 +0000</pubDate>
      <link>https://dev.to/choncou/ruby-bang-methods-560h</link>
      <guid>https://dev.to/choncou/ruby-bang-methods-560h</guid>
      <description>&lt;p&gt;Ruby is a powerful and elegant programming language, known for its readability and expressiveness. &lt;a href="https://rubyonrails.org/doctrine#optimize-for-programmer-happiness"&gt;Optimised for developer happiness&lt;/a&gt;. One of the more unique aspects of Ruby is its "bang"(!) methods. As a newcomer to Ruby, you might find yourself wondering about the purpose of these methods and how to use them. In this article, we will explore Ruby's bang methods, the naming convention, and their use in a Ruby on Rails project.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What are Bang Methods?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In Ruby, bang methods are simply methods that have an exclamation mark (!) at the end of their names. The bang is a naming convention to signify that the method has some potentially surprising or dangerous behaviour compared to its non-bang counterpart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutating bang methods
&lt;/h2&gt;

&lt;p&gt;Generally, bang methods mutate/modify the object they are called upon, while their non-bang counterparts return a new object with the desired changes.&lt;/p&gt;

&lt;p&gt;Here are a few examples of bang and non-bang method pairs in Ruby:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;String#upcase&lt;/code&gt; and &lt;code&gt;String#upcase!&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Array#sort&lt;/code&gt; and &lt;code&gt;Array#sort!&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Array#uniq&lt;/code&gt; and &lt;code&gt;Array#uniq!&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's take a closer look at these examples to understand the differences between bang and non-bang methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example 1: String#upcase and String#upcase!&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;String#upcase&lt;/code&gt; returns a new string with all characters in uppercase, leaving the original string unchanged. In contrast, &lt;code&gt;String#upcase!&lt;/code&gt; modifies the original string in-place, converting all characters to uppercase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hello, world!"&lt;/span&gt;

&lt;span class="n"&gt;uppercase_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upcase&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;uppercase_text&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "HELLO, WORLD!"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "hello, world!"&lt;/span&gt;

&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upcase!&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "HELLO, WORLD!"&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Example 2: Array#sort and Array#sort!&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Array#sort&lt;/code&gt; returns a new array with its elements sorted, while &lt;code&gt;Array#sort!&lt;/code&gt; sorts the original array in-place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;sorted_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;sorted_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [1, 2, 3, 4, 5]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [5, 3, 1, 4, 2]&lt;/span&gt;

&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort!&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [1, 2, 3, 4, 5]&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Example 3: Array#uniq and Array#uniq!&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Array#uniq&lt;/code&gt; returns a new array with duplicate elements removed, while &lt;code&gt;Array#uniq!&lt;/code&gt; removes duplicate elements from the original array in-place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;repeated_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;unique_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repeated_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;unique_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [1, 2, 3]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;repeated_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [1, 2, 2, 3, 3, 3]&lt;/span&gt;

&lt;span class="n"&gt;repeated_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq!&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;repeated_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [1, 2, 3]&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Error-raising bang methods
&lt;/h2&gt;

&lt;p&gt;In a Ruby application, bang methods are also prevalent for denoting error-raising methods. You can find common use cases in a library such as ActiveRecord, the built-in Object-Relational Mapping (ORM) framework for Ruby on Rails that handles database operations. Some of the most common ActiveRecord bang methods include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ActiveRecord::Base#save&lt;/code&gt; and &lt;code&gt;ActiveRecord::Base#save!&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ActiveRecord::Base#create&lt;/code&gt; and &lt;code&gt;ActiveRecord::Base#create!&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ActiveRecord::Base#update&lt;/code&gt; and &lt;code&gt;ActiveRecord::Base#update!&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ActiveRecord::Base#destroy&lt;/code&gt; and &lt;code&gt;ActiveRecord::Base#destroy!&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's examine these ActiveRecord examples to understand their usage and differences in a Ruby on Rails application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: ActiveRecord::Base#save and ActiveRecord::Base#save!
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;save&lt;/code&gt; and &lt;code&gt;save!&lt;/code&gt; are instance methods used to persist an ActiveRecord object to the database. The non-bang method, &lt;code&gt;save&lt;/code&gt;, returns a boolean value indicating whether the record was saved successfully or not. If there are any validation errors, it returns false.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;code&gt;save!&lt;/code&gt; raises an &lt;code&gt;ActiveRecord::RecordInvalid&lt;/code&gt; exception if the record is invalid, halting the execution flow. This method is useful when you want to ensure the record is valid and saved; otherwise, an exception should be raised.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Using save&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User saved!"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User not saved. Errors: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Using save!&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User saved!"&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RecordInvalid&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User not saved. Errors: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 2: ActiveRecord::Base#create and ActiveRecord::Base#create!
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;create&lt;/code&gt; and &lt;code&gt;create!&lt;/code&gt; are class methods used to create and save a new ActiveRecord object to the database in a single step. Similar to &lt;code&gt;save&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt; returns the newly created object, whether it's valid or not. If the object is invalid, it won't be persisted to the database.&lt;/p&gt;

&lt;p&gt;In contrast, &lt;code&gt;create!&lt;/code&gt; raises an &lt;code&gt;ActiveRecord::RecordInvalid&lt;/code&gt; exception if the object is invalid, making it suitable for situations where you want to ensure the record is valid and created; otherwise, an exception should be raised.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Using create&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;persisted?&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User created!"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User not created. Errors: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Using create!&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User created!"&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RecordInvalid&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User not created. Errors: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 3: ActiveRecord::Base#update and ActiveRecord::Base#update!
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;update&lt;/code&gt; and &lt;code&gt;update!&lt;/code&gt; are instance methods used to update the attributes of an ActiveRecord object and save it to the database. &lt;code&gt;update&lt;/code&gt; returns a boolean value, indicating whether the record was updated successfully or not. If there are any validation errors, it returns false.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;update!&lt;/code&gt;, like &lt;code&gt;save!&lt;/code&gt; and &lt;code&gt;create!&lt;/code&gt;, raises an &lt;code&gt;ActiveRecord::RecordInvalid&lt;/code&gt; exception if the record is invalid, making it suitable for situations where you want to ensure the record is valid and updated; otherwise, an exception should be raised.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 4: ActiveRecord::Base#destroy and ActiveRecord::Base#destroy!
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;destroy&lt;/code&gt; and &lt;code&gt;destroy!&lt;/code&gt; are instance methods used to delete an ActiveRecord object from the database. The non-bang method, &lt;code&gt;destroy&lt;/code&gt;, returns the destroyed object if the deletion is successful. If the object has any &lt;code&gt;before_destroy&lt;/code&gt; callbacks that halt the destruction process, it returns false.&lt;/p&gt;

&lt;p&gt;In contrast, &lt;code&gt;destroy!&lt;/code&gt; raises an &lt;code&gt;ActiveRecord::RecordNotDestroyed&lt;/code&gt; exception if the object cannot be destroyed due to any &lt;code&gt;before_destroy&lt;/code&gt; callbacks. This method is useful when you want to ensure the record is deleted; otherwise, an exception should be raised.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;before_destroy&lt;/span&gt; &lt;span class="ss"&gt;:check_admin&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_admin&lt;/span&gt;
    &lt;span class="kp"&gt;throw&lt;/span&gt; &lt;span class="ss"&gt;:abort&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"admin@example.com"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"admin@example.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Using destroy&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User destroyed!"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User not destroyed."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Using destroy!&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy!&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User destroyed!"&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RecordNotDestroyed&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"User not destroyed."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;User&lt;/code&gt; model has a &lt;code&gt;before_destroy&lt;/code&gt; callback that prevents the deletion of a user with the email "&lt;a href="//mailto:admin@example.com"&gt;admin@example.com&lt;/a&gt;". When using &lt;code&gt;destroy&lt;/code&gt;, the method returns false if the user cannot be deleted. However, when using &lt;code&gt;destroy!&lt;/code&gt;, an &lt;code&gt;ActiveRecord::RecordNotDestroyed&lt;/code&gt; exception is raised if the user cannot be deleted.&lt;/p&gt;

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

&lt;p&gt;Understanding the differences between bang and non-bang methods in Ruby can empower you to write more efficient and maintainable code. Bang methods are more aggressive, often modifying objects in place or raising exceptions when operations fail. Non-bang methods are more lenient, returning modified copies of objects or allowing for flexible error handling. Knowing when to use each type of method can be useful in creating robust and reliable applications.&lt;/p&gt;

&lt;p&gt;So, the next time you come across a bang method in Ruby, don't be afraid to use it but also be aware of its potential side effects. Happy coding!&lt;/p&gt;

&lt;p&gt;🧑🏾🚀🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Next Level Ruby on Rails Application Monitoring with AppSignal</title>
      <dc:creator>Unathi Chonco</dc:creator>
      <pubDate>Wed, 12 Jan 2022 13:09:58 +0000</pubDate>
      <link>https://dev.to/appsignal/next-level-ruby-on-rails-application-monitoring-with-appsignal-1395</link>
      <guid>https://dev.to/appsignal/next-level-ruby-on-rails-application-monitoring-with-appsignal-1395</guid>
      <description>&lt;p&gt;In the &lt;a href="https://blog.appsignal.com/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html" rel="noopener noreferrer"&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'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="https://www.appsignal.com/" rel="noopener noreferrer"&gt;www.appsignal.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&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="https://blog.appsignal.com/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html" rel="noopener noreferrer"&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'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's update the &lt;code&gt;PostsController#index&lt;/code&gt; to use the new method if a specific query parameter is present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/posts_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;starts_with&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:starts_with&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;starts_with&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
               &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where_title_starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;starts_with&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;else&lt;/span&gt;
               &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
             &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is such a core part of your application that you'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="https://docs.appsignal.com/ruby/instrumentation/instrumentation.html" rel="noopener noreferrer"&gt;instrumentation blocks&lt;/a&gt; to wrap the code blocks. Update the method like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/post.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where_title_starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Appsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Post.where_title_starts_with'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Fetch posts that start with letter"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track_post_title_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'*, pg_sleep(0.01)'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"title ILIKE :letter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;letter: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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="https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html" rel="noopener noreferrer"&gt;method instrumentation&lt;/a&gt; to instrument the entire method more accurately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/analytics.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'appsignal/integrations/object'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Analytics&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track_post_title_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# Some heavy processing&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;appsignal_instrument_class_method&lt;/span&gt; &lt;span class="ss"&gt;:track_post_title_search&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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'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;br&gt;
&lt;a href="http://localhost:3000/posts?starts_with=f" rel="noopener noreferrer"&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;br&gt;
&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 ('Performance' -&amp;gt; 'Issue list') and view the &lt;code&gt;PostsController#index&lt;/code&gt; action, lower down on the page, you should be able to see an 'Event Timeline'. This gives you a breakdown of how much time is spent running specific code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2022-01%2Fevent-timeline.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2022-01%2Fevent-timeline.png" alt="Performance issue event timeline"&gt;&lt;/a&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;p&gt;AppSignal's instrumentation helpers give you a more detailed breakdown of the application code, so it's easier to gain insights into particular pieces of code that you think could impact an application's performance. You can learn more about this in &lt;a href="https://docs.appsignal.com/ruby/instrumentation/instrumentation.html" rel="noopener noreferrer"&gt;AppSignal'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't behave as expected and where things go wrong. We've already seen how AppSignal reports exceptions that have not been handled by our code. But there'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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;
    &lt;span class="no"&gt;CreateRandomPostsJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in the overview dashboard, the application's error rate will drop significantly.&lt;/p&gt;

&lt;p&gt;Currently, when a user tries to view a post that doesn't exist, the application crashes — e.g, &lt;a href="http://localhost:3000/posts/doesnotexist" rel="noopener noreferrer"&gt;http://localhost:3000/posts/doesnotexist&lt;/a&gt;.&lt;br&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/posts_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="nf"&gt;.&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="nf"&gt;private&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_post&lt;/span&gt;
      &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RecordNotFound&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;error: &lt;/span&gt;&lt;span class="s2"&gt;"Oops. That post isn't here"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :not_found&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="nf"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we are manually handling the exception, it won'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'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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_post&lt;/span&gt;
  &lt;span class="no"&gt;Appsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tag_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="s1"&gt;'user-from-params'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;post_id: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RecordNotFound&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
  &lt;span class="no"&gt;Appsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;error: &lt;/span&gt;&lt;span class="s2"&gt;"Oops. That post isn't here"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;status: :not_found&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now visit &lt;a href="http://localhost:3000/posts/doesnotexist" rel="noopener noreferrer"&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.&lt;br&gt;
On the AppSignal dashboard, in &lt;em&gt;'Errors -&amp;gt; Issue list'&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2022-01%2Ftags.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2022-01%2Ftags.png" alt="Error tags"&gt;&lt;/a&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="http://localhost:3000/posts/posts/1" rel="noopener noreferrer"&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 ('Performance' -&amp;gt; 'Issue list' -&amp;gt; 'PostsController#show'). You can &lt;a href="https://docs.appsignal.com/guides/custom-data/tagging-request.html" rel="noopener noreferrer"&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="https://blog.appsignal.com/2021/06/23/adding-kubernetes-metadata-to-your-appsignal-errors.html" rel="noopener noreferrer"&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're calling our analytics service and with what data, using a counter and tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#app/services/analytics.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'appsignal/integrations/object'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Analytics&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track_post_title_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="no"&gt;Appsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment_counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"track_post_search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;letter: &lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="c1"&gt;# Some heavy processing&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;appsignal_instrument_class_method&lt;/span&gt; &lt;span class="ss"&gt;:track_post_title_search&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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's behavior, and we know it keeps growing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#app/controllers/posts_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="nf"&gt;.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nf"&gt;.&lt;/span&gt;
    &lt;span class="no"&gt;Appsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_gauge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"posts_index"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;starts_with: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:starts_with&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fake traffic script still running on the application will generate some data, but to add more variety, let's also search for posts starting with &lt;a href="http://localhost:3000/posts?starts_with=f" rel="noopener noreferrer"&gt;f&lt;/a&gt;, &lt;a href="http://localhost:3000/posts?starts_with=l" rel="noopener noreferrer"&gt;l&lt;/a&gt;, and &lt;a href="http://localhost:3000/posts?starts_with=v" rel="noopener noreferrer"&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 'Dashboard' section, click on 'Add dashboard' and import a dashboard with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Post Search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sample dashboard about posts search activity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"visuals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Analytics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"line_label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"%name% %letter%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LINE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"draw_null_as_zero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"track_post_search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"COUNTER"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"letter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"timeseries"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"line_label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"%name% %starts_with%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LINE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"draw_null_as_zero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"posts_index"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GAUGE"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"starts_with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"timeseries"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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'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't be covering here. One that's worth a quick mention is &lt;a href="https://docs.appsignal.com/ruby/instrumentation/breadcrumbs.html" rel="noopener noreferrer"&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'll have even more specific and ordered information about what led up to an error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.appsignal.com/ruby/instrumentation/" rel="noopener noreferrer"&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="https://blog.appsignal.com/2021/12/01/ruby-on-rails-application-monitoring-with-appsignal.html" rel="noopener noreferrer"&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'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'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's time to let your code run free in the wild, as long as you keep an eye on how it's doing. Happy coding!&lt;/p&gt;

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

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Ruby on Rails Application Monitoring with AppSignal</title>
      <dc:creator>Unathi Chonco</dc:creator>
      <pubDate>Tue, 14 Dec 2021 14:50:36 +0000</pubDate>
      <link>https://dev.to/appsignal/ruby-on-rails-application-monitoring-with-appsignal-1jo6</link>
      <guid>https://dev.to/appsignal/ruby-on-rails-application-monitoring-with-appsignal-1jo6</guid>
      <description>&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'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're developers and love maintainable solutions (right?), we also don'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="https://www.appsignal.com/"&gt;www.appsignal.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/products/docker-desktop"&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'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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; appsignal-setup/start-docker &lt;span class="nt"&gt;--single-branch&lt;/span&gt; https://github.com/choncou/sample_rails_app appsignal-setup
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;appsignal-setup
&lt;span class="nv"&gt;$ &lt;/span&gt;yarn start:compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="http://localhost:3000/"&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'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're using &lt;a href="https://github.com/mperham/sidekiq"&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's it. We've just released a product, and everything works perfectly...&lt;/p&gt;

&lt;p&gt;Or at least that's what we think until we start seeing user reports about the application's performance and users running into errors.&lt;/p&gt;

&lt;p&gt;So how do we figure out what's going on? When we're working in a development environment, it'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's begin our monitoring journey by adding AppSignal to our application.&lt;/p&gt;

&lt;p&gt;You'll need to log into your account at &lt;a href="https://www.appsignal.com/"&gt;www.appsignal.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're a new AppSignal user, you will see the page below to add an application (whereas existing users need to click on 'Add app').&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DcJ6y23A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/appsignal-install.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DcJ6y23A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/appsignal-install.png" alt="Appsignal Install: step 1" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select "Install for Ruby", which will then give us a few simple steps to follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the gem to your &lt;code&gt;Gemfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Gemfile&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'appsignal'&lt;/span&gt;
&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;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn compose:sh
&lt;span class="c"&gt;# We are now in a bash console within a docker container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# Stay in this console for the next command&lt;/span&gt;
&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'll need to respond to two prompts:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- `Do you want to change how this is displayed in AppSignal? (y/n): n`
- `How do you want to configure AppSignal?` : Input `1` because we will use a config file instead of just environment variables

Use the API key shown on the AppSignal setup page:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```bash
$ 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;/div&gt;



&lt;p&gt;After you run the install script, you'll notice when the AppSignal setup is complete in your browser. You can then 'Go to app' to view your AppSignal dashboard for your development environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N0kf7rpM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/install-complete.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N0kf7rpM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/install-complete.png" alt="AppSignal Install: complete" width="750" height="377"&gt;&lt;/a&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="https://docs.appsignal.com/ruby/configuration/"&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'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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn start:compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="https://appsignal.com/accounts"&gt;appsignal.com/accounts&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PDzSbUaz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/application-list.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PDzSbUaz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/application-list.png" alt="AppSignal application list" width="750" height="120"&gt;&lt;/a&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'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;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M7jRPAlX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/dashboard-errors.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M7jRPAlX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/dashboard-errors.png" alt="AppSignal dashboard errors" width="750" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the errors page, you find a list of all errors reported&lt;br&gt;
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;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zGs8xC4C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/magic-dashboards.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zGs8xC4C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/magic-dashboards.png" alt="AppSignal magic dashboards" width="750" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under 'Dashboard' in the sidebar, you can see that AppSignal has provided us with a few different dashboards already.&lt;/p&gt;

&lt;p&gt;The 'Overview' 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'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'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;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_YrdICM_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/performance-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_YrdICM_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/performance-dashboard.png" alt="AppSignal performance dashboard" width="750" height="303"&gt;&lt;/a&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'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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/puma.rb&lt;/span&gt;
&lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="ss"&gt;:appsignal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;APP_REVISION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latest_version_tag"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we'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's built-in integration with Puma, we now have information about the application server's activity. Specifying the &lt;code&gt;plugin&lt;/code&gt; in the config file has activated a &lt;a href="https://docs.appsignal.com/ruby/instrumentation/minutely-probes.html"&gt;minutely probe&lt;/a&gt; that reports metrics about the Puma server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--az5jRehA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/puma-metrics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--az5jRehA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/puma-metrics.png" alt="Puma metrics" width="750" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, across the entire AppSignal dashboard, there are now &lt;a href="https://docs.appsignal.com/application/markers/deploy-markers.html#revision-config-option"&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;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xMUcwxYI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/deploy-marker.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xMUcwxYI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2021-12/deploy-marker.png" alt="Deploy Marker" width="624" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also see a history of your deployments by viewing the 'Deploys' 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's visibility in the wild.&lt;/p&gt;

&lt;p&gt;We've demonstrated how AppSignal automatically integrates into Rails and some of the application's dependencies, such as Sidekiq and Puma. AppSignal works with many more Ruby frameworks and gems out-of-the-box. &lt;a href="https://docs.appsignal.com/ruby/integrations/"&gt;You can find a complete list of AppSignal'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'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/#ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Unathi Chonco is a Full-Stack Engineer at WeTransfer and Ruby on Rails contributor. He enjoys working across the Rails stack and sharing what he learns when solving day-to-day engineering challenges. You can find more content from Unathi &lt;a href="https://blog.unathichonco.com/"&gt;on his blog&lt;/a&gt; or reach out &lt;a href="https://twitter.com/UnathiUNC"&gt;via Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>ActiveRecord Optimistic Locking</title>
      <dc:creator>Unathi Chonco</dc:creator>
      <pubDate>Wed, 09 Jun 2021 18:53:24 +0000</pubDate>
      <link>https://dev.to/choncou/activerecord-optimistic-locking-2eph</link>
      <guid>https://dev.to/choncou/activerecord-optimistic-locking-2eph</guid>
      <description>&lt;h1&gt;
  
  
  ActiveRecord Optimistic Locking and Counter Caching
&lt;/h1&gt;

&lt;p&gt;In this post, we will discuss optimistic locking and how it can help us when building applications. This guide is targeted at Ruby on Rails developers, but similar strategies can be applied in other frameworks.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is Optimistic Locking
&lt;/h1&gt;

&lt;p&gt;Optimistic locking is a strategy used to verify that a database record has not been changed since the last time you read/fetched it. Locking gives you some certainty that any updates you are making are based on the most recent version.&lt;/p&gt;

&lt;p&gt;So how do we use this, and how could it help ?&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR: A Quick Practical Guide
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Documentation: &lt;a href="https://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html"&gt;https://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;lock_version&lt;/code&gt; column to your model&lt;/li&gt;
&lt;li&gt;Include &lt;code&gt;lock_version&lt;/code&gt; as hidden input in form&lt;/li&gt;
&lt;li&gt;Handle &lt;code&gt;ActiveRecord::StaleObjectError&lt;/code&gt; for when an update is attempted on an old version&lt;/li&gt;
&lt;li&gt;Short example: &lt;a href="https://gist.github.com/choncou/ed77efabdea92e38833839c974b35eee"&gt;https://gist.github.com/choncou/ed77efabdea92e38833839c974b35eee&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Longer Guide
&lt;/h2&gt;

&lt;p&gt;Let's introduce our scenario.&lt;/p&gt;

&lt;p&gt;We have a simple e-commerce application, with an assortment of products that are managed by different teams throughout the company. A common workflow in the company looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Inventory team adds new a product to the system as soon as they've received the stock. While they quickly import a lot of products, they prefer to only fill in the name and description.&lt;/li&gt;
&lt;li&gt;The Marketing team is notified of new products and starts working on better product descriptions, and setting the prices&lt;/li&gt;
&lt;li&gt;The Inventory team goes through all the new stock they’ve added, and update the packaged weight of each product.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;p&gt;The marketing team has started reporting that sometimes their updates to products are lost a while after saving and confirming the new information. How could this happen?&lt;/p&gt;

&lt;p&gt;After some digging, we find out that when the inventory team adds a new product, they keep a tab open with the edit page for each product. When they press save on those old tabs, it will replace any other information for the product with what is on their form. Here’s how it that looks in the application:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/cRJA2sqDSNk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Our real problem is that we have no way of knowing if the information that somebody is submitting in the form contains outdated values which we don't actually want to override the existing data about a product.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Optimistic locking was created to handle this kind of problem: allowing multiple users to access the same record for edits while assuming minimum conflict with the data.&lt;/p&gt;

&lt;p&gt;Optimistic Locking achieves this by tracking a version number for a record. Every time that record is updated, the version number increases. If we try to update a record with a version number that doesn't match the current version in the database, Rails will throw an &lt;code&gt;ActiveRecord::StaleObjectError&lt;/code&gt; exception.&lt;/p&gt;

&lt;p&gt;To add this to our existing application we need to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Add a &lt;code&gt;lock_version&lt;/code&gt; integer column to our products table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, rails will use optimistic locking on your model when it finds the &lt;code&gt;lock_version&lt;/code&gt; column. It is also possible to specify a custom locking column name for a specific model by setting &lt;code&gt;self.locking_column = :my_custom_column&lt;/code&gt; (&lt;a href="https://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html"&gt;see documentation&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Our database schema now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="s2"&gt;"products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: :cascade&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="s2"&gt;"description"&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="s2"&gt;"weight_grams"&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="s2"&gt;"price_cents"&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt; &lt;span class="s2"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="s2"&gt;"lock_version"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2) Include &lt;code&gt;lock_version&lt;/code&gt; in our form submission&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the view where we render the form, we want to include a hidden form field with the version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= form.hidden_field :lock_version %&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;In our controller, we need to permit the version column so that it is included in the params used to update the product.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Only allow a list of trusted parameters through.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;product_params&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:product&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:weight_grams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:price_cents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lock_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;—&lt;/p&gt;

&lt;p&gt;This is all we need to prevent the updates from happening. Now we get an exception when we try to make an update with the outdated form.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/hO0wICZrq9Y"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling ActiveRecord::StaleObjectError
&lt;/h3&gt;

&lt;p&gt;The application crashing because the form is outdated isn't a great user experience, so the least we can do is let the user know what went wrong.&lt;/p&gt;

&lt;p&gt;This isn't a post about error handling, so we'll go with the simplest solution of catching this error in the controller right where we are making the update. Right now, this is what our controllers &lt;code&gt;#update&lt;/code&gt; action looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# PATCH/PUT /products/1 or /products/1.json&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
  &lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;notice: &lt;/span&gt;&lt;span class="s2"&gt;"Product was successfully updated."&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;We can add an error message to the product and display it in the view. Our controller would then include a &lt;code&gt;rescue&lt;/code&gt; block to add the error to the product and display the edit page again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# PATCH/PUT /products/1 or /products/1.json&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
  &lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;notice: &lt;/span&gt;&lt;span class="s2"&gt;"Product was successfully updated."&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StaleObjectError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_error&lt;/span&gt;
    &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Oops. Looks like the product has changed since you last opened it. Please refresh the page"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now when we submit an outdated form, we are shown a regular error message. In your application, you could provide more context on the page, by showing the user both the new and old information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PjPtrVhQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1623264054440/5tNFHU-Uu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PjPtrVhQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1623264054440/5tNFHU-Uu.png" alt="Error Message Displayed on form.png" width="672" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using optimistic locking in rails can give us a lot of value out of the box, but it is important to note that this doesn't solve all cases that could look the same. Optimistic locking should be used when there isn't a high chance of conflicts and race conditions. If you really need to prevent other users from editing a record until the first user is done, you should consider &lt;a href="https://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html"&gt;pessimistic locking&lt;/a&gt; or implementing something similar in your application.&lt;/p&gt;

&lt;p&gt;Find the example application and implementation from this post &lt;a href="https://github.com/choncou/blog-post-optimistic-locking/pull/1"&gt;here on GitHub&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/choncou/blog-post-optimistic-locking/pull/1"&gt;https://github.com/choncou/blog-post-optimistic-locking/pull/1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this has given you some insight and inspiration into how you could use locking in your code.&lt;/p&gt;

&lt;p&gt;🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Verifying JWTs with JWKS in Ruby</title>
      <dc:creator>Unathi Chonco</dc:creator>
      <pubDate>Sun, 28 Mar 2021 06:11:14 +0000</pubDate>
      <link>https://dev.to/choncou/verifying-jwts-with-jwks-in-ruby-1lc2</link>
      <guid>https://dev.to/choncou/verifying-jwts-with-jwks-in-ruby-1lc2</guid>
      <description>&lt;p&gt;In this post, we will discuss a few issues, and their solutions, when working with asymmetrically signed &lt;a href="https://jwt.io/"&gt;JWTs (JSON Web Tokens)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post assumes you have a basic working knowledge of JWTs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lowdown (Intro)
&lt;/h2&gt;

&lt;p&gt;When working with &lt;a href="https://jwt.io/"&gt;JWTs (JSON Web Tokens)&lt;/a&gt;, it is common to sign them using asymmetric encryption. Asymmetric encryption involves using a public/private key pair, where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the issuer signs the JWT with the private key&lt;/li&gt;
&lt;li&gt;the receiver verifies the JWT's origin &amp;amp; contents with the public key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ability for a receiver to verify the JWT means that the receiver can trust the contents of the JWT, without the need to make an additional API call for that information.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://tools.ietf.org/html/rfc7517"&gt;JWKS (JSON Web Key Set)&lt;/a&gt; is a representation of the cryptographic key pairs mentioned above. The JWKS contains the public keys that can be used to verify a JWT. The JWT would contain a claim(&lt;code&gt;kid&lt;/code&gt;) that specifies which key was used to sign it. The issuer of the JWT provides the JWKS.&lt;/p&gt;

&lt;p&gt;Working with a JWKS can introduce a few problems if not dealt with correctly. To discuss these problems, let us introduce an example use case as a starting point.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://auth0.com/"&gt;Auth0&lt;/a&gt; is a service that provides an authentication and authorization platform. We want to use Auth0 for our application's user authentication needs. Similar to a regular &lt;a href="https://oauth.net/2/"&gt;OAuth2&lt;/a&gt; flow, we get the user to authenticate themselves off of our application (on Auth0), and once they return and we complete the flow, we receive an access token for the user. The access token is a JWT which we can use for making authenticated API calls to our application's backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification: How our backend authenticates a user
&lt;/h3&gt;

&lt;p&gt;When the backend receives an API call it will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;confirm that a JWT is present&lt;/li&gt;
&lt;li&gt;verify the JWT signature using the relevant public key&lt;/li&gt;
&lt;li&gt;verify other claims in the JWT (expiry, scopes, etc)&lt;/li&gt;
&lt;li&gt;use the contents of the JWT to identify the user and process the rest of the request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Auth0 provides a &lt;a href="https://auth0.com/docs/tokens/json-web-tokens/json-web-key-sets"&gt;JWKS endpoint&lt;/a&gt;, at &lt;code&gt;https://TENANT_DOMAIN/.well-known/jwks.json&lt;/code&gt;, which we will use in step #2 to fetch the public keys to verify the received.&lt;/p&gt;

&lt;p&gt;In a Ruby application, steps 2 &amp;amp; 3 could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Auth&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;VerifyJwt&lt;/span&gt;
    &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;

    &lt;span class="no"&gt;JWKS_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auth0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:auth_domain&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.well-known/jwks.json"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;audience: :web&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Verify the signature of this token&lt;/span&gt;
        &lt;span class="ss"&gt;algorithms: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"RS256"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;iss: &lt;/span&gt;&lt;span class="s2"&gt;"https://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auth0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:auth_domain&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;verify_iss: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;aud: &lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auth0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:web_audience&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;verify_aud: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;jwks: &lt;/span&gt;&lt;span class="n"&gt;fetch_jwks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_jwks&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JWKS_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;With the above, we have a working solution. But we have also introduced a few potentials problems that are worth resolving before deploying the application to production.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Problems
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Problem 1: External API call on every request
&lt;/h3&gt;

&lt;p&gt;For every authenticated request that comes into our application, we will also be making an API call to the &lt;code&gt;/jwks.json&lt;/code&gt; endpoint. This introduces a few concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there is additional overhead on every request, increasing the response time&lt;/li&gt;
&lt;li&gt;a spike in traffic, or an attack, can result in rate limits being crossed and all our authentication processes being unavailable for a period of time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution: Caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;External calls like the &lt;code&gt;/jwks.json&lt;/code&gt; endpoint are a perfect opportunity for caching. The key pair used for signing our JWTs won't change unless they are &lt;a href="https://auth0.com/docs/tokens/rotate-signing-keys"&gt;rotated&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In code, we would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cache the response of the API call&lt;/li&gt;
&lt;li&gt;return the cached value, if it exists, for future calls
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Auth&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;VerifyJwt&lt;/span&gt;
    &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="no"&gt;JWKS_CACHE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"auth/jwks-json"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="ss"&gt;jwks: &lt;/span&gt;&lt;span class="n"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_jwks&lt;/span&gt;
      &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;jwks&lt;/span&gt;
      &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JWKS_CACHE_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;fetch_jwks&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deep_symbolize_keys&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;

&lt;h3&gt;
  
  
  Problem 2: Delayed Cache invalidation for rotated keys
&lt;/h3&gt;

&lt;p&gt;The solution to Problem 1 introduces a new issue.&lt;/p&gt;

&lt;p&gt;What happens when our key pairs are rotated? Leaked private keys, routine security processes, or other circumstances, are possible reasons for key rotation. Rotating key pairs means that all new JWTs will be signed by those new keys.&lt;/p&gt;

&lt;p&gt;After a key rotation, our application will always fail to verify new JWTS because our cache would still be returning the previous JWKS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution: Cache Invalidation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can solve this issue by fetching from the &lt;code&gt;/jwks.json&lt;/code&gt; endpoint again if we recognise a JWT that was signed by a different key than those we know about. We can identify this by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extracting the &lt;code&gt;kid&lt;/code&gt; from the JWT header&lt;/li&gt;
&lt;li&gt;looking for a key, in the &lt;code&gt;keys&lt;/code&gt; of the JWKS, with the matching &lt;code&gt;kid&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;fetching the JWKS again (not from cache), if no key is found&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fortunately, the ruby &lt;a href="https://github.com/jwt/ruby-jwt"&gt;JWT gem&lt;/a&gt; has done the heavy lifting. When we pass through a function, instead of the raw JWKS, the library will call that function with options if it expects us to try to return a new set of JWKS. (&lt;a href="https://github.com/jwt/ruby-jwt/blob/master/lib/jwt/jwk/key_finder.rb"&gt;gem implementation&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Auth&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;VerifyJwt&lt;/span&gt;
    &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="ss"&gt;jwks: &lt;/span&gt;&lt;span class="n"&gt;jwk_loader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;jwk_loader&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="c1"&gt;# options[:invalidate] will be `true` if a matching `kid` was not found&lt;/span&gt;
        &lt;span class="c1"&gt;# https://github.com/jwt/ruby-jwt/blob/master/lib/jwt/jwk/key_finder.rb#L31&lt;/span&gt;
        &lt;span class="n"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:invalidate&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_jwks&lt;/span&gt;
      &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JWKS_CACHE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;fetch_jwks&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deep_symbolize_keys&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;

&lt;h3&gt;
  
  
  Problem 3: Attacker-triggered cache invalidation
&lt;/h3&gt;

&lt;p&gt;The current solution to Problem 2 reintroduces Problem 1. An attacker could flood our API with calls using a random invalid JWT that will always fail verification.&lt;/p&gt;

&lt;p&gt;Because the invalid JWT would have a different &lt;code&gt;kid&lt;/code&gt;, we would invalidate our cache every time, forcing us to do an API call.&lt;/p&gt;

&lt;p&gt;In theory, this could be acceptable because we are fetching the latest JWKS. But if the call to the &lt;code&gt;.../jwks.json&lt;/code&gt; endpoint fails, maybe due to rate-limiting, we won't have a value to cache and could be caching &lt;code&gt;nil&lt;/code&gt;. This will mean that verifying any JWTs will not be possible until our application can get a successful response from the &lt;code&gt;.../jwks.json&lt;/code&gt; endpoint again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution: Ignore nil for cache invalidation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;#fetch_jwks&lt;/code&gt; method only returns the JSON response if the request was successful, and &lt;code&gt;nil&lt;/code&gt; otherwise. Meaning we can cater for any failed response by leaving the current value in the cache, so we always have the latest successful response we've received.&lt;/p&gt;

&lt;p&gt;The Rails cache store already provides a simple helper for this use case. We need to add &lt;code&gt;skip_nil: true&lt;/code&gt; to our fetch&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Auth&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;VerifyJwt&lt;/span&gt;
    &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;jwk_loader&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:invalidate&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JWKS_CACHE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;skip_nil: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;fetch_jwks&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deep_symbolize_keys&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;We also handle the possibility of receiving &lt;code&gt;nil&lt;/code&gt; by defaulting to an empty hash (&lt;code&gt;{}&lt;/code&gt;), because the JWT gem expects a hash and not nil.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Looking back, we now know how to protect our application from basic performance and security concerns. We have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;implemented caching of the JWKS required for verifying JWTs&lt;/li&gt;
&lt;li&gt;accounted for the possibility of keys being rotated&lt;/li&gt;
&lt;li&gt;protected our application from an attacker disrupting functionality for all users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is final code for our JWT verification implementation &lt;a href="https://gist.github.com/choncou/18161b215fcc8ad2a44e256079f22ef3"&gt;here&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;This is not an exhaustive reference to all security-related concerns that you could encounter when working with JWTs, but I hope you find it helpful as a good starting point to get up and running.&lt;/p&gt;

&lt;p&gt;🚀&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>jwt</category>
      <category>security</category>
      <category>rails</category>
    </item>
    <item>
      <title>TIL: HTML &lt;label&gt; elements, what are they `for`?</title>
      <dc:creator>Unathi Chonco</dc:creator>
      <pubDate>Mon, 06 Jul 2020 18:54:50 +0000</pubDate>
      <link>https://dev.to/choncou/til-html-label-elements-what-are-they-for-2277</link>
      <guid>https://dev.to/choncou/til-html-label-elements-what-are-they-for-2277</guid>
      <description>&lt;h1&gt;
  
  
  TIL: HTML &amp;lt;label&amp;gt; elements, what are they &lt;code&gt;for&lt;/code&gt;?
&lt;/h1&gt;

&lt;p&gt;Today I learnt that &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; elements aren’t just semantic html tags used to make your code a bit more understandable, or “well structured”.&lt;/p&gt;

&lt;p&gt;Besides just representing the caption for a specific piece of your interface, labels can give you a few benefits when used correctly to associate the label with an input (or any other &lt;a href="https://html.spec.whatwg.org/multipage/forms.html#category-label"&gt;labelable element&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;There are 2 methods of associating a label to an input&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Using the &lt;code&gt;for&lt;/code&gt; attribute
&lt;/h3&gt;

&lt;p&gt;To get this working you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;give the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; an &lt;code&gt;id&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;give the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; a &lt;code&gt;for&lt;/code&gt; attribute that is the same value as the input’s id
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form__group"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form__input"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form__label"&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Full name&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Having a &lt;a href="https://www.w3.org/TR/html4/interact/forms.html#h-17.2.1"&gt;contained control element&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;All this requires is placing the input inside of the label, and it will be associated implicitly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form__group"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form__label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form__input"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Full name
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Benefits
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Accessibility
&lt;/h4&gt;

&lt;p&gt;Assistive tech, such as screen readers, will be able to read out the label when the user is focused on the input. This is a small use-case, but accessibility is always important. A small win that could go a long way.&lt;/p&gt;

&lt;h4&gt;
  
  
  User Experience
&lt;/h4&gt;

&lt;p&gt;A user, on desktop or mobile, can click on either the input or the label to focus/activate the specific input.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR and Examples
&lt;/h2&gt;

&lt;p&gt;Labels can be used to activate/focus on an associated input.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/unathi/embed/oNbpEGy?height=600&amp;amp;default-tab=html,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>html</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Opinionated Tips for Maintainable Rails Applications?</title>
      <dc:creator>Unathi Chonco</dc:creator>
      <pubDate>Thu, 02 Jul 2020 06:07:43 +0000</pubDate>
      <link>https://dev.to/choncou/opinionated-tips-for-maintainable-rails-applications-51j6</link>
      <guid>https://dev.to/choncou/opinionated-tips-for-maintainable-rails-applications-51j6</guid>
      <description>&lt;p&gt;What opinionated tips do you have when it comes to creating maintainable web applications?&lt;/p&gt;

&lt;p&gt;Just over a year ago I wrote &lt;a href="https://medium.com/@unathiunc/5-opinionated-tips-for-creating-a-maintainable-rails-application-bae9d0c0f16b"&gt;a post on medium&lt;/a&gt; which covered the following 5 core points.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating &amp;amp; maintaining a living style guide&lt;/li&gt;
&lt;li&gt;Enforcing consistency in stylesheets&lt;/li&gt;
&lt;li&gt;Keeping your models skinny&lt;/li&gt;
&lt;li&gt;Keeping your controllers skinny&lt;/li&gt;
&lt;li&gt;Keeping your JavaScript simple&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The full post for a deeper look into each point:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@unathiunc/5-opinionated-tips-for-creating-a-maintainable-rails-application-bae9d0c0f16b" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3yWf3WM1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/1%2ALxPdSQtf87HdaMubEloSJg.png" alt="Unathi Chonco"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@unathiunc/5-opinionated-tips-for-creating-a-maintainable-rails-application-bae9d0c0f16b" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;5 Opinionated Tips for Creating a Maintainable Rails Application | by Unathi Chonco | Medium&lt;/h2&gt;
      &lt;h3&gt;Unathi Chonco ・ &lt;time&gt;Mar 13, 2019&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ze5yh_2q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;I would like to hear from other experienced web developers on maintainability tips that they think are evergreen.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>webdev</category>
      <category>css</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
